Files
OpenAssetTools/src/ObjImage/Image/TextureConverter.cpp
T
Jan LaupetinandGitHub 0874f0c36f feat: font generation from ttf (#882)
* chore: add file variant of json fonts

* chore: add stb dependency

* feat: generate fonts from ttf files

* chore: add option to add yOffset to file font

* chore: dynamically adjust generated font bitmap size

* fix: printable characters should respect ISO-8859-1 control characters

* chore: do not write missing glyphs into the bitmap

* chore: remove test texture conversion

* chore: adjust font compiler for iw3,iw4,iw5,t4,t5

* chore: add possibility to fill color channels when converting

* fix: games other than t6 use rgba for fonts

* fix: make sure no mipmaps are set on loaddef

* fix: t4 and iw3 use dimensions in image loaddef

* chore: also include optional glyphs in fonts
2026-07-08 23:35:58 +02:00

255 lines
9.6 KiB
C++

#include "TextureConverter.h"
#include <cassert>
namespace
{
constexpr uint64_t Mask1(const unsigned length)
{
if (length >= sizeof(uint64_t) * 8)
return std::numeric_limits<uint64_t>::max();
return std::numeric_limits<uint64_t>::max() >> (sizeof(uint64_t) * 8 - length);
}
bool CanReorder(const unsigned inputSize, const unsigned outputSize)
{
return inputSize == 0 || outputSize == 0 || inputSize == outputSize;
}
} // namespace
namespace image
{
void TextureConverter::SetPixelFunctions(const unsigned inBitCount, const unsigned outBitCount)
{
switch (inBitCount)
{
case 16:
m_read_pixel_func = [](const void* offset, unsigned bitCount)
{
return static_cast<uint64_t>(*static_cast<const uint16_t*>(offset));
};
break;
case 32:
m_read_pixel_func = [](const void* offset, unsigned bitCount)
{
return static_cast<uint64_t>(*static_cast<const uint32_t*>(offset));
};
break;
case 64:
m_read_pixel_func = [](const void* offset, unsigned bitCount)
{
return *static_cast<const uint64_t*>(offset);
};
break;
default:
if (inBitCount <= 64)
{
m_read_pixel_func = [](const void* offset, const unsigned bitCount)
{
uint64_t result = 0;
for (auto pixelOffset = 0u; pixelOffset < bitCount; pixelOffset += 8)
{
result |= (static_cast<uint64_t>(*(static_cast<const uint8_t*>(offset) + (pixelOffset / 8))) << pixelOffset);
}
return result;
};
}
else
{
assert(false);
m_read_pixel_func = [](const void* offset, unsigned bitCount)
{
return 0ull;
};
}
break;
}
switch (outBitCount)
{
case 16:
m_write_pixel_func = [](void* offset, const uint64_t pixel, unsigned bitCount)
{
*static_cast<uint16_t*>(offset) = static_cast<uint16_t>(pixel);
};
break;
case 32:
m_write_pixel_func = [](void* offset, const uint64_t pixel, unsigned bitCount)
{
*static_cast<uint32_t*>(offset) = static_cast<uint32_t>(pixel);
};
break;
case 64:
m_write_pixel_func = [](void* offset, const uint64_t pixel, unsigned bitCount)
{
*static_cast<uint64_t*>(offset) = pixel;
};
break;
default:
if (inBitCount <= 64)
{
m_write_pixel_func = [](void* offset, const uint64_t pixel, const unsigned bitCount)
{
for (auto pixelOffset = 0u; pixelOffset < bitCount; pixelOffset += 8)
{
*(static_cast<uint8_t*>(offset) + (pixelOffset / 8)) = static_cast<uint8_t>(pixel >> pixelOffset);
}
};
}
else
{
assert(false);
m_write_pixel_func = [](void* offset, uint64_t pixel, unsigned bitCount) {};
}
break;
}
}
TextureConverter::TextureConverter(const Texture* inputTexture, const ImageFormat* targetFormat)
: m_fill_r(false),
m_fill_g(false),
m_fill_b(false),
m_input_texture(inputTexture),
m_output_texture(nullptr),
m_input_format(inputTexture->GetFormat()),
m_output_format(targetFormat)
{
}
void TextureConverter::CreateOutputTexture()
{
switch (m_input_texture->GetTextureType())
{
case TextureType::T_2D:
m_output_texture =
std::make_unique<Texture2D>(m_output_format, m_input_texture->GetWidth(), m_input_texture->GetHeight(), m_input_texture->HasMipMaps());
break;
case TextureType::T_CUBE:
m_output_texture =
std::make_unique<TextureCube>(m_output_format, m_input_texture->GetWidth(), m_input_texture->GetHeight(), m_input_texture->HasMipMaps());
break;
case TextureType::T_3D:
m_output_texture = std::make_unique<Texture3D>(
m_output_format, m_input_texture->GetWidth(), m_input_texture->GetHeight(), m_input_texture->GetDepth(), m_input_texture->HasMipMaps());
break;
default:
assert(false);
break;
}
m_output_texture->Allocate();
}
void TextureConverter::ReorderUnsignedToUnsigned() const
{
const auto* inputFormat = dynamic_cast<const ImageFormatUnsigned*>(m_input_format);
const auto* outputFormat = dynamic_cast<const ImageFormatUnsigned*>(m_output_format);
const auto mipCount = m_input_texture->HasMipMaps() ? m_input_texture->GetMipMapCount() : 1;
const auto rInputMask = inputFormat->HasR() ? Mask1(inputFormat->m_r_size) << inputFormat->m_r_offset : 0;
const auto gInputMask = inputFormat->HasG() ? Mask1(inputFormat->m_g_size) << inputFormat->m_g_offset : 0;
const auto bInputMask = inputFormat->HasB() ? Mask1(inputFormat->m_b_size) << inputFormat->m_b_offset : 0;
const auto aInputMask = inputFormat->HasA() ? Mask1(inputFormat->m_a_size) << inputFormat->m_a_offset : 0;
const auto rFill = m_fill_r ? Mask1(outputFormat->m_r_size) << outputFormat->m_r_offset : 0;
const auto gFill = m_fill_g ? Mask1(outputFormat->m_g_size) << outputFormat->m_g_offset : 0;
const auto bFill = m_fill_b ? Mask1(outputFormat->m_b_size) << outputFormat->m_b_offset : 0;
const auto aOutputMask = outputFormat->HasA() ? Mask1(outputFormat->m_a_size) << outputFormat->m_a_offset : 0;
const bool rConvert = (rInputMask != 0 || rFill) && outputFormat->m_r_size > 0;
const bool gConvert = (gInputMask != 0 || gFill) && outputFormat->m_g_size > 0;
const bool bConvert = (bInputMask != 0 || bFill) && outputFormat->m_b_size > 0;
// alpha has a default of 1 so we need to convert even input has no alpha
const bool aConvert = outputFormat->m_a_size > 0;
for (auto mipLevel = 0; mipLevel < mipCount; mipLevel++)
{
const auto mipLevelSize = m_input_texture->GetSizeOfMipLevel(mipLevel) * m_input_texture->GetFaceCount();
const auto* inputBuffer = m_input_texture->GetBufferForMipLevel(mipLevel);
auto* outputBuffer = m_output_texture->GetBufferForMipLevel(mipLevel);
const auto inputBytePerPixel = inputFormat->m_bits_per_pixel / 8;
const auto outputBytePerPixel = outputFormat->m_bits_per_pixel / 8;
auto outputOffset = 0u;
for (auto inputOffset = 0u; inputOffset < mipLevelSize; inputOffset += inputBytePerPixel, outputOffset += outputBytePerPixel)
{
uint64_t outPixel = 0;
const auto inPixel = m_read_pixel_func(&inputBuffer[inputOffset], inputFormat->m_bits_per_pixel);
if (rConvert)
outPixel |= ((inPixel & rInputMask) >> inputFormat->m_r_offset << outputFormat->m_r_offset) | rFill;
if (gConvert)
outPixel |= ((inPixel & gInputMask) >> inputFormat->m_g_offset << outputFormat->m_g_offset) | gFill;
if (bConvert)
outPixel |= ((inPixel & bInputMask) >> inputFormat->m_b_offset << outputFormat->m_b_offset) | bFill;
if (aConvert)
{
const auto value = aInputMask != 0 ? (inPixel & aInputMask) >> inputFormat->m_a_offset << outputFormat->m_a_offset
: std::numeric_limits<uint64_t>::max() & aOutputMask;
outPixel |= value;
}
m_write_pixel_func(&outputBuffer[outputOffset], outPixel, outputFormat->m_bits_per_pixel);
}
}
}
void TextureConverter::ConvertUnsignedToUnsigned()
{
const auto* inputFormat = dynamic_cast<const ImageFormatUnsigned*>(m_input_format);
const auto* outputFormat = dynamic_cast<const ImageFormatUnsigned*>(m_output_format);
assert(inputFormat->m_bits_per_pixel <= 64);
assert(outputFormat->m_bits_per_pixel <= 64);
SetPixelFunctions(inputFormat->m_bits_per_pixel, outputFormat->m_bits_per_pixel);
if (CanReorder(inputFormat->m_r_size, outputFormat->m_r_size) && CanReorder(inputFormat->m_g_size, outputFormat->m_g_size)
&& CanReorder(inputFormat->m_b_size, outputFormat->m_b_size) && CanReorder(inputFormat->m_a_size, outputFormat->m_a_size))
{
ReorderUnsignedToUnsigned();
}
else
{
// Unsupported as of now
assert(false);
}
}
void TextureConverter::SetColorFill(const bool fillR, const bool fillG, const bool fillB)
{
m_fill_r = fillR;
m_fill_g = fillG;
m_fill_b = fillB;
}
std::unique_ptr<Texture> TextureConverter::Convert()
{
CreateOutputTexture();
if (m_input_format->GetType() == ImageFormatType::UNSIGNED && m_output_format->GetType() == ImageFormatType::UNSIGNED)
{
ConvertUnsignedToUnsigned();
}
else
{
// Unsupported as of now
assert(false);
}
return std::move(m_output_texture);
}
} // namespace image