From 612eebbeeb36d092bf433fdaa6297b357771d94f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Branimir=20Karad=C5=BEi=C4=87?= Date: Mon, 22 May 2017 17:29:33 -0700 Subject: [PATCH] texturec: Added alpha to coverage mip map generation. --- include/bimg/bimg.h | 14 +++-- include/bimg/encode.h | 22 +++++++ src/image.cpp | 101 ++++++++++++++++++++------------ src/image_encode.cpp | 111 ++++++++++++++++++++++++++++++++++++ tools/texturec/texturec.cpp | 42 +++++++++++++- 5 files changed, 249 insertions(+), 41 deletions(-) diff --git a/include/bimg/bimg.h b/include/bimg/bimg.h index 7451630..d963478 100644 --- a/include/bimg/bimg.h +++ b/include/bimg/bimg.h @@ -9,7 +9,7 @@ #include // uint32_t #include // NULL -#define BIMG_API_VERSION UINT32_C(2) +#define BIMG_API_VERSION UINT32_C(3) namespace bx { @@ -335,6 +335,12 @@ namespace bimg , const void* _src ); + /// + PackFn getPack(TextureFormat::Enum _format); + + /// + UnpackFn getUnpack(TextureFormat::Enum _format); + /// bool imageConvert( TextureFormat::Enum _dstFormat @@ -408,7 +414,7 @@ namespace bimg ); /// - void imageWriteTga( + int32_t imageWriteTga( bx::WriterI* _writer , uint32_t _width , uint32_t _height @@ -420,7 +426,7 @@ namespace bimg ); /// - void imageWriteKtx( + int32_t imageWriteKtx( bx::WriterI* _writer , TextureFormat::Enum _format , bool _cubeMap @@ -433,7 +439,7 @@ namespace bimg ); /// - void imageWriteKtx( + int32_t imageWriteKtx( bx::WriterI* _writer , ImageContainer& _imageContainer , const void* _data diff --git a/include/bimg/encode.h b/include/bimg/encode.h index b6c64bf..ef99327 100644 --- a/include/bimg/encode.h +++ b/include/bimg/encode.h @@ -79,6 +79,28 @@ namespace bimg , 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 #endif // BIMG_ENCODE_H_HEADER_GUARD diff --git a/src/image.cpp b/src/image.cpp index ff94ee1..94390e5 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -778,6 +778,16 @@ namespace bimg }; 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) { UnpackFn unpack = s_packUnpack[_srcFormat].unpack; @@ -3149,7 +3159,7 @@ namespace bimg 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); @@ -3165,31 +3175,34 @@ namespace bimg header[16] = bpp; 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; if (_yflip) { 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; } } else if (_srcPitch == dstPitch) { - bx::write(_writer, _src, _height*_srcPitch, _err); + total += bx::write(_writer, _src, _height*_srcPitch, _err); } else { 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; } } + + 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) @@ -3198,31 +3211,37 @@ namespace bimg 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. + 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(size == 64, "KTX: Failed to write header size %d (expected: %d).", size, 64); - return 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); - 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 uint8_t bpp = blockInfo.bitsPerPixel; @@ -3236,18 +3255,18 @@ namespace bimg uint32_t height = _height; 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); height = bx::uint32_max(blockHeight * minBlockY, ( (height + blockHeight - 1) / blockHeight)*blockHeight); depth = bx::uint32_max(1, depth); 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; } @@ -3255,13 +3274,16 @@ namespace bimg height >>= 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); - imageWriteKtxHeader(_writer + int32_t total = 0; + total += imageWriteKtxHeader(_writer , TextureFormat::Enum(_imageContainer.m_format) , _imageContainer.m_cubeMap , _imageContainer.m_width @@ -3271,20 +3293,27 @@ namespace bimg , _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; 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) ) { - 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 diff --git a/src/image_encode.cpp b/src/image_encode.cpp index 8239851..f90c450 100644 --- a/src/image_encode.cpp +++ b/src/image_encode.cpp @@ -361,4 +361,115 @@ namespace bimg 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 diff --git a/tools/texturec/texturec.cpp b/tools/texturec/texturec.cpp index 2cf795b..c4af514 100644 --- a/tools/texturec/texturec.cpp +++ b/tools/texturec/texturec.cpp @@ -38,6 +38,7 @@ struct Options , normalMap(false) , iqa(false) , sdf(false) + , alphaTest(false) { } @@ -69,6 +70,7 @@ struct Options bool normalMap; bool iqa; bool sdf; + bool alphaTest; }; 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 && !needResize && (1 < input->m_numMips) == _options.mips + && !_options.sdf + && !_options.alphaTest + && !_options.normalMap + && !_options.iqa ; if (needResize) @@ -373,6 +379,18 @@ bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData , 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; if (_options.iqa) { @@ -400,6 +418,18 @@ bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData , 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); dstData = const_cast(dstMip.m_data); @@ -496,7 +526,8 @@ void help(const char* _error = NULL, bool _showHelp = true) " -m, --mips Generate mip-maps.\n" " -n, --normalmap Input texture is normal map.\n" " --sdf Compute SDF texture.\n" - " --iqa Image Quality Assesment\n" + " --ref Alpha reference value.\n" + " --iqa Image Quality Assessment\n" " --max Maximum width/height (image will be scaled down and\n" " aspect ratio will be preserved.\n" " --as Save as.\n" @@ -572,6 +603,15 @@ int main(int _argc, const char* _argv[]) options.sdf = true; 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.normalMap = cmdLine.hasArg('n', "normalmap");