#include "TextureConverter.h" #include namespace { constexpr uint64_t Mask1(const unsigned length) { if (length >= sizeof(uint64_t) * 8) return std::numeric_limits::max(); return std::numeric_limits::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(*static_cast(offset)); }; break; case 32: m_read_pixel_func = [](const void* offset, unsigned bitCount) { return static_cast(*static_cast(offset)); }; break; case 64: m_read_pixel_func = [](const void* offset, unsigned bitCount) { return *static_cast(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(*(static_cast(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(offset) = static_cast(pixel); }; break; case 32: m_write_pixel_func = [](void* offset, const uint64_t pixel, unsigned bitCount) { *static_cast(offset) = static_cast(pixel); }; break; case 64: m_write_pixel_func = [](void* offset, const uint64_t pixel, unsigned bitCount) { *static_cast(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(offset) + (pixelOffset / 8)) = static_cast(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(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(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( 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(m_input_format); const auto* outputFormat = dynamic_cast(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::max() & aOutputMask; outPixel |= value; } m_write_pixel_func(&outputBuffer[outputOffset], outPixel, outputFormat->m_bits_per_pixel); } } } void TextureConverter::ConvertUnsignedToUnsigned() { const auto* inputFormat = dynamic_cast(m_input_format); const auto* outputFormat = dynamic_cast(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 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