Ver Fonte

texturec: Added alpha to coverage mip map generation.

Branimir Karadžić há 8 anos atrás
pai
commit
612eebbeeb
5 ficheiros alterados com 250 adições e 42 exclusões
  1. 10 4
      include/bimg/bimg.h
  2. 22 0
      include/bimg/encode.h
  3. 66 37
      src/image.cpp
  4. 111 0
      src/image_encode.cpp
  5. 41 1
      tools/texturec/texturec.cpp

+ 10 - 4
include/bimg/bimg.h

@@ -9,7 +9,7 @@
 #include <stdint.h> // uint32_t
 #include <stdint.h> // uint32_t
 #include <stdlib.h> // NULL
 #include <stdlib.h> // NULL
 
 
-#define BIMG_API_VERSION UINT32_C(2)
+#define BIMG_API_VERSION UINT32_C(3)
 
 
 namespace bx
 namespace bx
 {
 {
@@ -335,6 +335,12 @@ namespace bimg
 		, const void* _src
 		, const void* _src
 		);
 		);
 
 
+	///
+	PackFn getPack(TextureFormat::Enum _format);
+
+	///
+	UnpackFn getUnpack(TextureFormat::Enum _format);
+
 	///
 	///
 	bool imageConvert(
 	bool imageConvert(
 		  TextureFormat::Enum _dstFormat
 		  TextureFormat::Enum _dstFormat
@@ -408,7 +414,7 @@ namespace bimg
 		);
 		);
 
 
 	///
 	///
-	void imageWriteTga(
+	int32_t imageWriteTga(
 		  bx::WriterI* _writer
 		  bx::WriterI* _writer
 		, uint32_t _width
 		, uint32_t _width
 		, uint32_t _height
 		, uint32_t _height
@@ -420,7 +426,7 @@ namespace bimg
 		);
 		);
 
 
 	///
 	///
-	void imageWriteKtx(
+	int32_t imageWriteKtx(
 		  bx::WriterI* _writer
 		  bx::WriterI* _writer
 		, TextureFormat::Enum _format
 		, TextureFormat::Enum _format
 		, bool _cubeMap
 		, bool _cubeMap
@@ -433,7 +439,7 @@ namespace bimg
 		);
 		);
 
 
 	///
 	///
-	void imageWriteKtx(
+	int32_t imageWriteKtx(
 		  bx::WriterI* _writer
 		  bx::WriterI* _writer
 		, ImageContainer& _imageContainer
 		, ImageContainer& _imageContainer
 		, const void* _data
 		, const void* _data

+ 22 - 0
include/bimg/encode.h

@@ -79,6 +79,28 @@ namespace bimg
 		, const ImageContainer* _src
 		, const ImageContainer* _src
 		);
 		);
 
 
+	///
+	float imageAlphaTestCoverage(
+		  TextureFormat::Enum _format
+		, uint32_t _width
+		, uint32_t _height
+		, uint32_t _srcPitch
+		, const void* _src
+		, float _alphaRef
+		, float _scale = 1.0f
+		);
+
+	///
+	void imageScaleAlphaToCoverage(
+		  TextureFormat::Enum _format
+		, uint32_t _width
+		, uint32_t _height
+		, uint32_t _srcPitch
+		, void* _src
+		, float _coverage
+		, float _alphaRef
+		);
+
 } // namespace bimg
 } // namespace bimg
 
 
 #endif // BIMG_ENCODE_H_HEADER_GUARD
 #endif // BIMG_ENCODE_H_HEADER_GUARD

+ 66 - 37
src/image.cpp

@@ -778,6 +778,16 @@ namespace bimg
 	};
 	};
 	BX_STATIC_ASSERT(TextureFormat::Count ==       BX_COUNTOF(s_packUnpack) );
 	BX_STATIC_ASSERT(TextureFormat::Count ==       BX_COUNTOF(s_packUnpack) );
 
 
+	PackFn getPack(TextureFormat::Enum _format)
+	{
+		return s_packUnpack[_format].pack;
+	}
+
+	UnpackFn getUnpack(TextureFormat::Enum _format)
+	{
+		return s_packUnpack[_format].unpack;
+	}
+
 	bool imageConvert(TextureFormat::Enum _dstFormat, TextureFormat::Enum _srcFormat)
 	bool imageConvert(TextureFormat::Enum _dstFormat, TextureFormat::Enum _srcFormat)
 	{
 	{
 		UnpackFn unpack = s_packUnpack[_srcFormat].unpack;
 		UnpackFn unpack = s_packUnpack[_srcFormat].unpack;
@@ -3149,7 +3159,7 @@ namespace bimg
 		return false;
 		return false;
 	}
 	}
 
 
-	void imageWriteTga(bx::WriterI* _writer, uint32_t _width, uint32_t _height, uint32_t _srcPitch, const void* _src, bool _grayscale, bool _yflip, bx::Error* _err)
+	int32_t imageWriteTga(bx::WriterI* _writer, uint32_t _width, uint32_t _height, uint32_t _srcPitch, const void* _src, bool _grayscale, bool _yflip, bx::Error* _err)
 	{
 	{
 		BX_ERROR_SCOPE(_err);
 		BX_ERROR_SCOPE(_err);
 
 
@@ -3165,31 +3175,34 @@ namespace bimg
 		header[16] = bpp;
 		header[16] = bpp;
 		header[17] = 32;
 		header[17] = 32;
 
 
-		bx::write(_writer, header, sizeof(header), _err);
+		int32_t total = 0;
+		total += bx::write(_writer, header, sizeof(header), _err);
 
 
 		uint32_t dstPitch = _width*bpp/8;
 		uint32_t dstPitch = _width*bpp/8;
 		if (_yflip)
 		if (_yflip)
 		{
 		{
 			uint8_t* data = (uint8_t*)_src + _srcPitch*_height - _srcPitch;
 			uint8_t* data = (uint8_t*)_src + _srcPitch*_height - _srcPitch;
-			for (uint32_t yy = 0; yy < _height; ++yy)
+			for (uint32_t yy = 0; yy < _height && _err->isOk(); ++yy)
 			{
 			{
-				bx::write(_writer, data, dstPitch, _err);
+				total += bx::write(_writer, data, dstPitch, _err);
 				data -= _srcPitch;
 				data -= _srcPitch;
 			}
 			}
 		}
 		}
 		else if (_srcPitch == dstPitch)
 		else if (_srcPitch == dstPitch)
 		{
 		{
-			bx::write(_writer, _src, _height*_srcPitch, _err);
+			total += bx::write(_writer, _src, _height*_srcPitch, _err);
 		}
 		}
 		else
 		else
 		{
 		{
 			uint8_t* data = (uint8_t*)_src;
 			uint8_t* data = (uint8_t*)_src;
-			for (uint32_t yy = 0; yy < _height; ++yy)
+			for (uint32_t yy = 0; yy < _height && _err->isOk(); ++yy)
 			{
 			{
-				bx::write(_writer, data, dstPitch, _err);
+				total += bx::write(_writer, data, dstPitch, _err);
 				data += _srcPitch;
 				data += _srcPitch;
 			}
 			}
 		}
 		}
+
+		return total;
 	}
 	}
 
 
 	static int32_t imageWriteKtxHeader(bx::WriterI* _writer, TextureFormat::Enum _format, bool _cubeMap, uint32_t _width, uint32_t _height, uint32_t _depth, uint8_t _numMips, bx::Error* _err)
 	static int32_t imageWriteKtxHeader(bx::WriterI* _writer, TextureFormat::Enum _format, bool _cubeMap, uint32_t _width, uint32_t _height, uint32_t _depth, uint8_t _numMips, bx::Error* _err)
@@ -3198,31 +3211,37 @@ namespace bimg
 
 
 		const KtxFormatInfo& tfi = s_translateKtxFormat[_format];
 		const KtxFormatInfo& tfi = s_translateKtxFormat[_format];
 
 
-		int32_t size = 0;
-		size += bx::write(_writer, "\xabKTX 11\xbb\r\n\x1a\n", 12, _err);
-		size += bx::write(_writer, uint32_t(0x04030201), _err);
-		size += bx::write(_writer, uint32_t(0), _err); // glType
-		size += bx::write(_writer, uint32_t(1), _err); // glTypeSize
-		size += bx::write(_writer, uint32_t(0), _err); // glFormat
-		size += bx::write(_writer, tfi.m_internalFmt, _err); // glInternalFormat
-		size += bx::write(_writer, tfi.m_fmt, _err); // glBaseInternalFormat
-		size += bx::write(_writer, _width, _err);
-		size += bx::write(_writer, _height, _err);
-		size += bx::write(_writer, _depth, _err);
-		size += bx::write(_writer, uint32_t(0), _err); // numberOfArrayElements
-		size += bx::write(_writer, _cubeMap ? uint32_t(6) : uint32_t(0), _err);
-		size += bx::write(_writer, uint32_t(_numMips), _err);
-		size += bx::write(_writer, uint32_t(0), _err); // Meta-data size.
-
-		BX_WARN(size == 64, "KTX: Failed to write header size %d (expected: %d).", size, 64);
-		return size;
+		int32_t total = 0;
+		total += bx::write(_writer, "\xabKTX 11\xbb\r\n\x1a\n", 12, _err);
+		total += bx::write(_writer, uint32_t(0x04030201), _err);
+		total += bx::write(_writer, uint32_t(0), _err); // glType
+		total += bx::write(_writer, uint32_t(1), _err); // glTypeSize
+		total += bx::write(_writer, uint32_t(0), _err); // glFormat
+		total += bx::write(_writer, tfi.m_internalFmt, _err); // glInternalFormat
+		total += bx::write(_writer, tfi.m_fmt, _err); // glBaseInternalFormat
+		total += bx::write(_writer, _width, _err);
+		total += bx::write(_writer, _height, _err);
+		total += bx::write(_writer, _depth, _err);
+		total += bx::write(_writer, uint32_t(0), _err); // numberOfArrayElements
+		total += bx::write(_writer, _cubeMap ? uint32_t(6) : uint32_t(0), _err);
+		total += bx::write(_writer, uint32_t(_numMips), _err);
+		total += bx::write(_writer, uint32_t(0), _err); // Meta-data size.
+
+		BX_WARN(total == 64, "KTX: Failed to write header size %d (expected: %d).", total, 64);
+		return total;
 	}
 	}
 
 
-	void imageWriteKtx(bx::WriterI* _writer, TextureFormat::Enum _format, bool _cubeMap, uint32_t _width, uint32_t _height, uint32_t _depth, uint8_t _numMips, const void* _src, bx::Error* _err)
+	int32_t imageWriteKtx(bx::WriterI* _writer, TextureFormat::Enum _format, bool _cubeMap, uint32_t _width, uint32_t _height, uint32_t _depth, uint8_t _numMips, const void* _src, bx::Error* _err)
 	{
 	{
 		BX_ERROR_SCOPE(_err);
 		BX_ERROR_SCOPE(_err);
 
 
-		imageWriteKtxHeader(_writer, _format, _cubeMap, _width, _height, _depth, _numMips, _err);
+		int32_t total = 0;
+		total += imageWriteKtxHeader(_writer, _format, _cubeMap, _width, _height, _depth, _numMips, _err);
+
+		if (!_err->isOk() )
+		{
+			return total;
+		}
 
 
 		const ImageBlockInfo& blockInfo = s_imageBlockInfo[_format];
 		const ImageBlockInfo& blockInfo = s_imageBlockInfo[_format];
 		const uint8_t  bpp         = blockInfo.bitsPerPixel;
 		const uint8_t  bpp         = blockInfo.bitsPerPixel;
@@ -3236,18 +3255,18 @@ namespace bimg
 		uint32_t height = _height;
 		uint32_t height = _height;
 		uint32_t depth  = _depth;
 		uint32_t depth  = _depth;
 
 
-		for (uint8_t lod = 0, num = _numMips; lod < num; ++lod)
+		for (uint8_t lod = 0, num = _numMips; lod < num && _err->isOk(); ++lod)
 		{
 		{
 			width  = bx::uint32_max(blockWidth  * minBlockX, ( (width  + blockWidth  - 1) / blockWidth )*blockWidth);
 			width  = bx::uint32_max(blockWidth  * minBlockX, ( (width  + blockWidth  - 1) / blockWidth )*blockWidth);
 			height = bx::uint32_max(blockHeight * minBlockY, ( (height + blockHeight - 1) / blockHeight)*blockHeight);
 			height = bx::uint32_max(blockHeight * minBlockY, ( (height + blockHeight - 1) / blockHeight)*blockHeight);
 			depth  = bx::uint32_max(1, depth);
 			depth  = bx::uint32_max(1, depth);
 
 
 			uint32_t size = width*height*depth*bpp/8;
 			uint32_t size = width*height*depth*bpp/8;
-			bx::write(_writer, size, _err);
+			total += bx::write(_writer, size, _err);
 
 
-			for (uint8_t side = 0, numSides = _cubeMap ? 6 : 1; side < numSides; ++side)
+			for (uint8_t side = 0, numSides = _cubeMap ? 6 : 1; side < numSides && _err->isOk(); ++side)
 			{
 			{
-				bx::write(_writer, src, size, _err);
+				total += bx::write(_writer, src, size, _err);
 				src += size;
 				src += size;
 			}
 			}
 
 
@@ -3255,13 +3274,16 @@ namespace bimg
 			height >>= 1;
 			height >>= 1;
 			depth  >>= 1;
 			depth  >>= 1;
 		}
 		}
+
+		return total;
 	}
 	}
 
 
-	void imageWriteKtx(bx::WriterI* _writer, ImageContainer& _imageContainer, const void* _data, uint32_t _size, bx::Error* _err)
+	int32_t imageWriteKtx(bx::WriterI* _writer, ImageContainer& _imageContainer, const void* _data, uint32_t _size, bx::Error* _err)
 	{
 	{
 		BX_ERROR_SCOPE(_err);
 		BX_ERROR_SCOPE(_err);
 
 
-		imageWriteKtxHeader(_writer
+		int32_t total = 0;
+		total += imageWriteKtxHeader(_writer
 			, TextureFormat::Enum(_imageContainer.m_format)
 			, TextureFormat::Enum(_imageContainer.m_format)
 			, _imageContainer.m_cubeMap
 			, _imageContainer.m_cubeMap
 			, _imageContainer.m_width
 			, _imageContainer.m_width
@@ -3271,20 +3293,27 @@ namespace bimg
 			, _err
 			, _err
 			);
 			);
 
 
-		for (uint8_t lod = 0, num = _imageContainer.m_numMips; lod < num; ++lod)
+		if (!_err->isOk() )
+		{
+			return total;
+		}
+
+		for (uint8_t lod = 0, num = _imageContainer.m_numMips; lod < num && _err->isOk(); ++lod)
 		{
 		{
 			ImageMip mip;
 			ImageMip mip;
 			imageGetRawData(_imageContainer, 0, lod, _data, _size, mip);
 			imageGetRawData(_imageContainer, 0, lod, _data, _size, mip);
-			bx::write(_writer, mip.m_size, _err);
+			total += bx::write(_writer, mip.m_size, _err);
 
 
-			for (uint8_t side = 0, numSides = _imageContainer.m_cubeMap ? 6 : 1; side < numSides; ++side)
+			for (uint8_t side = 0, numSides = _imageContainer.m_cubeMap ? 6 : 1; side < numSides && _err->isOk(); ++side)
 			{
 			{
 				if (imageGetRawData(_imageContainer, side, lod, _data, _size, mip) )
 				if (imageGetRawData(_imageContainer, side, lod, _data, _size, mip) )
 				{
 				{
-					bx::write(_writer, mip.m_data, mip.m_size, _err);
+					total += bx::write(_writer, mip.m_data, mip.m_size, _err);
 				}
 				}
 			}
 			}
 		}
 		}
+
+		return total;
 	}
 	}
 
 
 } // namespace bimg
 } // namespace bimg

+ 111 - 0
src/image_encode.cpp

@@ -361,4 +361,115 @@ namespace bimg
 		return true;
 		return true;
 	}
 	}
 
 
+	static float getAlpha(UnpackFn _unpack, const void* _data)
+	{
+		float rgba[4];
+		_unpack(rgba, _data);
+		return rgba[3];
+	}
+
+	float imageAlphaTestCoverage(TextureFormat::Enum _format, uint32_t _width, uint32_t _height, uint32_t _srcPitch, const void* _src, float _alphaRef, float _scale)
+	{
+		UnpackFn unpack = getUnpack(_format);
+		if (NULL == unpack)
+		{
+			return 0.0f;
+		}
+
+		float coverage = 0.0f;
+		const uint8_t* src = (const uint8_t*)_src;
+		const uint32_t xstep = getBitsPerPixel(_format) / 8;
+		const float numSamples = 8.0f;
+
+		for (uint32_t yy = 0, ystep = _srcPitch; yy < _height-1; ++yy, src += ystep)
+		{
+			const uint8_t* data = src;
+			for (uint32_t xx = 0; xx < _width-1; ++xx, data += xstep)
+			{
+				float alpha00 = _scale * getAlpha(unpack, data);
+				float alpha10 = _scale * getAlpha(unpack, data+xstep);
+				float alpha01 = _scale * getAlpha(unpack, data+ystep);
+				float alpha11 = _scale * getAlpha(unpack, data+ystep+xstep);
+
+				for (float fy = 0.5f/numSamples; fy < 1.0f; fy += 1.0f)
+				{
+					for (float fx = 0.5f/numSamples; fx < 1.0f; fx += 1.0f)
+					{
+						float alpha = 0.0f
+							+ alpha00 * (1.0f - fx) * (1.0f - fy)
+							+ alpha10 * (       fx) * (1.0f - fy)
+							+ alpha01 * (1.0f - fx) * (       fy)
+							+ alpha11 * (       fx) * (       fy)
+							;
+
+						if (alpha > _alphaRef)
+						{
+							coverage += 1.0f;
+						}
+					}
+				}
+			}
+		}
+
+		return coverage / float(_width*_height*numSamples*numSamples);
+	}
+
+	void imageScaleAlphaToCoverage(TextureFormat::Enum _format, uint32_t _width, uint32_t _height, uint32_t _srcPitch, void* _src, float _desiredCoverage, float _alphaRef)
+	{
+		PackFn   pack   = getPack(_format);
+		UnpackFn unpack = getUnpack(_format);
+		if (NULL == pack
+		||  NULL == unpack)
+		{
+			return;
+		}
+
+		float min   = 0.0f;
+		float max   = 4.0f;
+		float scale = 1.0f;
+
+		for (uint32_t ii = 0; ii < 8; ++ii)
+		{
+			float coverage = imageAlphaTestCoverage(
+				  _format
+				, _width
+				, _height
+				, _srcPitch
+				, _src
+				, _alphaRef
+				, scale
+				);
+
+			if (coverage < _desiredCoverage)
+			{
+				min = scale;
+			}
+			else if (coverage > _desiredCoverage)
+			{
+				max = scale;
+			}
+			else
+			{
+				break;
+			}
+
+			scale = (min + max) * 0.5f;
+		}
+
+		uint8_t* src = (uint8_t*)_src;
+		const uint32_t xstep = getBitsPerPixel(_format) / 8;
+
+		for (uint32_t yy = 0, ystep = _srcPitch; yy < _height; ++yy, src += ystep)
+		{
+			uint8_t* data = src;
+			for (uint32_t xx = 0; xx < _width; ++xx, data += xstep)
+			{
+				float rgba[4];
+				unpack(rgba, data);
+				rgba[3] = bx::fsaturate(rgba[3]*scale);
+				pack(data, rgba);
+			}
+		}
+	}
+
 } // namespace bimg
 } // namespace bimg

+ 41 - 1
tools/texturec/texturec.cpp

@@ -38,6 +38,7 @@ struct Options
 		, normalMap(false)
 		, normalMap(false)
 		, iqa(false)
 		, iqa(false)
 		, sdf(false)
 		, sdf(false)
+		, alphaTest(false)
 	{
 	{
 	}
 	}
 
 
@@ -69,6 +70,7 @@ struct Options
 	bool normalMap;
 	bool normalMap;
 	bool iqa;
 	bool iqa;
 	bool sdf;
 	bool sdf;
+	bool alphaTest;
 };
 };
 
 
 bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData, uint32_t _inputSize, const Options& _options, bx::Error* _err)
 bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData, uint32_t _inputSize, const Options& _options, bx::Error* _err)
@@ -123,6 +125,10 @@ bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData
 			&& inputFormat == outputFormat
 			&& inputFormat == outputFormat
 			&& !needResize
 			&& !needResize
 			&& (1 < input->m_numMips) == _options.mips
 			&& (1 < input->m_numMips) == _options.mips
+			&& !_options.sdf
+			&& !_options.alphaTest
+			&& !_options.normalMap
+			&& !_options.iqa
 			;
 			;
 
 
 		if (needResize)
 		if (needResize)
@@ -373,6 +379,18 @@ bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData
 						, mip.m_format
 						, mip.m_format
 						);
 						);
 
 
+					float coverage = 0.0f;
+					if (_options.alphaTest)
+					{
+						coverage = bimg::imageAlphaTestCoverage(bimg::TextureFormat::RGBA8
+							, mip.m_width
+							, mip.m_height
+							, mip.m_width*4
+							, rgba
+							, _options.edge
+							);
+					}
+
 					void* ref = NULL;
 					void* ref = NULL;
 					if (_options.iqa)
 					if (_options.iqa)
 					{
 					{
@@ -400,6 +418,18 @@ bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData
 							, rgba
 							, rgba
 							);
 							);
 
 
+						if (_options.alphaTest)
+						{
+							bimg::imageScaleAlphaToCoverage(bimg::TextureFormat::RGBA8
+								, dstMip.m_width
+								, dstMip.m_height
+								, dstMip.m_width*4
+								, rgba
+								, coverage
+								, _options.edge
+								);
+						}
+
 						bimg::imageGetRawData(*output, side, lod, output->m_data, output->m_size, dstMip);
 						bimg::imageGetRawData(*output, side, lod, output->m_data, output->m_size, dstMip);
 						dstData = const_cast<uint8_t*>(dstMip.m_data);
 						dstData = const_cast<uint8_t*>(dstMip.m_data);
 
 
@@ -496,7 +526,8 @@ void help(const char* _error = NULL, bool _showHelp = true)
 		  "  -m, --mips               Generate mip-maps.\n"
 		  "  -m, --mips               Generate mip-maps.\n"
 		  "  -n, --normalmap          Input texture is normal map.\n"
 		  "  -n, --normalmap          Input texture is normal map.\n"
 		  "      --sdf <edge>         Compute SDF texture.\n"
 		  "      --sdf <edge>         Compute SDF texture.\n"
-		  "      --iqa                Image Quality Assesment\n"
+		  "      --ref <alpha>        Alpha reference value.\n"
+		  "      --iqa                Image Quality Assessment\n"
 		  "      --max <max size>     Maximum width/height (image will be scaled down and\n"
 		  "      --max <max size>     Maximum width/height (image will be scaled down and\n"
 		  "                           aspect ratio will be preserved.\n"
 		  "                           aspect ratio will be preserved.\n"
 		  "      --as <extension>     Save as.\n"
 		  "      --as <extension>     Save as.\n"
@@ -572,6 +603,15 @@ int main(int _argc, const char* _argv[])
 		options.sdf  = true;
 		options.sdf  = true;
 		options.edge = (float)atof(edgeOpt);
 		options.edge = (float)atof(edgeOpt);
 	}
 	}
+	else
+	{
+		const char* alphaRef = cmdLine.findOption("ref");
+		if (NULL != alphaRef)
+		{
+			options.alphaTest = true;
+			options.edge      = (float)atof(alphaRef);
+		}
+	}
 
 
 	options.mips      = cmdLine.hasArg('m',  "mips");
 	options.mips      = cmdLine.hasArg('m',  "mips");
 	options.normalMap = cmdLine.hasArg('n',  "normalmap");
 	options.normalMap = cmdLine.hasArg('n',  "normalmap");