mirror of
https://github.com/bkaradzic/bimg.git
synced 2026-02-17 20:52:38 +01:00
texturec: Added alpha to coverage mip map generation.
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
#include <stdint.h> // uint32_t
|
||||
#include <stdlib.h> // 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
|
||||
|
||||
@@ -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
|
||||
|
||||
101
src/image.cpp
101
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<uint8_t*>(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 <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"
|
||||
" aspect ratio will be preserved.\n"
|
||||
" --as <extension> 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");
|
||||
|
||||
Reference in New Issue
Block a user