mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2026-06-26 19:08:07 +00:00
fix: t6 modman textures (#849)
* feat: dynamically decompress bc5 textures for modman * chore: restructure image format class * chore: keep dds file conversions * chore: convert all kinds of webgl unsupported formats * chore: add decompressors for remaining formats * chore: always set full alpha if available on bc4 and bc5 decompression
This commit is contained in:
@@ -73,10 +73,16 @@ namespace
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto loadResult = image::LoadIwi(file);
|
auto loadResult = image::LoadIwi(file);
|
||||||
if (!loadResult)
|
if (!loadResult)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
auto texture(std::move(loadResult->m_texture));
|
||||||
|
|
||||||
|
auto convertedTexture = ConvertTextureForDdsFileOutputIfNecessary(texture.get());
|
||||||
|
if (convertedTexture)
|
||||||
|
texture = std::move(convertedTexture);
|
||||||
|
|
||||||
auto outPath = iwiPath;
|
auto outPath = iwiPath;
|
||||||
outPath.replace_extension(".dds");
|
outPath.replace_extension(".dds");
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
|
|
||||||
#include "Context/ModManContext.h"
|
#include "Context/ModManContext.h"
|
||||||
#include "Game/CommonAsset.h"
|
#include "Game/CommonAsset.h"
|
||||||
|
#include "Image/Compression/ImageDecompressor.h"
|
||||||
#include "Image/DdsWriter.h"
|
#include "Image/DdsWriter.h"
|
||||||
#include "Image/ImageToCommonConverter.h"
|
#include "Image/ImageToCommonConverter.h"
|
||||||
|
#include "Image/TextureConverter.h"
|
||||||
#include "Pool/XAssetInfo.h"
|
#include "Pool/XAssetInfo.h"
|
||||||
#include "SearchPath/SearchPaths.h"
|
|
||||||
#include "Utils/Logging/Log.h"
|
#include "Utils/Logging/Log.h"
|
||||||
#include "Utils/StringUtils.h"
|
#include "Utils/StringUtils.h"
|
||||||
|
|
||||||
@@ -44,6 +45,26 @@ namespace
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<ImageFormatId> NeedsConversionToWebGlSupportedFormat(const ImageFormatId imageFormatId)
|
||||||
|
{
|
||||||
|
static std::unordered_map<ImageFormatId, ImageFormatId> unsupportedFormatMap{
|
||||||
|
{ImageFormatId::BC4, ImageFormatId::B8_G8_R8 },
|
||||||
|
{ImageFormatId::BC5, ImageFormatId::B8_G8_R8 },
|
||||||
|
{ImageFormatId::B8_G8_R8_X8, ImageFormatId::B8_G8_R8 },
|
||||||
|
{ImageFormatId::R8_G8_B8, ImageFormatId::B8_G8_R8 },
|
||||||
|
{ImageFormatId::R8_G8_B8_A8, ImageFormatId::B8_G8_R8_A8},
|
||||||
|
{ImageFormatId::A8, ImageFormatId::B8_G8_R8 },
|
||||||
|
{ImageFormatId::R8, ImageFormatId::B8_G8_R8 },
|
||||||
|
{ImageFormatId::R8_A8, ImageFormatId::B8_G8_R8 },
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto entry = unsupportedFormatMap.find(imageFormatId);
|
||||||
|
if (entry != unsupportedFormatMap.end())
|
||||||
|
return entry->second;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
void ImageDds(const webwindowed::dynamic_asset_request& request, webwindowed::dynamic_asset_response& response)
|
void ImageDds(const webwindowed::dynamic_asset_request& request, webwindowed::dynamic_asset_response& response)
|
||||||
{
|
{
|
||||||
const auto imageName = request.get_query("name");
|
const auto imageName = request.get_query("name");
|
||||||
@@ -68,8 +89,8 @@ namespace
|
|||||||
assert(zone);
|
assert(zone);
|
||||||
|
|
||||||
const auto gameName = GameId_Names[std::to_underlying(zone->m_game_id)];
|
const auto gameName = GameId_Names[std::to_underlying(zone->m_game_id)];
|
||||||
const auto converter = ToCommonConverter::GetForGame(zone->m_game_id);
|
const auto toCommonConverter = ToCommonConverter::GetForGame(zone->m_game_id);
|
||||||
if (!converter)
|
if (!toCommonConverter)
|
||||||
{
|
{
|
||||||
con::error("No image converter for game {}", gameName);
|
con::error("No image converter for game {}", gameName);
|
||||||
response.send_response(500);
|
response.send_response(500);
|
||||||
@@ -79,7 +100,7 @@ namespace
|
|||||||
std::unique_ptr<Texture> texture;
|
std::unique_ptr<Texture> texture;
|
||||||
{
|
{
|
||||||
const auto searchPaths = ModManContext::Get().m_fast_file.GetSearchPaths();
|
const auto searchPaths = ModManContext::Get().m_fast_file.GetSearchPaths();
|
||||||
texture = converter->Convert(*image, searchPaths.Data());
|
texture = toCommonConverter->Convert(*image, searchPaths.Data());
|
||||||
if (!texture)
|
if (!texture)
|
||||||
{
|
{
|
||||||
con::warn("Failed to convert image {} of zone {}", *imageName, *zoneName);
|
con::warn("Failed to convert image {} of zone {}", *imageName, *zoneName);
|
||||||
@@ -88,6 +109,31 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto* originalTextureFormat = texture->GetFormat();
|
||||||
|
const auto originalTextureFormatId = originalTextureFormat->GetId();
|
||||||
|
const auto targetFormatId = NeedsConversionToWebGlSupportedFormat(originalTextureFormatId);
|
||||||
|
if (targetFormatId)
|
||||||
|
{
|
||||||
|
const auto* targetFormat = ImageFormat::GetImageFormatById(*targetFormatId);
|
||||||
|
if (originalTextureFormat->GetType() == ImageFormatType::BLOCK_COMPRESSED)
|
||||||
|
{
|
||||||
|
auto* textureDecompressor = ImageDecompressor::GetDecompressorForFormat(originalTextureFormatId);
|
||||||
|
assert(textureDecompressor);
|
||||||
|
|
||||||
|
if (textureDecompressor)
|
||||||
|
texture = textureDecompressor->Decompress(*texture, targetFormat);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TextureConverter converter(texture.get(), targetFormat);
|
||||||
|
auto newTexture = converter.Convert();
|
||||||
|
assert(newTexture);
|
||||||
|
|
||||||
|
if (newTexture)
|
||||||
|
texture = std::move(newTexture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
DdsWriter output;
|
DdsWriter output;
|
||||||
output.DumpImage(ss, texture.get());
|
output.DumpImage(ss, texture.get());
|
||||||
|
|||||||
@@ -0,0 +1,448 @@
|
|||||||
|
#include "DecompressorBc1.h"
|
||||||
|
|
||||||
|
#include "Utils/Alignment.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr auto BC1_BLOCK_PIXELS = 4u;
|
||||||
|
constexpr unsigned BC1_BLUE_MASK = 0x1F;
|
||||||
|
constexpr unsigned BC1_BLUE_SHIFT = 0;
|
||||||
|
constexpr unsigned BC1_BLUE_MAX = 0x1F;
|
||||||
|
constexpr unsigned BC1_GREEN_MASK = 0x7E0;
|
||||||
|
constexpr unsigned BC1_GREEN_SHIFT = 5;
|
||||||
|
constexpr unsigned BC1_GREEN_MAX = 0x3F;
|
||||||
|
constexpr unsigned BC1_RED_MASK = 0xF800;
|
||||||
|
constexpr unsigned BC1_RED_SHIFT = 11;
|
||||||
|
constexpr unsigned BC1_RED_MAX = 0x1F;
|
||||||
|
constexpr auto BC1_COLOR_CHANNELS_SIZE = 8u;
|
||||||
|
constexpr auto BC1_BLOCK_SIZE = BC1_COLOR_CHANNELS_SIZE;
|
||||||
|
|
||||||
|
void SetupColorTables(
|
||||||
|
const uint8_t* in, uint8_t (&colorTableRed)[4], uint8_t (&colorTableGreen)[4], uint8_t (&colorTableBlue)[4], uint8_t (&colorTableAlpha)[4])
|
||||||
|
{
|
||||||
|
#define EXTRACT_COLOR_CHANNEL_BC1(color, mask, shift, colMax) \
|
||||||
|
static_cast<uint8_t>(static_cast<uint16_t>(((color) & (mask)) >> (shift)) * std::numeric_limits<uint8_t>::max() / (colMax))
|
||||||
|
#define INTERPOLATE_BC1(colorTable, val0, val1) \
|
||||||
|
static_cast<uint8_t>( \
|
||||||
|
(static_cast<uint16_t>(val0) * static_cast<uint16_t>((colorTable)[0]) + static_cast<uint16_t>(val1) * static_cast<uint16_t>((colorTable)[1])) \
|
||||||
|
/ ((val0) + (val1)))
|
||||||
|
|
||||||
|
const auto color0 = static_cast<uint16_t>(in[0] | (in[1] << 8));
|
||||||
|
const auto color1 = static_cast<uint16_t>(in[2] | (in[3] << 8));
|
||||||
|
|
||||||
|
colorTableRed[0] = EXTRACT_COLOR_CHANNEL_BC1(color0, BC1_RED_MASK, BC1_RED_SHIFT, BC1_RED_MAX);
|
||||||
|
colorTableRed[1] = EXTRACT_COLOR_CHANNEL_BC1(color1, BC1_RED_MASK, BC1_RED_SHIFT, BC1_RED_MAX);
|
||||||
|
|
||||||
|
colorTableGreen[0] = EXTRACT_COLOR_CHANNEL_BC1(color0, BC1_GREEN_MASK, BC1_GREEN_SHIFT, BC1_GREEN_MAX);
|
||||||
|
colorTableGreen[1] = EXTRACT_COLOR_CHANNEL_BC1(color1, BC1_GREEN_MASK, BC1_GREEN_SHIFT, BC1_GREEN_MAX);
|
||||||
|
|
||||||
|
colorTableBlue[0] = EXTRACT_COLOR_CHANNEL_BC1(color0, BC1_BLUE_MASK, BC1_BLUE_SHIFT, BC1_BLUE_MAX);
|
||||||
|
colorTableBlue[1] = EXTRACT_COLOR_CHANNEL_BC1(color1, BC1_BLUE_MASK, BC1_BLUE_SHIFT, BC1_BLUE_MAX);
|
||||||
|
|
||||||
|
colorTableAlpha[0] = std::numeric_limits<uint8_t>::max();
|
||||||
|
colorTableAlpha[1] = std::numeric_limits<uint8_t>::max();
|
||||||
|
colorTableAlpha[2] = std::numeric_limits<uint8_t>::max();
|
||||||
|
|
||||||
|
if (color0 > color1)
|
||||||
|
{
|
||||||
|
colorTableRed[2] = INTERPOLATE_BC1(colorTableRed, 2, 1);
|
||||||
|
colorTableRed[3] = INTERPOLATE_BC1(colorTableRed, 1, 2);
|
||||||
|
|
||||||
|
colorTableGreen[2] = INTERPOLATE_BC1(colorTableGreen, 2, 1);
|
||||||
|
colorTableGreen[3] = INTERPOLATE_BC1(colorTableGreen, 1, 2);
|
||||||
|
|
||||||
|
colorTableBlue[2] = INTERPOLATE_BC1(colorTableBlue, 2, 1);
|
||||||
|
colorTableBlue[3] = INTERPOLATE_BC1(colorTableBlue, 1, 2);
|
||||||
|
|
||||||
|
colorTableAlpha[3] = std::numeric_limits<uint8_t>::max();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
colorTableRed[2] = INTERPOLATE_BC1(colorTableRed, 1, 1);
|
||||||
|
colorTableGreen[2] = INTERPOLATE_BC1(colorTableGreen, 1, 1);
|
||||||
|
colorTableBlue[2] = INTERPOLATE_BC1(colorTableBlue, 1, 1);
|
||||||
|
|
||||||
|
colorTableRed[3] = 0;
|
||||||
|
colorTableGreen[3] = 0;
|
||||||
|
colorTableBlue[3] = 0;
|
||||||
|
colorTableAlpha[3] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef INTERPOLATE_BC1
|
||||||
|
#undef EXTRACT_COLOR_CHANNEL_BC1
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecompressBlock(const uint8_t* in,
|
||||||
|
uint8_t* out,
|
||||||
|
const unsigned outPitch,
|
||||||
|
const unsigned outPixelSize,
|
||||||
|
const unsigned outOffsetR,
|
||||||
|
const unsigned outOffsetG,
|
||||||
|
const unsigned outOffsetB,
|
||||||
|
const unsigned outOffsetA,
|
||||||
|
const bool hasAlpha)
|
||||||
|
{
|
||||||
|
uint8_t colorTableRed[4];
|
||||||
|
uint8_t colorTableGreen[4];
|
||||||
|
uint8_t colorTableBlue[4];
|
||||||
|
uint8_t colorTableAlpha[4];
|
||||||
|
SetupColorTables(in, colorTableRed, colorTableGreen, colorTableBlue, colorTableAlpha);
|
||||||
|
|
||||||
|
const uint32_t dataColor0 = in[4];
|
||||||
|
const uint32_t dataColor1 = in[5];
|
||||||
|
const uint32_t dataColor2 = in[6];
|
||||||
|
const uint32_t dataColor3 = in[7];
|
||||||
|
|
||||||
|
out[0 * outPitch + 0 * outPixelSize + outOffsetR] = colorTableRed[(dataColor0 & (0x3 << 0)) >> 0];
|
||||||
|
out[0 * outPitch + 0 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor0 & (0x3 << 0)) >> 0];
|
||||||
|
out[0 * outPitch + 0 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor0 & (0x3 << 0)) >> 0];
|
||||||
|
|
||||||
|
out[0 * outPitch + 1 * outPixelSize + outOffsetR] = colorTableRed[(dataColor0 & (0x3 << 2)) >> 2];
|
||||||
|
out[0 * outPitch + 1 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor0 & (0x3 << 2)) >> 2];
|
||||||
|
out[0 * outPitch + 1 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor0 & (0x3 << 2)) >> 2];
|
||||||
|
|
||||||
|
out[0 * outPitch + 2 * outPixelSize + outOffsetR] = colorTableRed[(dataColor0 & (0x3 << 4)) >> 4];
|
||||||
|
out[0 * outPitch + 2 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor0 & (0x3 << 4)) >> 4];
|
||||||
|
out[0 * outPitch + 2 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor0 & (0x3 << 4)) >> 4];
|
||||||
|
|
||||||
|
out[0 * outPitch + 3 * outPixelSize + outOffsetR] = colorTableRed[(dataColor0 & (0x3 << 6)) >> 6];
|
||||||
|
out[0 * outPitch + 3 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor0 & (0x3 << 6)) >> 6];
|
||||||
|
out[0 * outPitch + 3 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor0 & (0x3 << 6)) >> 6];
|
||||||
|
|
||||||
|
out[1 * outPitch + 0 * outPixelSize + outOffsetR] = colorTableRed[(dataColor1 & (0x3 << 0)) >> 0];
|
||||||
|
out[1 * outPitch + 0 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor1 & (0x3 << 0)) >> 0];
|
||||||
|
out[1 * outPitch + 0 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor1 & (0x3 << 0)) >> 0];
|
||||||
|
|
||||||
|
out[1 * outPitch + 1 * outPixelSize + outOffsetR] = colorTableRed[(dataColor1 & (0x3 << 2)) >> 2];
|
||||||
|
out[1 * outPitch + 1 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor1 & (0x3 << 2)) >> 2];
|
||||||
|
out[1 * outPitch + 1 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor1 & (0x3 << 2)) >> 2];
|
||||||
|
|
||||||
|
out[1 * outPitch + 2 * outPixelSize + outOffsetR] = colorTableRed[(dataColor1 & (0x3 << 4)) >> 4];
|
||||||
|
out[1 * outPitch + 2 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor1 & (0x3 << 4)) >> 4];
|
||||||
|
out[1 * outPitch + 2 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor1 & (0x3 << 4)) >> 4];
|
||||||
|
|
||||||
|
out[1 * outPitch + 3 * outPixelSize + outOffsetR] = colorTableRed[(dataColor1 & (0x3 << 6)) >> 6];
|
||||||
|
out[1 * outPitch + 3 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor1 & (0x3 << 6)) >> 6];
|
||||||
|
out[1 * outPitch + 3 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor1 & (0x3 << 6)) >> 6];
|
||||||
|
|
||||||
|
out[2 * outPitch + 0 * outPixelSize + outOffsetR] = colorTableRed[(dataColor2 & (0x3 << 0)) >> 0];
|
||||||
|
out[2 * outPitch + 0 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor2 & (0x3 << 0)) >> 0];
|
||||||
|
out[2 * outPitch + 0 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor2 & (0x3 << 0)) >> 0];
|
||||||
|
|
||||||
|
out[2 * outPitch + 1 * outPixelSize + outOffsetR] = colorTableRed[(dataColor2 & (0x3 << 2)) >> 2];
|
||||||
|
out[2 * outPitch + 1 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor2 & (0x3 << 2)) >> 2];
|
||||||
|
out[2 * outPitch + 1 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor2 & (0x3 << 2)) >> 2];
|
||||||
|
|
||||||
|
out[2 * outPitch + 2 * outPixelSize + outOffsetR] = colorTableRed[(dataColor2 & (0x3 << 4)) >> 4];
|
||||||
|
out[2 * outPitch + 2 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor2 & (0x3 << 4)) >> 4];
|
||||||
|
out[2 * outPitch + 2 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor2 & (0x3 << 4)) >> 4];
|
||||||
|
|
||||||
|
out[2 * outPitch + 3 * outPixelSize + outOffsetR] = colorTableRed[(dataColor2 & (0x3 << 6)) >> 6];
|
||||||
|
out[2 * outPitch + 3 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor2 & (0x3 << 6)) >> 6];
|
||||||
|
out[2 * outPitch + 3 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor2 & (0x3 << 6)) >> 6];
|
||||||
|
|
||||||
|
out[3 * outPitch + 0 * outPixelSize + outOffsetR] = colorTableRed[(dataColor3 & (0x3 << 0)) >> 0];
|
||||||
|
out[3 * outPitch + 0 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor3 & (0x3 << 0)) >> 0];
|
||||||
|
out[3 * outPitch + 0 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor3 & (0x3 << 0)) >> 0];
|
||||||
|
|
||||||
|
out[3 * outPitch + 1 * outPixelSize + outOffsetR] = colorTableRed[(dataColor3 & (0x3 << 2)) >> 2];
|
||||||
|
out[3 * outPitch + 1 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor3 & (0x3 << 2)) >> 2];
|
||||||
|
out[3 * outPitch + 1 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor3 & (0x3 << 2)) >> 2];
|
||||||
|
|
||||||
|
out[3 * outPitch + 2 * outPixelSize + outOffsetR] = colorTableRed[(dataColor3 & (0x3 << 4)) >> 4];
|
||||||
|
out[3 * outPitch + 2 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor3 & (0x3 << 4)) >> 4];
|
||||||
|
out[3 * outPitch + 2 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor3 & (0x3 << 4)) >> 4];
|
||||||
|
|
||||||
|
out[3 * outPitch + 3 * outPixelSize + outOffsetR] = colorTableRed[(dataColor3 & (0x3 << 6)) >> 6];
|
||||||
|
out[3 * outPitch + 3 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor3 & (0x3 << 6)) >> 6];
|
||||||
|
out[3 * outPitch + 3 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor3 & (0x3 << 6)) >> 6];
|
||||||
|
|
||||||
|
if (hasAlpha)
|
||||||
|
{
|
||||||
|
out[0 * outPitch + 0 * outPixelSize + outOffsetA] = colorTableAlpha[(dataColor0 & (0x3 << 0)) >> 0];
|
||||||
|
out[0 * outPitch + 1 * outPixelSize + outOffsetA] = colorTableAlpha[(dataColor0 & (0x3 << 2)) >> 2];
|
||||||
|
out[0 * outPitch + 2 * outPixelSize + outOffsetA] = colorTableAlpha[(dataColor0 & (0x3 << 4)) >> 4];
|
||||||
|
out[0 * outPitch + 3 * outPixelSize + outOffsetA] = colorTableAlpha[(dataColor0 & (0x3 << 6)) >> 6];
|
||||||
|
out[1 * outPitch + 0 * outPixelSize + outOffsetA] = colorTableAlpha[(dataColor1 & (0x3 << 0)) >> 0];
|
||||||
|
out[1 * outPitch + 1 * outPixelSize + outOffsetA] = colorTableAlpha[(dataColor1 & (0x3 << 2)) >> 2];
|
||||||
|
out[1 * outPitch + 2 * outPixelSize + outOffsetA] = colorTableAlpha[(dataColor1 & (0x3 << 4)) >> 4];
|
||||||
|
out[1 * outPitch + 3 * outPixelSize + outOffsetA] = colorTableAlpha[(dataColor1 & (0x3 << 6)) >> 6];
|
||||||
|
out[2 * outPitch + 0 * outPixelSize + outOffsetA] = colorTableAlpha[(dataColor2 & (0x3 << 0)) >> 0];
|
||||||
|
out[2 * outPitch + 1 * outPixelSize + outOffsetA] = colorTableAlpha[(dataColor2 & (0x3 << 2)) >> 2];
|
||||||
|
out[2 * outPitch + 2 * outPixelSize + outOffsetA] = colorTableAlpha[(dataColor2 & (0x3 << 4)) >> 4];
|
||||||
|
out[2 * outPitch + 3 * outPixelSize + outOffsetA] = colorTableAlpha[(dataColor2 & (0x3 << 6)) >> 6];
|
||||||
|
out[3 * outPitch + 0 * outPixelSize + outOffsetA] = colorTableAlpha[(dataColor3 & (0x3 << 0)) >> 0];
|
||||||
|
out[3 * outPitch + 1 * outPixelSize + outOffsetA] = colorTableAlpha[(dataColor3 & (0x3 << 2)) >> 2];
|
||||||
|
out[3 * outPitch + 2 * outPixelSize + outOffsetA] = colorTableAlpha[(dataColor3 & (0x3 << 4)) >> 4];
|
||||||
|
out[3 * outPitch + 3 * outPixelSize + outOffsetA] = colorTableAlpha[(dataColor3 & (0x3 << 6)) >> 6];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecompressBlockEdge(const uint8_t* in,
|
||||||
|
uint8_t* out,
|
||||||
|
const unsigned width,
|
||||||
|
const unsigned height,
|
||||||
|
const unsigned outPitch,
|
||||||
|
const unsigned outPixelSize,
|
||||||
|
const unsigned outOffsetR,
|
||||||
|
const unsigned outOffsetG,
|
||||||
|
const unsigned outOffsetB,
|
||||||
|
const unsigned outOffsetA,
|
||||||
|
const bool hasAlpha)
|
||||||
|
{
|
||||||
|
assert(width <= BC1_BLOCK_PIXELS);
|
||||||
|
assert(height <= BC1_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
uint8_t colorTableRed[4];
|
||||||
|
uint8_t colorTableGreen[4];
|
||||||
|
uint8_t colorTableBlue[4];
|
||||||
|
uint8_t colorTableAlpha[4];
|
||||||
|
SetupColorTables(in, colorTableRed, colorTableGreen, colorTableBlue, colorTableAlpha);
|
||||||
|
|
||||||
|
const uint8_t dataColor0 = in[4];
|
||||||
|
const uint8_t dataColor1 = in[5];
|
||||||
|
const uint8_t dataColor2 = in[6];
|
||||||
|
const uint8_t dataColor3 = in[7];
|
||||||
|
|
||||||
|
const uint8_t pixelsRed[]{
|
||||||
|
colorTableRed[(dataColor0 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableRed[(dataColor0 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableRed[(dataColor0 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableRed[(dataColor0 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableRed[(dataColor1 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableRed[(dataColor1 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableRed[(dataColor1 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableRed[(dataColor1 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableRed[(dataColor2 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableRed[(dataColor2 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableRed[(dataColor2 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableRed[(dataColor2 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableRed[(dataColor3 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableRed[(dataColor3 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableRed[(dataColor3 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableRed[(dataColor3 & (0x3 << 6)) >> 6],
|
||||||
|
};
|
||||||
|
static_assert(std::extent_v<decltype(pixelsRed)> == BC1_BLOCK_PIXELS * BC1_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
const uint8_t pixelsGreen[]{
|
||||||
|
colorTableGreen[(dataColor0 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableGreen[(dataColor0 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableGreen[(dataColor0 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableGreen[(dataColor0 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableGreen[(dataColor1 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableGreen[(dataColor1 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableGreen[(dataColor1 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableGreen[(dataColor1 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableGreen[(dataColor2 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableGreen[(dataColor2 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableGreen[(dataColor2 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableGreen[(dataColor2 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableGreen[(dataColor3 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableGreen[(dataColor3 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableGreen[(dataColor3 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableGreen[(dataColor3 & (0x3 << 6)) >> 6],
|
||||||
|
};
|
||||||
|
static_assert(std::extent_v<decltype(pixelsGreen)> == BC1_BLOCK_PIXELS * BC1_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
const uint8_t pixelsBlue[]{
|
||||||
|
colorTableBlue[(dataColor0 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableBlue[(dataColor0 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableBlue[(dataColor0 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableBlue[(dataColor0 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableBlue[(dataColor1 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableBlue[(dataColor1 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableBlue[(dataColor1 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableBlue[(dataColor1 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableBlue[(dataColor2 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableBlue[(dataColor2 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableBlue[(dataColor2 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableBlue[(dataColor2 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableBlue[(dataColor3 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableBlue[(dataColor3 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableBlue[(dataColor3 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableBlue[(dataColor3 & (0x3 << 6)) >> 6],
|
||||||
|
};
|
||||||
|
static_assert(std::extent_v<decltype(pixelsBlue)> == BC1_BLOCK_PIXELS * BC1_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
for (auto curHeight = 0u; curHeight < height; curHeight++)
|
||||||
|
{
|
||||||
|
for (auto curWidth = 0u; curWidth < width; curWidth++)
|
||||||
|
{
|
||||||
|
out[curHeight * outPitch + curWidth * outPixelSize + outOffsetR] = pixelsRed[curHeight * BC1_BLOCK_PIXELS + curWidth];
|
||||||
|
out[curHeight * outPitch + curWidth * outPixelSize + outOffsetG] = pixelsGreen[curHeight * BC1_BLOCK_PIXELS + curWidth];
|
||||||
|
out[curHeight * outPitch + curWidth * outPixelSize + outOffsetB] = pixelsBlue[curHeight * BC1_BLOCK_PIXELS + curWidth];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasAlpha)
|
||||||
|
{
|
||||||
|
const uint8_t pixelsAlpha[]{
|
||||||
|
colorTableAlpha[(dataColor0 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableAlpha[(dataColor0 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableAlpha[(dataColor0 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableAlpha[(dataColor0 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableAlpha[(dataColor1 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableAlpha[(dataColor1 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableAlpha[(dataColor1 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableAlpha[(dataColor1 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableAlpha[(dataColor2 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableAlpha[(dataColor2 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableAlpha[(dataColor2 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableAlpha[(dataColor2 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableAlpha[(dataColor3 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableAlpha[(dataColor3 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableAlpha[(dataColor3 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableAlpha[(dataColor3 & (0x3 << 6)) >> 6],
|
||||||
|
};
|
||||||
|
static_assert(std::extent_v<decltype(pixelsAlpha)> == BC1_BLOCK_PIXELS * BC1_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
for (auto curHeight = 0u; curHeight < height; curHeight++)
|
||||||
|
{
|
||||||
|
for (auto curWidth = 0u; curWidth < width; curWidth++)
|
||||||
|
{
|
||||||
|
out[curHeight * outPitch + curWidth * outPixelSize + outOffsetA] = pixelsAlpha[curHeight * BC1_BLOCK_PIXELS + curWidth];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace image
|
||||||
|
{
|
||||||
|
std::unique_ptr<Texture> DecompressorBc1::Decompress(const Texture& input, const ImageFormat* targetFormat)
|
||||||
|
{
|
||||||
|
assert(targetFormat->GetType() == ImageFormatType::UNSIGNED);
|
||||||
|
if (targetFormat->GetType() != ImageFormatType::UNSIGNED)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const auto* unsignedTargetFormat = dynamic_cast<const ImageFormatUnsigned*>(targetFormat);
|
||||||
|
|
||||||
|
const auto outPixelSize = unsignedTargetFormat->m_bits_per_pixel / 8u;
|
||||||
|
|
||||||
|
// Only support formats with byte aligned channels
|
||||||
|
const auto redByteAligned = unsignedTargetFormat->m_r_size == 8 && (unsignedTargetFormat->m_r_offset % 8) == 0;
|
||||||
|
const auto greenByteAligned = unsignedTargetFormat->m_g_size == 8 && (unsignedTargetFormat->m_g_offset % 8) == 0;
|
||||||
|
const auto blueByteAligned = unsignedTargetFormat->m_b_size == 8 && (unsignedTargetFormat->m_b_offset % 8) == 0;
|
||||||
|
const auto hasAlpha = unsignedTargetFormat->m_a_size > 0;
|
||||||
|
const auto alphaByteAligned = unsignedTargetFormat->m_a_size == 8 && (unsignedTargetFormat->m_a_offset % 8) == 0;
|
||||||
|
assert(redByteAligned);
|
||||||
|
assert(greenByteAligned);
|
||||||
|
assert(blueByteAligned);
|
||||||
|
assert(!hasAlpha || alphaByteAligned);
|
||||||
|
if (!redByteAligned || !greenByteAligned || !blueByteAligned || (hasAlpha && !alphaByteAligned))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const auto outOffsetR = unsignedTargetFormat->m_r_offset / 8;
|
||||||
|
const auto outOffsetG = unsignedTargetFormat->m_g_offset / 8;
|
||||||
|
const auto outOffsetB = unsignedTargetFormat->m_b_offset / 8;
|
||||||
|
const auto outOffsetA = unsignedTargetFormat->m_a_offset / 8;
|
||||||
|
|
||||||
|
const auto width = input.GetWidth();
|
||||||
|
const auto height = input.GetHeight();
|
||||||
|
const auto depth = input.GetDepth();
|
||||||
|
|
||||||
|
auto result = Texture::CreateForType(input.GetTextureType(), targetFormat, width, height, depth, input.HasMipMaps());
|
||||||
|
result->Allocate();
|
||||||
|
|
||||||
|
const auto faceCount = result->GetFaceCount();
|
||||||
|
const auto mipCount = result->HasMipMaps() ? result->GetMipMapCount() : 1;
|
||||||
|
assert(mipCount == input.HasMipMaps() ? input.GetMipMapCount() : 1);
|
||||||
|
|
||||||
|
for (auto mipLevel = 0; mipLevel < mipCount; mipLevel++)
|
||||||
|
{
|
||||||
|
const auto mipWidth = std::max<unsigned>(width >> mipLevel, 1u);
|
||||||
|
const auto mipHeight = std::max<unsigned>(height >> mipLevel, 1u);
|
||||||
|
const auto mipDepth = std::max<unsigned>(depth >> mipLevel, 1u);
|
||||||
|
const unsigned mipPitch = outPixelSize * mipWidth;
|
||||||
|
assert(mipPitch == result->GetFormat()->GetPitch(mipLevel, width));
|
||||||
|
|
||||||
|
for (auto face = 0; face < faceCount; face++)
|
||||||
|
{
|
||||||
|
const auto* bufferIn = input.GetBufferForMipLevel(mipLevel, face);
|
||||||
|
auto* bufferOut = result->GetBufferForMipLevel(mipLevel, face);
|
||||||
|
|
||||||
|
for (auto curDepth = 0u; curDepth < mipDepth; curDepth++)
|
||||||
|
{
|
||||||
|
const auto fullBlocksWidth = utils::AlignToPrevious(mipWidth, BC1_BLOCK_PIXELS);
|
||||||
|
const auto fullBlocksHeight = utils::AlignToPrevious(mipHeight, BC1_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
for (auto curHeight = 0u; curHeight < fullBlocksHeight; curHeight += BC1_BLOCK_PIXELS)
|
||||||
|
{
|
||||||
|
for (auto curWidth = 0u; curWidth < fullBlocksWidth; curWidth += BC1_BLOCK_PIXELS)
|
||||||
|
{
|
||||||
|
DecompressBlock(bufferIn, bufferOut, mipPitch, outPixelSize, outOffsetR, outOffsetG, outOffsetB, outOffsetA, hasAlpha);
|
||||||
|
bufferIn += BC1_BLOCK_SIZE;
|
||||||
|
bufferOut += outPixelSize * BC1_BLOCK_PIXELS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullBlocksWidth < mipWidth)
|
||||||
|
{
|
||||||
|
const auto edgeBlockWidth = mipWidth - fullBlocksWidth;
|
||||||
|
DecompressBlockEdge(bufferIn,
|
||||||
|
bufferOut,
|
||||||
|
edgeBlockWidth,
|
||||||
|
BC1_BLOCK_PIXELS,
|
||||||
|
mipPitch,
|
||||||
|
outPixelSize,
|
||||||
|
outOffsetR,
|
||||||
|
outOffsetG,
|
||||||
|
outOffsetB,
|
||||||
|
outOffsetA,
|
||||||
|
hasAlpha);
|
||||||
|
bufferIn += BC1_BLOCK_SIZE;
|
||||||
|
bufferOut += outPixelSize * edgeBlockWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferOut += mipPitch * (BC1_BLOCK_PIXELS - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullBlocksHeight < mipHeight)
|
||||||
|
{
|
||||||
|
const auto edgeBlockHeight = mipHeight - fullBlocksHeight;
|
||||||
|
for (auto curWidth = 0u; curWidth < fullBlocksWidth; curWidth += BC1_BLOCK_PIXELS)
|
||||||
|
{
|
||||||
|
DecompressBlockEdge(bufferIn,
|
||||||
|
bufferOut,
|
||||||
|
BC1_BLOCK_PIXELS,
|
||||||
|
edgeBlockHeight,
|
||||||
|
mipPitch,
|
||||||
|
outPixelSize,
|
||||||
|
outOffsetR,
|
||||||
|
outOffsetG,
|
||||||
|
outOffsetB,
|
||||||
|
outOffsetA,
|
||||||
|
hasAlpha);
|
||||||
|
bufferIn += BC1_BLOCK_SIZE;
|
||||||
|
bufferOut += outPixelSize * BC1_BLOCK_PIXELS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullBlocksWidth < mipWidth)
|
||||||
|
{
|
||||||
|
const auto edgeBlockWidth = mipWidth - fullBlocksWidth;
|
||||||
|
DecompressBlockEdge(bufferIn,
|
||||||
|
bufferOut,
|
||||||
|
edgeBlockWidth,
|
||||||
|
edgeBlockHeight,
|
||||||
|
mipPitch,
|
||||||
|
outPixelSize,
|
||||||
|
outOffsetR,
|
||||||
|
outOffsetG,
|
||||||
|
outOffsetB,
|
||||||
|
outOffsetA,
|
||||||
|
hasAlpha);
|
||||||
|
bufferIn += BC1_BLOCK_SIZE;
|
||||||
|
bufferOut += outPixelSize * edgeBlockWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferOut += mipPitch * (BC1_BLOCK_PIXELS - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} // namespace image
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ImageDecompressor.h"
|
||||||
|
|
||||||
|
namespace image
|
||||||
|
{
|
||||||
|
class DecompressorBc1 final : public ImageDecompressor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::unique_ptr<Texture> Decompress(const Texture& input, const ImageFormat* targetFormat) override;
|
||||||
|
};
|
||||||
|
} // namespace image
|
||||||
@@ -0,0 +1,387 @@
|
|||||||
|
#include "DecompressorBc2.h"
|
||||||
|
|
||||||
|
#include "Utils/Alignment.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr auto BC2_BLOCK_PIXELS = 4u;
|
||||||
|
constexpr unsigned BC2_BLUE_MASK = 0x1F;
|
||||||
|
constexpr unsigned BC2_BLUE_SHIFT = 0;
|
||||||
|
constexpr unsigned BC2_BLUE_MAX = 0x1F;
|
||||||
|
constexpr unsigned BC2_GREEN_MASK = 0x7E0;
|
||||||
|
constexpr unsigned BC2_GREEN_SHIFT = 5;
|
||||||
|
constexpr unsigned BC2_GREEN_MAX = 0x3F;
|
||||||
|
constexpr unsigned BC2_RED_MASK = 0xF800;
|
||||||
|
constexpr unsigned BC2_RED_SHIFT = 11;
|
||||||
|
constexpr unsigned BC2_RED_MAX = 0x1F;
|
||||||
|
constexpr auto BC2_COLOR_CHANNELS_SIZE = 8u;
|
||||||
|
constexpr auto BC2_ALPHA_CHANNEL_SIZE = 8u;
|
||||||
|
constexpr auto BC2_BLOCK_SIZE = BC2_COLOR_CHANNELS_SIZE + BC2_ALPHA_CHANNEL_SIZE;
|
||||||
|
|
||||||
|
void SetupColorTables(const uint8_t* in, uint8_t (&colorTableRed)[4], uint8_t (&colorTableGreen)[4], uint8_t (&colorTableBlue)[4])
|
||||||
|
{
|
||||||
|
#define EXTRACT_COLOR_CHANNEL_BC2(color, mask, shift, colMax) \
|
||||||
|
static_cast<uint8_t>(static_cast<uint16_t>(((color) & (mask)) >> (shift)) * std::numeric_limits<uint8_t>::max() / (colMax))
|
||||||
|
#define INTERPOLATE_BC2(colorTable, val0, val1) \
|
||||||
|
static_cast<uint8_t>( \
|
||||||
|
(static_cast<uint16_t>(val0) * static_cast<uint16_t>((colorTable)[0]) + static_cast<uint16_t>(val1) * static_cast<uint16_t>((colorTable)[1])) \
|
||||||
|
/ ((val0) + (val1)))
|
||||||
|
|
||||||
|
const auto color0 = static_cast<uint16_t>(in[0 + BC2_ALPHA_CHANNEL_SIZE] | (in[1 + BC2_ALPHA_CHANNEL_SIZE] << 8));
|
||||||
|
const auto color1 = static_cast<uint16_t>(in[2 + BC2_ALPHA_CHANNEL_SIZE] | (in[3 + BC2_ALPHA_CHANNEL_SIZE] << 8));
|
||||||
|
|
||||||
|
colorTableRed[0] = EXTRACT_COLOR_CHANNEL_BC2(color0, BC2_RED_MASK, BC2_RED_SHIFT, BC2_RED_MAX);
|
||||||
|
colorTableRed[1] = EXTRACT_COLOR_CHANNEL_BC2(color1, BC2_RED_MASK, BC2_RED_SHIFT, BC2_RED_MAX);
|
||||||
|
colorTableRed[2] = INTERPOLATE_BC2(colorTableRed, 2, 1);
|
||||||
|
colorTableRed[3] = INTERPOLATE_BC2(colorTableRed, 1, 2);
|
||||||
|
|
||||||
|
colorTableGreen[0] = EXTRACT_COLOR_CHANNEL_BC2(color0, BC2_GREEN_MASK, BC2_GREEN_SHIFT, BC2_GREEN_MAX);
|
||||||
|
colorTableGreen[1] = EXTRACT_COLOR_CHANNEL_BC2(color1, BC2_GREEN_MASK, BC2_GREEN_SHIFT, BC2_GREEN_MAX);
|
||||||
|
colorTableGreen[2] = INTERPOLATE_BC2(colorTableGreen, 2, 1);
|
||||||
|
colorTableGreen[3] = INTERPOLATE_BC2(colorTableGreen, 1, 2);
|
||||||
|
|
||||||
|
colorTableBlue[0] = EXTRACT_COLOR_CHANNEL_BC2(color0, BC2_BLUE_MASK, BC2_BLUE_SHIFT, BC2_BLUE_MAX);
|
||||||
|
colorTableBlue[1] = EXTRACT_COLOR_CHANNEL_BC2(color1, BC2_BLUE_MASK, BC2_BLUE_SHIFT, BC2_BLUE_MAX);
|
||||||
|
colorTableBlue[2] = INTERPOLATE_BC2(colorTableBlue, 2, 1);
|
||||||
|
colorTableBlue[3] = INTERPOLATE_BC2(colorTableBlue, 1, 2);
|
||||||
|
|
||||||
|
#undef INTERPOLATE_BC2
|
||||||
|
#undef EXTRACT_COLOR_CHANNEL_BC2
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecompressBlock(const uint8_t* in,
|
||||||
|
uint8_t* out,
|
||||||
|
const unsigned outPitch,
|
||||||
|
const unsigned outPixelSize,
|
||||||
|
const unsigned outOffsetR,
|
||||||
|
const unsigned outOffsetG,
|
||||||
|
const unsigned outOffsetB,
|
||||||
|
const unsigned outOffsetA)
|
||||||
|
{
|
||||||
|
uint8_t colorTableRed[4];
|
||||||
|
uint8_t colorTableGreen[4];
|
||||||
|
uint8_t colorTableBlue[4];
|
||||||
|
SetupColorTables(in, colorTableRed, colorTableGreen, colorTableBlue);
|
||||||
|
|
||||||
|
const uint16_t dataAlpha0 = static_cast<uint16_t>(in[0] | static_cast<uint32_t>(in[1] << 8u));
|
||||||
|
const uint16_t dataAlpha1 = static_cast<uint16_t>(in[2] | static_cast<uint32_t>(in[3] << 8u));
|
||||||
|
const uint16_t dataAlpha2 = static_cast<uint16_t>(in[4] | static_cast<uint32_t>(in[5] << 8u));
|
||||||
|
const uint16_t dataAlpha3 = static_cast<uint16_t>(in[6] | static_cast<uint32_t>(in[7] << 8u));
|
||||||
|
const uint32_t dataColor0 = in[BC2_ALPHA_CHANNEL_SIZE + 4];
|
||||||
|
const uint32_t dataColor1 = in[BC2_ALPHA_CHANNEL_SIZE + 5];
|
||||||
|
const uint32_t dataColor2 = in[BC2_ALPHA_CHANNEL_SIZE + 6];
|
||||||
|
const uint32_t dataColor3 = in[BC2_ALPHA_CHANNEL_SIZE + 7];
|
||||||
|
|
||||||
|
out[0 * outPitch + 0 * outPixelSize + outOffsetR] = colorTableRed[(dataColor0 & (0x3 << 0)) >> 0];
|
||||||
|
out[0 * outPitch + 0 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor0 & (0x3 << 0)) >> 0];
|
||||||
|
out[0 * outPitch + 0 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor0 & (0x3 << 0)) >> 0];
|
||||||
|
out[0 * outPitch + 0 * outPixelSize + outOffsetA] = ((dataAlpha0 & 0xF) >> 0) * std::numeric_limits<uint8_t>::max() / 0xF;
|
||||||
|
|
||||||
|
out[0 * outPitch + 1 * outPixelSize + outOffsetR] = colorTableRed[(dataColor0 & (0x3 << 2)) >> 2];
|
||||||
|
out[0 * outPitch + 1 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor0 & (0x3 << 2)) >> 2];
|
||||||
|
out[0 * outPitch + 1 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor0 & (0x3 << 2)) >> 2];
|
||||||
|
out[0 * outPitch + 1 * outPixelSize + outOffsetA] = ((dataAlpha0 & 0xF0) >> 4) * std::numeric_limits<uint8_t>::max() / 0xF;
|
||||||
|
|
||||||
|
out[0 * outPitch + 2 * outPixelSize + outOffsetR] = colorTableRed[(dataColor0 & (0x3 << 4)) >> 4];
|
||||||
|
out[0 * outPitch + 2 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor0 & (0x3 << 4)) >> 4];
|
||||||
|
out[0 * outPitch + 2 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor0 & (0x3 << 4)) >> 4];
|
||||||
|
out[0 * outPitch + 2 * outPixelSize + outOffsetA] = ((dataAlpha0 & 0xF00) >> 8) * std::numeric_limits<uint8_t>::max() / 0xF;
|
||||||
|
|
||||||
|
out[0 * outPitch + 3 * outPixelSize + outOffsetR] = colorTableRed[(dataColor0 & (0x3 << 6)) >> 6];
|
||||||
|
out[0 * outPitch + 3 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor0 & (0x3 << 6)) >> 6];
|
||||||
|
out[0 * outPitch + 3 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor0 & (0x3 << 6)) >> 6];
|
||||||
|
out[0 * outPitch + 3 * outPixelSize + outOffsetA] = ((dataAlpha0 & 0xF000) >> 12) * std::numeric_limits<uint8_t>::max() / 0xF;
|
||||||
|
|
||||||
|
out[1 * outPitch + 0 * outPixelSize + outOffsetR] = colorTableRed[(dataColor1 & (0x3 << 0)) >> 0];
|
||||||
|
out[1 * outPitch + 0 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor1 & (0x3 << 0)) >> 0];
|
||||||
|
out[1 * outPitch + 0 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor1 & (0x3 << 0)) >> 0];
|
||||||
|
out[1 * outPitch + 0 * outPixelSize + outOffsetA] = ((dataAlpha1 & 0xF) >> 0) * std::numeric_limits<uint8_t>::max() / 0xF;
|
||||||
|
|
||||||
|
out[1 * outPitch + 1 * outPixelSize + outOffsetR] = colorTableRed[(dataColor1 & (0x3 << 2)) >> 2];
|
||||||
|
out[1 * outPitch + 1 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor1 & (0x3 << 2)) >> 2];
|
||||||
|
out[1 * outPitch + 1 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor1 & (0x3 << 2)) >> 2];
|
||||||
|
out[1 * outPitch + 1 * outPixelSize + outOffsetA] = ((dataAlpha1 & 0xF0) >> 4) * std::numeric_limits<uint8_t>::max() / 0xF;
|
||||||
|
|
||||||
|
out[1 * outPitch + 2 * outPixelSize + outOffsetR] = colorTableRed[(dataColor1 & (0x3 << 4)) >> 4];
|
||||||
|
out[1 * outPitch + 2 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor1 & (0x3 << 4)) >> 4];
|
||||||
|
out[1 * outPitch + 2 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor1 & (0x3 << 4)) >> 4];
|
||||||
|
out[1 * outPitch + 2 * outPixelSize + outOffsetA] = ((dataAlpha1 & 0xF00) >> 8) * std::numeric_limits<uint8_t>::max() / 0xF;
|
||||||
|
|
||||||
|
out[1 * outPitch + 3 * outPixelSize + outOffsetR] = colorTableRed[(dataColor1 & (0x3 << 6)) >> 6];
|
||||||
|
out[1 * outPitch + 3 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor1 & (0x3 << 6)) >> 6];
|
||||||
|
out[1 * outPitch + 3 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor1 & (0x3 << 6)) >> 6];
|
||||||
|
out[1 * outPitch + 3 * outPixelSize + outOffsetA] = ((dataAlpha1 & 0xF000) >> 12) * std::numeric_limits<uint8_t>::max() / 0xF;
|
||||||
|
|
||||||
|
out[2 * outPitch + 0 * outPixelSize + outOffsetR] = colorTableRed[(dataColor2 & (0x3 << 0)) >> 0];
|
||||||
|
out[2 * outPitch + 0 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor2 & (0x3 << 0)) >> 0];
|
||||||
|
out[2 * outPitch + 0 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor2 & (0x3 << 0)) >> 0];
|
||||||
|
out[2 * outPitch + 0 * outPixelSize + outOffsetA] = ((dataAlpha2 & 0xF) >> 0) * std::numeric_limits<uint8_t>::max() / 0xF;
|
||||||
|
|
||||||
|
out[2 * outPitch + 1 * outPixelSize + outOffsetR] = colorTableRed[(dataColor2 & (0x3 << 2)) >> 2];
|
||||||
|
out[2 * outPitch + 1 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor2 & (0x3 << 2)) >> 2];
|
||||||
|
out[2 * outPitch + 1 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor2 & (0x3 << 2)) >> 2];
|
||||||
|
out[2 * outPitch + 1 * outPixelSize + outOffsetA] = ((dataAlpha2 & 0xF0) >> 4) * std::numeric_limits<uint8_t>::max() / 0xF;
|
||||||
|
|
||||||
|
out[2 * outPitch + 2 * outPixelSize + outOffsetR] = colorTableRed[(dataColor2 & (0x3 << 4)) >> 4];
|
||||||
|
out[2 * outPitch + 2 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor2 & (0x3 << 4)) >> 4];
|
||||||
|
out[2 * outPitch + 2 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor2 & (0x3 << 4)) >> 4];
|
||||||
|
out[2 * outPitch + 2 * outPixelSize + outOffsetA] = ((dataAlpha2 & 0xF00) >> 8) * std::numeric_limits<uint8_t>::max() / 0xF;
|
||||||
|
|
||||||
|
out[2 * outPitch + 3 * outPixelSize + outOffsetR] = colorTableRed[(dataColor2 & (0x3 << 6)) >> 6];
|
||||||
|
out[2 * outPitch + 3 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor2 & (0x3 << 6)) >> 6];
|
||||||
|
out[2 * outPitch + 3 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor2 & (0x3 << 6)) >> 6];
|
||||||
|
out[2 * outPitch + 3 * outPixelSize + outOffsetA] = ((dataAlpha2 & 0xF000) >> 12) * std::numeric_limits<uint8_t>::max() / 0xF;
|
||||||
|
|
||||||
|
out[3 * outPitch + 0 * outPixelSize + outOffsetR] = colorTableRed[(dataColor3 & (0x3 << 0)) >> 0];
|
||||||
|
out[3 * outPitch + 0 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor3 & (0x3 << 0)) >> 0];
|
||||||
|
out[3 * outPitch + 0 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor3 & (0x3 << 0)) >> 0];
|
||||||
|
out[3 * outPitch + 0 * outPixelSize + outOffsetA] = ((dataAlpha3 & 0xF) >> 0) * std::numeric_limits<uint8_t>::max() / 0xF;
|
||||||
|
|
||||||
|
out[3 * outPitch + 1 * outPixelSize + outOffsetR] = colorTableRed[(dataColor3 & (0x3 << 2)) >> 2];
|
||||||
|
out[3 * outPitch + 1 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor3 & (0x3 << 2)) >> 2];
|
||||||
|
out[3 * outPitch + 1 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor3 & (0x3 << 2)) >> 2];
|
||||||
|
out[3 * outPitch + 1 * outPixelSize + outOffsetA] = ((dataAlpha3 & 0xF0) >> 4) * std::numeric_limits<uint8_t>::max() / 0xF;
|
||||||
|
|
||||||
|
out[3 * outPitch + 2 * outPixelSize + outOffsetR] = colorTableRed[(dataColor3 & (0x3 << 4)) >> 4];
|
||||||
|
out[3 * outPitch + 2 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor3 & (0x3 << 4)) >> 4];
|
||||||
|
out[3 * outPitch + 2 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor3 & (0x3 << 4)) >> 4];
|
||||||
|
out[3 * outPitch + 2 * outPixelSize + outOffsetA] = ((dataAlpha3 & 0xF00) >> 8) * std::numeric_limits<uint8_t>::max() / 0xF;
|
||||||
|
|
||||||
|
out[3 * outPitch + 3 * outPixelSize + outOffsetR] = colorTableRed[(dataColor3 & (0x3 << 6)) >> 6];
|
||||||
|
out[3 * outPitch + 3 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor3 & (0x3 << 6)) >> 6];
|
||||||
|
out[3 * outPitch + 3 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor3 & (0x3 << 6)) >> 6];
|
||||||
|
out[3 * outPitch + 3 * outPixelSize + outOffsetA] = ((dataAlpha3 & 0xF000) >> 12) * std::numeric_limits<uint8_t>::max() / 0xF;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecompressBlockEdge(const uint8_t* in,
|
||||||
|
uint8_t* out,
|
||||||
|
const unsigned width,
|
||||||
|
const unsigned height,
|
||||||
|
const unsigned outPitch,
|
||||||
|
const unsigned outPixelSize,
|
||||||
|
const unsigned outOffsetR,
|
||||||
|
const unsigned outOffsetG,
|
||||||
|
const unsigned outOffsetB,
|
||||||
|
const unsigned outOffsetA)
|
||||||
|
{
|
||||||
|
assert(width <= BC2_BLOCK_PIXELS);
|
||||||
|
assert(height <= BC2_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
uint8_t colorTableRed[4];
|
||||||
|
uint8_t colorTableGreen[4];
|
||||||
|
uint8_t colorTableBlue[4];
|
||||||
|
SetupColorTables(in, colorTableRed, colorTableGreen, colorTableBlue);
|
||||||
|
|
||||||
|
const uint16_t dataAlpha0 = static_cast<uint16_t>(in[0] | static_cast<uint32_t>(in[1] << 8u));
|
||||||
|
const uint16_t dataAlpha1 = static_cast<uint16_t>(in[2] | static_cast<uint32_t>(in[3] << 8u));
|
||||||
|
const uint16_t dataAlpha2 = static_cast<uint16_t>(in[4] | static_cast<uint32_t>(in[5] << 8u));
|
||||||
|
const uint16_t dataAlpha3 = static_cast<uint16_t>(in[6] | static_cast<uint32_t>(in[7] << 8u));
|
||||||
|
const uint8_t dataColor0 = in[BC2_ALPHA_CHANNEL_SIZE + 4];
|
||||||
|
const uint8_t dataColor1 = in[BC2_ALPHA_CHANNEL_SIZE + 5];
|
||||||
|
const uint8_t dataColor2 = in[BC2_ALPHA_CHANNEL_SIZE + 6];
|
||||||
|
const uint8_t dataColor3 = in[BC2_ALPHA_CHANNEL_SIZE + 7];
|
||||||
|
|
||||||
|
const uint8_t pixelsRed[]{
|
||||||
|
colorTableRed[(dataColor0 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableRed[(dataColor0 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableRed[(dataColor0 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableRed[(dataColor0 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableRed[(dataColor1 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableRed[(dataColor1 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableRed[(dataColor1 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableRed[(dataColor1 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableRed[(dataColor2 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableRed[(dataColor2 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableRed[(dataColor2 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableRed[(dataColor2 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableRed[(dataColor3 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableRed[(dataColor3 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableRed[(dataColor3 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableRed[(dataColor3 & (0x3 << 6)) >> 6],
|
||||||
|
};
|
||||||
|
static_assert(std::extent_v<decltype(pixelsRed)> == BC2_BLOCK_PIXELS * BC2_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
const uint8_t pixelsGreen[]{
|
||||||
|
colorTableGreen[(dataColor0 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableGreen[(dataColor0 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableGreen[(dataColor0 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableGreen[(dataColor0 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableGreen[(dataColor1 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableGreen[(dataColor1 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableGreen[(dataColor1 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableGreen[(dataColor1 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableGreen[(dataColor2 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableGreen[(dataColor2 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableGreen[(dataColor2 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableGreen[(dataColor2 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableGreen[(dataColor3 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableGreen[(dataColor3 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableGreen[(dataColor3 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableGreen[(dataColor3 & (0x3 << 6)) >> 6],
|
||||||
|
};
|
||||||
|
static_assert(std::extent_v<decltype(pixelsGreen)> == BC2_BLOCK_PIXELS * BC2_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
const uint8_t pixelsBlue[]{
|
||||||
|
colorTableBlue[(dataColor0 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableBlue[(dataColor0 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableBlue[(dataColor0 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableBlue[(dataColor0 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableBlue[(dataColor1 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableBlue[(dataColor1 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableBlue[(dataColor1 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableBlue[(dataColor1 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableBlue[(dataColor2 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableBlue[(dataColor2 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableBlue[(dataColor2 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableBlue[(dataColor2 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableBlue[(dataColor3 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableBlue[(dataColor3 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableBlue[(dataColor3 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableBlue[(dataColor3 & (0x3 << 6)) >> 6],
|
||||||
|
};
|
||||||
|
static_assert(std::extent_v<decltype(pixelsBlue)> == BC2_BLOCK_PIXELS * BC2_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
const uint8_t pixelsAlpha[]{
|
||||||
|
static_cast<uint8_t>(((dataAlpha0 & 0xF) >> 0) * std::numeric_limits<uint8_t>::max() / 0xF),
|
||||||
|
static_cast<uint8_t>(((dataAlpha0 & 0xF0) >> 4) * std::numeric_limits<uint8_t>::max() / 0xF),
|
||||||
|
static_cast<uint8_t>(((dataAlpha0 & 0xF00) >> 8) * std::numeric_limits<uint8_t>::max() / 0xF),
|
||||||
|
static_cast<uint8_t>(((dataAlpha0 & 0xF000) >> 12) * std::numeric_limits<uint8_t>::max() / 0xF),
|
||||||
|
static_cast<uint8_t>(((dataAlpha1 & 0xF) >> 0) * std::numeric_limits<uint8_t>::max() / 0xF),
|
||||||
|
static_cast<uint8_t>(((dataAlpha1 & 0xF0) >> 4) * std::numeric_limits<uint8_t>::max() / 0xF),
|
||||||
|
static_cast<uint8_t>(((dataAlpha1 & 0xF00) >> 8) * std::numeric_limits<uint8_t>::max() / 0xF),
|
||||||
|
static_cast<uint8_t>(((dataAlpha1 & 0xF000) >> 12) * std::numeric_limits<uint8_t>::max() / 0xF),
|
||||||
|
static_cast<uint8_t>(((dataAlpha2 & 0xF) >> 0) * std::numeric_limits<uint8_t>::max() / 0xF),
|
||||||
|
static_cast<uint8_t>(((dataAlpha2 & 0xF0) >> 4) * std::numeric_limits<uint8_t>::max() / 0xF),
|
||||||
|
static_cast<uint8_t>(((dataAlpha2 & 0xF00) >> 8) * std::numeric_limits<uint8_t>::max() / 0xF),
|
||||||
|
static_cast<uint8_t>(((dataAlpha2 & 0xF000) >> 12) * std::numeric_limits<uint8_t>::max() / 0xF),
|
||||||
|
static_cast<uint8_t>(((dataAlpha3 & 0xF) >> 0) * std::numeric_limits<uint8_t>::max() / 0xF),
|
||||||
|
static_cast<uint8_t>(((dataAlpha3 & 0xF0) >> 4) * std::numeric_limits<uint8_t>::max() / 0xF),
|
||||||
|
static_cast<uint8_t>(((dataAlpha3 & 0xF00) >> 8) * std::numeric_limits<uint8_t>::max() / 0xF),
|
||||||
|
static_cast<uint8_t>(((dataAlpha3 & 0xF000) >> 12) * std::numeric_limits<uint8_t>::max() / 0xF),
|
||||||
|
};
|
||||||
|
static_assert(std::extent_v<decltype(pixelsAlpha)> == BC2_BLOCK_PIXELS * BC2_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
for (auto curHeight = 0u; curHeight < height; curHeight++)
|
||||||
|
{
|
||||||
|
for (auto curWidth = 0u; curWidth < width; curWidth++)
|
||||||
|
{
|
||||||
|
out[curHeight * outPitch + curWidth * outPixelSize + outOffsetR] = pixelsRed[curHeight * BC2_BLOCK_PIXELS + curWidth];
|
||||||
|
out[curHeight * outPitch + curWidth * outPixelSize + outOffsetG] = pixelsGreen[curHeight * BC2_BLOCK_PIXELS + curWidth];
|
||||||
|
out[curHeight * outPitch + curWidth * outPixelSize + outOffsetB] = pixelsBlue[curHeight * BC2_BLOCK_PIXELS + curWidth];
|
||||||
|
out[curHeight * outPitch + curWidth * outPixelSize + outOffsetA] = pixelsAlpha[curHeight * BC2_BLOCK_PIXELS + curWidth];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace image
|
||||||
|
{
|
||||||
|
std::unique_ptr<Texture> DecompressorBc2::Decompress(const Texture& input, const ImageFormat* targetFormat)
|
||||||
|
{
|
||||||
|
assert(targetFormat->GetType() == ImageFormatType::UNSIGNED);
|
||||||
|
if (targetFormat->GetType() != ImageFormatType::UNSIGNED)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const auto* unsignedTargetFormat = dynamic_cast<const ImageFormatUnsigned*>(targetFormat);
|
||||||
|
|
||||||
|
const auto outPixelSize = unsignedTargetFormat->m_bits_per_pixel / 8u;
|
||||||
|
|
||||||
|
// Only support formats with byte aligned channels
|
||||||
|
const auto redByteAligned = unsignedTargetFormat->m_r_size == 8 && (unsignedTargetFormat->m_r_offset % 8) == 0;
|
||||||
|
const auto greenByteAligned = unsignedTargetFormat->m_g_size == 8 && (unsignedTargetFormat->m_g_offset % 8) == 0;
|
||||||
|
const auto blueByteAligned = unsignedTargetFormat->m_b_size == 8 && (unsignedTargetFormat->m_b_offset % 8) == 0;
|
||||||
|
const auto alphaByteAligned = unsignedTargetFormat->m_a_size == 8 && (unsignedTargetFormat->m_a_offset % 8) == 0;
|
||||||
|
assert(redByteAligned);
|
||||||
|
assert(greenByteAligned);
|
||||||
|
assert(blueByteAligned);
|
||||||
|
assert(alphaByteAligned);
|
||||||
|
if (!redByteAligned || !greenByteAligned || !blueByteAligned || !alphaByteAligned)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const auto outOffsetR = unsignedTargetFormat->m_r_offset / 8;
|
||||||
|
const auto outOffsetG = unsignedTargetFormat->m_g_offset / 8;
|
||||||
|
const auto outOffsetB = unsignedTargetFormat->m_b_offset / 8;
|
||||||
|
const auto outOffsetA = unsignedTargetFormat->m_a_offset / 8;
|
||||||
|
|
||||||
|
const auto width = input.GetWidth();
|
||||||
|
const auto height = input.GetHeight();
|
||||||
|
const auto depth = input.GetDepth();
|
||||||
|
|
||||||
|
auto result = Texture::CreateForType(input.GetTextureType(), targetFormat, width, height, depth, input.HasMipMaps());
|
||||||
|
result->Allocate();
|
||||||
|
|
||||||
|
const auto faceCount = result->GetFaceCount();
|
||||||
|
const auto mipCount = result->HasMipMaps() ? result->GetMipMapCount() : 1;
|
||||||
|
assert(mipCount == input.HasMipMaps() ? input.GetMipMapCount() : 1);
|
||||||
|
|
||||||
|
for (auto mipLevel = 0; mipLevel < mipCount; mipLevel++)
|
||||||
|
{
|
||||||
|
const auto mipWidth = std::max<unsigned>(width >> mipLevel, 1u);
|
||||||
|
const auto mipHeight = std::max<unsigned>(height >> mipLevel, 1u);
|
||||||
|
const auto mipDepth = std::max<unsigned>(depth >> mipLevel, 1u);
|
||||||
|
const unsigned mipPitch = outPixelSize * mipWidth;
|
||||||
|
assert(mipPitch == result->GetFormat()->GetPitch(mipLevel, width));
|
||||||
|
|
||||||
|
for (auto face = 0; face < faceCount; face++)
|
||||||
|
{
|
||||||
|
const auto* bufferIn = input.GetBufferForMipLevel(mipLevel, face);
|
||||||
|
auto* bufferOut = result->GetBufferForMipLevel(mipLevel, face);
|
||||||
|
|
||||||
|
for (auto curDepth = 0u; curDepth < mipDepth; curDepth++)
|
||||||
|
{
|
||||||
|
const auto fullBlocksWidth = utils::AlignToPrevious(mipWidth, BC2_BLOCK_PIXELS);
|
||||||
|
const auto fullBlocksHeight = utils::AlignToPrevious(mipHeight, BC2_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
for (auto curHeight = 0u; curHeight < fullBlocksHeight; curHeight += BC2_BLOCK_PIXELS)
|
||||||
|
{
|
||||||
|
for (auto curWidth = 0u; curWidth < fullBlocksWidth; curWidth += BC2_BLOCK_PIXELS)
|
||||||
|
{
|
||||||
|
DecompressBlock(bufferIn, bufferOut, mipPitch, outPixelSize, outOffsetR, outOffsetG, outOffsetB, outOffsetA);
|
||||||
|
bufferIn += BC2_BLOCK_SIZE;
|
||||||
|
bufferOut += outPixelSize * BC2_BLOCK_PIXELS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullBlocksWidth < mipWidth)
|
||||||
|
{
|
||||||
|
const auto edgeBlockWidth = mipWidth - fullBlocksWidth;
|
||||||
|
DecompressBlockEdge(
|
||||||
|
bufferIn, bufferOut, edgeBlockWidth, BC2_BLOCK_PIXELS, mipPitch, outPixelSize, outOffsetR, outOffsetG, outOffsetB, outOffsetA);
|
||||||
|
bufferIn += BC2_BLOCK_SIZE;
|
||||||
|
bufferOut += outPixelSize * edgeBlockWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferOut += mipPitch * (BC2_BLOCK_PIXELS - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullBlocksHeight < mipHeight)
|
||||||
|
{
|
||||||
|
const auto edgeBlockHeight = mipHeight - fullBlocksHeight;
|
||||||
|
for (auto curWidth = 0u; curWidth < fullBlocksWidth; curWidth += BC2_BLOCK_PIXELS)
|
||||||
|
{
|
||||||
|
DecompressBlockEdge(
|
||||||
|
bufferIn, bufferOut, BC2_BLOCK_PIXELS, edgeBlockHeight, mipPitch, outPixelSize, outOffsetR, outOffsetG, outOffsetB, outOffsetA);
|
||||||
|
bufferIn += BC2_BLOCK_SIZE;
|
||||||
|
bufferOut += outPixelSize * BC2_BLOCK_PIXELS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullBlocksWidth < mipWidth)
|
||||||
|
{
|
||||||
|
const auto edgeBlockWidth = mipWidth - fullBlocksWidth;
|
||||||
|
DecompressBlockEdge(
|
||||||
|
bufferIn, bufferOut, edgeBlockWidth, edgeBlockHeight, mipPitch, outPixelSize, outOffsetR, outOffsetG, outOffsetB, outOffsetA);
|
||||||
|
bufferIn += BC2_BLOCK_SIZE;
|
||||||
|
bufferOut += outPixelSize * edgeBlockWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferOut += mipPitch * (BC2_BLOCK_PIXELS - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} // namespace image
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ImageDecompressor.h"
|
||||||
|
|
||||||
|
namespace image
|
||||||
|
{
|
||||||
|
class DecompressorBc2 final : public ImageDecompressor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::unique_ptr<Texture> Decompress(const Texture& input, const ImageFormat* targetFormat) override;
|
||||||
|
};
|
||||||
|
} // namespace image
|
||||||
@@ -0,0 +1,410 @@
|
|||||||
|
#include "DecompressorBc3.h"
|
||||||
|
|
||||||
|
#include "Utils/Alignment.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr auto BC3_BLOCK_PIXELS = 4u;
|
||||||
|
constexpr unsigned BC3_BLUE_MASK = 0x1F;
|
||||||
|
constexpr unsigned BC3_BLUE_SHIFT = 0;
|
||||||
|
constexpr unsigned BC3_BLUE_MAX = 0x1F;
|
||||||
|
constexpr unsigned BC3_GREEN_MASK = 0x7E0;
|
||||||
|
constexpr unsigned BC3_GREEN_SHIFT = 5;
|
||||||
|
constexpr unsigned BC3_GREEN_MAX = 0x3F;
|
||||||
|
constexpr unsigned BC3_RED_MASK = 0xF800;
|
||||||
|
constexpr unsigned BC3_RED_SHIFT = 11;
|
||||||
|
constexpr unsigned BC3_RED_MAX = 0x1F;
|
||||||
|
constexpr auto BC3_COLOR_CHANNELS_SIZE = 8u;
|
||||||
|
constexpr auto BC3_ALPHA_CHANNEL_SIZE = 8u;
|
||||||
|
constexpr auto BC3_BLOCK_SIZE = BC3_COLOR_CHANNELS_SIZE + BC3_ALPHA_CHANNEL_SIZE;
|
||||||
|
|
||||||
|
void SetupColorTables(
|
||||||
|
const uint8_t* in, uint8_t (&colorTableRed)[4], uint8_t (&colorTableGreen)[4], uint8_t (&colorTableBlue)[4], uint8_t (&colorTableAlpha)[8])
|
||||||
|
{
|
||||||
|
#define EXTRACT_COLOR_CHANNEL_BC3(color, mask, shift, colMax) \
|
||||||
|
static_cast<uint8_t>(static_cast<uint16_t>(((color) & (mask)) >> (shift)) * std::numeric_limits<uint8_t>::max() / (colMax))
|
||||||
|
#define INTERPOLATE_BC3(colorTable, val0, val1) \
|
||||||
|
static_cast<uint8_t>( \
|
||||||
|
(static_cast<uint16_t>(val0) * static_cast<uint16_t>((colorTable)[0]) + static_cast<uint16_t>(val1) * static_cast<uint16_t>((colorTable)[1])) \
|
||||||
|
/ ((val0) + (val1)))
|
||||||
|
|
||||||
|
colorTableAlpha[0] = in[0];
|
||||||
|
colorTableAlpha[1] = in[1];
|
||||||
|
|
||||||
|
if (colorTableAlpha[0] > colorTableAlpha[1])
|
||||||
|
{
|
||||||
|
// 6 interpolated color values
|
||||||
|
colorTableAlpha[2] = INTERPOLATE_BC3(colorTableAlpha, 6, 1);
|
||||||
|
colorTableAlpha[3] = INTERPOLATE_BC3(colorTableAlpha, 5, 2);
|
||||||
|
colorTableAlpha[4] = INTERPOLATE_BC3(colorTableAlpha, 4, 3);
|
||||||
|
colorTableAlpha[5] = INTERPOLATE_BC3(colorTableAlpha, 3, 4);
|
||||||
|
colorTableAlpha[6] = INTERPOLATE_BC3(colorTableAlpha, 2, 5);
|
||||||
|
colorTableAlpha[7] = INTERPOLATE_BC3(colorTableAlpha, 1, 6);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 4 interpolated color values
|
||||||
|
colorTableAlpha[2] = INTERPOLATE_BC3(colorTableAlpha, 4, 1);
|
||||||
|
colorTableAlpha[3] = INTERPOLATE_BC3(colorTableAlpha, 3, 2);
|
||||||
|
colorTableAlpha[4] = INTERPOLATE_BC3(colorTableAlpha, 2, 3);
|
||||||
|
colorTableAlpha[5] = INTERPOLATE_BC3(colorTableAlpha, 1, 4);
|
||||||
|
colorTableAlpha[6] = std::numeric_limits<uint8_t>::min();
|
||||||
|
colorTableAlpha[7] = std::numeric_limits<uint8_t>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto color0 = static_cast<uint16_t>(in[0 + BC3_ALPHA_CHANNEL_SIZE] | (in[1 + BC3_ALPHA_CHANNEL_SIZE] << 8));
|
||||||
|
const auto color1 = static_cast<uint16_t>(in[2 + BC3_ALPHA_CHANNEL_SIZE] | (in[3 + BC3_ALPHA_CHANNEL_SIZE] << 8));
|
||||||
|
|
||||||
|
colorTableRed[0] = EXTRACT_COLOR_CHANNEL_BC3(color0, BC3_RED_MASK, BC3_RED_SHIFT, BC3_RED_MAX);
|
||||||
|
colorTableRed[1] = EXTRACT_COLOR_CHANNEL_BC3(color1, BC3_RED_MASK, BC3_RED_SHIFT, BC3_RED_MAX);
|
||||||
|
colorTableRed[2] = INTERPOLATE_BC3(colorTableRed, 2, 1);
|
||||||
|
colorTableRed[3] = INTERPOLATE_BC3(colorTableRed, 1, 2);
|
||||||
|
|
||||||
|
colorTableGreen[0] = EXTRACT_COLOR_CHANNEL_BC3(color0, BC3_GREEN_MASK, BC3_GREEN_SHIFT, BC3_GREEN_MAX);
|
||||||
|
colorTableGreen[1] = EXTRACT_COLOR_CHANNEL_BC3(color1, BC3_GREEN_MASK, BC3_GREEN_SHIFT, BC3_GREEN_MAX);
|
||||||
|
colorTableGreen[2] = INTERPOLATE_BC3(colorTableGreen, 2, 1);
|
||||||
|
colorTableGreen[3] = INTERPOLATE_BC3(colorTableGreen, 1, 2);
|
||||||
|
|
||||||
|
colorTableBlue[0] = EXTRACT_COLOR_CHANNEL_BC3(color0, BC3_BLUE_MASK, BC3_BLUE_SHIFT, BC3_BLUE_MAX);
|
||||||
|
colorTableBlue[1] = EXTRACT_COLOR_CHANNEL_BC3(color1, BC3_BLUE_MASK, BC3_BLUE_SHIFT, BC3_BLUE_MAX);
|
||||||
|
colorTableBlue[2] = INTERPOLATE_BC3(colorTableBlue, 2, 1);
|
||||||
|
colorTableBlue[3] = INTERPOLATE_BC3(colorTableBlue, 1, 2);
|
||||||
|
|
||||||
|
#undef INTERPOLATE_BC3
|
||||||
|
#undef EXTRACT_COLOR_CHANNEL_BC3
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecompressBlock(const uint8_t* in,
|
||||||
|
uint8_t* out,
|
||||||
|
const unsigned outPitch,
|
||||||
|
const unsigned outPixelSize,
|
||||||
|
const unsigned outOffsetR,
|
||||||
|
const unsigned outOffsetG,
|
||||||
|
const unsigned outOffsetB,
|
||||||
|
const unsigned outOffsetA)
|
||||||
|
{
|
||||||
|
uint8_t colorTableRed[4];
|
||||||
|
uint8_t colorTableGreen[4];
|
||||||
|
uint8_t colorTableBlue[4];
|
||||||
|
uint8_t colorTableAlpha[8];
|
||||||
|
SetupColorTables(in, colorTableRed, colorTableGreen, colorTableBlue, colorTableAlpha);
|
||||||
|
|
||||||
|
const uint32_t dataAlpha0 = in[2] | static_cast<uint32_t>(in[3] << 8u) | static_cast<uint32_t>(in[4] << 16u);
|
||||||
|
const uint32_t dataAlpha1 = in[5] | static_cast<uint32_t>(in[6] << 8u) | static_cast<uint32_t>(in[7] << 16u);
|
||||||
|
const uint8_t dataColor0 = in[BC3_ALPHA_CHANNEL_SIZE + 4];
|
||||||
|
const uint8_t dataColor1 = in[BC3_ALPHA_CHANNEL_SIZE + 5];
|
||||||
|
const uint8_t dataColor2 = in[BC3_ALPHA_CHANNEL_SIZE + 6];
|
||||||
|
const uint8_t dataColor3 = in[BC3_ALPHA_CHANNEL_SIZE + 7];
|
||||||
|
|
||||||
|
out[0 * outPitch + 0 * outPixelSize + outOffsetR] = colorTableRed[(dataColor0 & (0x3 << 0)) >> 0];
|
||||||
|
out[0 * outPitch + 0 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor0 & (0x3 << 0)) >> 0];
|
||||||
|
out[0 * outPitch + 0 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor0 & (0x3 << 0)) >> 0];
|
||||||
|
out[0 * outPitch + 0 * outPixelSize + outOffsetA] = colorTableAlpha[(dataAlpha0 & (0x7 << 0)) >> 0];
|
||||||
|
|
||||||
|
out[0 * outPitch + 1 * outPixelSize + outOffsetR] = colorTableRed[(dataColor0 & (0x3 << 2)) >> 2];
|
||||||
|
out[0 * outPitch + 1 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor0 & (0x3 << 2)) >> 2];
|
||||||
|
out[0 * outPitch + 1 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor0 & (0x3 << 2)) >> 2];
|
||||||
|
out[0 * outPitch + 1 * outPixelSize + outOffsetA] = colorTableAlpha[(dataAlpha0 & (0x7 << 3)) >> 3];
|
||||||
|
|
||||||
|
out[0 * outPitch + 2 * outPixelSize + outOffsetR] = colorTableRed[(dataColor0 & (0x3 << 4)) >> 4];
|
||||||
|
out[0 * outPitch + 2 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor0 & (0x3 << 4)) >> 4];
|
||||||
|
out[0 * outPitch + 2 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor0 & (0x3 << 4)) >> 4];
|
||||||
|
out[0 * outPitch + 2 * outPixelSize + outOffsetA] = colorTableAlpha[(dataAlpha0 & (0x7 << 6)) >> 6];
|
||||||
|
|
||||||
|
out[0 * outPitch + 3 * outPixelSize + outOffsetR] = colorTableRed[(dataColor0 & (0x3 << 6)) >> 6];
|
||||||
|
out[0 * outPitch + 3 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor0 & (0x3 << 6)) >> 6];
|
||||||
|
out[0 * outPitch + 3 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor0 & (0x3 << 6)) >> 6];
|
||||||
|
out[0 * outPitch + 3 * outPixelSize + outOffsetA] = colorTableAlpha[(dataAlpha0 & (0x7 << 9)) >> 9];
|
||||||
|
|
||||||
|
out[1 * outPitch + 0 * outPixelSize + outOffsetR] = colorTableRed[(dataColor1 & (0x3 << 0)) >> 0];
|
||||||
|
out[1 * outPitch + 0 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor1 & (0x3 << 0)) >> 0];
|
||||||
|
out[1 * outPitch + 0 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor1 & (0x3 << 0)) >> 0];
|
||||||
|
out[1 * outPitch + 0 * outPixelSize + outOffsetA] = colorTableAlpha[(dataAlpha0 & (0x7 << 12)) >> 12];
|
||||||
|
|
||||||
|
out[1 * outPitch + 1 * outPixelSize + outOffsetR] = colorTableRed[(dataColor1 & (0x3 << 2)) >> 2];
|
||||||
|
out[1 * outPitch + 1 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor1 & (0x3 << 2)) >> 2];
|
||||||
|
out[1 * outPitch + 1 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor1 & (0x3 << 2)) >> 2];
|
||||||
|
out[1 * outPitch + 1 * outPixelSize + outOffsetA] = colorTableAlpha[(dataAlpha0 & (0x7 << 15)) >> 15];
|
||||||
|
|
||||||
|
out[1 * outPitch + 2 * outPixelSize + outOffsetR] = colorTableRed[(dataColor1 & (0x3 << 4)) >> 4];
|
||||||
|
out[1 * outPitch + 2 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor1 & (0x3 << 4)) >> 4];
|
||||||
|
out[1 * outPitch + 2 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor1 & (0x3 << 4)) >> 4];
|
||||||
|
out[1 * outPitch + 2 * outPixelSize + outOffsetA] = colorTableAlpha[(dataAlpha0 & (0x7 << 18)) >> 18];
|
||||||
|
|
||||||
|
out[1 * outPitch + 3 * outPixelSize + outOffsetR] = colorTableRed[(dataColor1 & (0x3 << 6)) >> 6];
|
||||||
|
out[1 * outPitch + 3 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor1 & (0x3 << 6)) >> 6];
|
||||||
|
out[1 * outPitch + 3 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor1 & (0x3 << 6)) >> 6];
|
||||||
|
out[1 * outPitch + 3 * outPixelSize + outOffsetA] = colorTableAlpha[(dataAlpha0 & (0x7 << 21)) >> 21];
|
||||||
|
|
||||||
|
out[2 * outPitch + 0 * outPixelSize + outOffsetR] = colorTableRed[(dataColor2 & (0x3 << 0)) >> 0];
|
||||||
|
out[2 * outPitch + 0 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor2 & (0x3 << 0)) >> 0];
|
||||||
|
out[2 * outPitch + 0 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor2 & (0x3 << 0)) >> 0];
|
||||||
|
out[2 * outPitch + 0 * outPixelSize + outOffsetA] = colorTableAlpha[(dataAlpha1 & (0x7 << 0)) >> 0];
|
||||||
|
|
||||||
|
out[2 * outPitch + 1 * outPixelSize + outOffsetR] = colorTableRed[(dataColor2 & (0x3 << 2)) >> 2];
|
||||||
|
out[2 * outPitch + 1 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor2 & (0x3 << 2)) >> 2];
|
||||||
|
out[2 * outPitch + 1 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor2 & (0x3 << 2)) >> 2];
|
||||||
|
out[2 * outPitch + 1 * outPixelSize + outOffsetA] = colorTableAlpha[(dataAlpha1 & (0x7 << 3)) >> 3];
|
||||||
|
|
||||||
|
out[2 * outPitch + 2 * outPixelSize + outOffsetR] = colorTableRed[(dataColor2 & (0x3 << 4)) >> 4];
|
||||||
|
out[2 * outPitch + 2 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor2 & (0x3 << 4)) >> 4];
|
||||||
|
out[2 * outPitch + 2 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor2 & (0x3 << 4)) >> 4];
|
||||||
|
out[2 * outPitch + 2 * outPixelSize + outOffsetA] = colorTableAlpha[(dataAlpha1 & (0x7 << 6)) >> 6];
|
||||||
|
|
||||||
|
out[2 * outPitch + 3 * outPixelSize + outOffsetR] = colorTableRed[(dataColor2 & (0x3 << 6)) >> 6];
|
||||||
|
out[2 * outPitch + 3 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor2 & (0x3 << 6)) >> 6];
|
||||||
|
out[2 * outPitch + 3 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor2 & (0x3 << 6)) >> 6];
|
||||||
|
out[2 * outPitch + 3 * outPixelSize + outOffsetA] = colorTableAlpha[(dataAlpha1 & (0x7 << 9)) >> 9];
|
||||||
|
|
||||||
|
out[3 * outPitch + 0 * outPixelSize + outOffsetR] = colorTableRed[(dataColor3 & (0x3 << 0)) >> 0];
|
||||||
|
out[3 * outPitch + 0 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor3 & (0x3 << 0)) >> 0];
|
||||||
|
out[3 * outPitch + 0 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor3 & (0x3 << 0)) >> 0];
|
||||||
|
out[3 * outPitch + 0 * outPixelSize + outOffsetA] = colorTableAlpha[(dataAlpha1 & (0x7 << 12)) >> 12];
|
||||||
|
|
||||||
|
out[3 * outPitch + 1 * outPixelSize + outOffsetR] = colorTableRed[(dataColor3 & (0x3 << 2)) >> 2];
|
||||||
|
out[3 * outPitch + 1 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor3 & (0x3 << 2)) >> 2];
|
||||||
|
out[3 * outPitch + 1 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor3 & (0x3 << 2)) >> 2];
|
||||||
|
out[3 * outPitch + 1 * outPixelSize + outOffsetA] = colorTableAlpha[(dataAlpha1 & (0x7 << 15)) >> 15];
|
||||||
|
|
||||||
|
out[3 * outPitch + 2 * outPixelSize + outOffsetR] = colorTableRed[(dataColor3 & (0x3 << 4)) >> 4];
|
||||||
|
out[3 * outPitch + 2 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor3 & (0x3 << 4)) >> 4];
|
||||||
|
out[3 * outPitch + 2 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor3 & (0x3 << 4)) >> 4];
|
||||||
|
out[3 * outPitch + 2 * outPixelSize + outOffsetA] = colorTableAlpha[(dataAlpha1 & (0x7 << 18)) >> 18];
|
||||||
|
|
||||||
|
out[3 * outPitch + 3 * outPixelSize + outOffsetR] = colorTableRed[(dataColor3 & (0x3 << 6)) >> 6];
|
||||||
|
out[3 * outPitch + 3 * outPixelSize + outOffsetG] = colorTableGreen[(dataColor3 & (0x3 << 6)) >> 6];
|
||||||
|
out[3 * outPitch + 3 * outPixelSize + outOffsetB] = colorTableBlue[(dataColor3 & (0x3 << 6)) >> 6];
|
||||||
|
out[3 * outPitch + 3 * outPixelSize + outOffsetA] = colorTableAlpha[(dataAlpha1 & (0x7 << 21)) >> 21];
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecompressBlockEdge(const uint8_t* in,
|
||||||
|
uint8_t* out,
|
||||||
|
const unsigned width,
|
||||||
|
const unsigned height,
|
||||||
|
const unsigned outPitch,
|
||||||
|
const unsigned outPixelSize,
|
||||||
|
const unsigned outOffsetR,
|
||||||
|
const unsigned outOffsetG,
|
||||||
|
const unsigned outOffsetB,
|
||||||
|
const unsigned outOffsetA)
|
||||||
|
{
|
||||||
|
assert(width <= BC3_BLOCK_PIXELS);
|
||||||
|
assert(height <= BC3_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
uint8_t colorTableRed[4];
|
||||||
|
uint8_t colorTableGreen[4];
|
||||||
|
uint8_t colorTableBlue[4];
|
||||||
|
uint8_t colorTableAlpha[8];
|
||||||
|
SetupColorTables(in, colorTableRed, colorTableGreen, colorTableBlue, colorTableAlpha);
|
||||||
|
|
||||||
|
const uint32_t dataAlpha0 = in[2] | static_cast<uint32_t>(in[3] << 8u) | static_cast<uint32_t>(in[4] << 16u);
|
||||||
|
const uint32_t dataAlpha1 = in[5] | static_cast<uint32_t>(in[6] << 8u) | static_cast<uint32_t>(in[7] << 16u);
|
||||||
|
const uint8_t dataColor0 = in[BC3_ALPHA_CHANNEL_SIZE + 4];
|
||||||
|
const uint8_t dataColor1 = in[BC3_ALPHA_CHANNEL_SIZE + 5];
|
||||||
|
const uint8_t dataColor2 = in[BC3_ALPHA_CHANNEL_SIZE + 6];
|
||||||
|
const uint8_t dataColor3 = in[BC3_ALPHA_CHANNEL_SIZE + 7];
|
||||||
|
|
||||||
|
const uint8_t pixelsRed[]{
|
||||||
|
colorTableRed[(dataColor0 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableRed[(dataColor0 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableRed[(dataColor0 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableRed[(dataColor0 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableRed[(dataColor1 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableRed[(dataColor1 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableRed[(dataColor1 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableRed[(dataColor1 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableRed[(dataColor2 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableRed[(dataColor2 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableRed[(dataColor2 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableRed[(dataColor2 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableRed[(dataColor3 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableRed[(dataColor3 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableRed[(dataColor3 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableRed[(dataColor3 & (0x3 << 6)) >> 6],
|
||||||
|
};
|
||||||
|
static_assert(std::extent_v<decltype(pixelsRed)> == BC3_BLOCK_PIXELS * BC3_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
const uint8_t pixelsGreen[]{
|
||||||
|
colorTableGreen[(dataColor0 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableGreen[(dataColor0 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableGreen[(dataColor0 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableGreen[(dataColor0 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableGreen[(dataColor1 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableGreen[(dataColor1 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableGreen[(dataColor1 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableGreen[(dataColor1 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableGreen[(dataColor2 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableGreen[(dataColor2 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableGreen[(dataColor2 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableGreen[(dataColor2 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableGreen[(dataColor3 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableGreen[(dataColor3 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableGreen[(dataColor3 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableGreen[(dataColor3 & (0x3 << 6)) >> 6],
|
||||||
|
};
|
||||||
|
static_assert(std::extent_v<decltype(pixelsGreen)> == BC3_BLOCK_PIXELS * BC3_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
const uint8_t pixelsBlue[]{
|
||||||
|
colorTableBlue[(dataColor0 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableBlue[(dataColor0 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableBlue[(dataColor0 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableBlue[(dataColor0 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableBlue[(dataColor1 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableBlue[(dataColor1 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableBlue[(dataColor1 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableBlue[(dataColor1 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableBlue[(dataColor2 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableBlue[(dataColor2 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableBlue[(dataColor2 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableBlue[(dataColor2 & (0x3 << 6)) >> 6],
|
||||||
|
colorTableBlue[(dataColor3 & (0x3 << 0)) >> 0],
|
||||||
|
colorTableBlue[(dataColor3 & (0x3 << 2)) >> 2],
|
||||||
|
colorTableBlue[(dataColor3 & (0x3 << 4)) >> 4],
|
||||||
|
colorTableBlue[(dataColor3 & (0x3 << 6)) >> 6],
|
||||||
|
};
|
||||||
|
static_assert(std::extent_v<decltype(pixelsBlue)> == BC3_BLOCK_PIXELS * BC3_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
const uint8_t pixelsAlpha[]{
|
||||||
|
colorTableAlpha[(dataAlpha0 & (0x7 << 0)) >> 0],
|
||||||
|
colorTableAlpha[(dataAlpha0 & (0x7 << 3)) >> 3],
|
||||||
|
colorTableAlpha[(dataAlpha0 & (0x7 << 6)) >> 6],
|
||||||
|
colorTableAlpha[(dataAlpha0 & (0x7 << 9)) >> 9],
|
||||||
|
colorTableAlpha[(dataAlpha0 & (0x7 << 12)) >> 12],
|
||||||
|
colorTableAlpha[(dataAlpha0 & (0x7 << 15)) >> 15],
|
||||||
|
colorTableAlpha[(dataAlpha0 & (0x7 << 18)) >> 18],
|
||||||
|
colorTableAlpha[(dataAlpha0 & (0x7 << 21)) >> 21],
|
||||||
|
colorTableAlpha[(dataAlpha1 & (0x7 << 0)) >> 0],
|
||||||
|
colorTableAlpha[(dataAlpha1 & (0x7 << 3)) >> 3],
|
||||||
|
colorTableAlpha[(dataAlpha1 & (0x7 << 6)) >> 6],
|
||||||
|
colorTableAlpha[(dataAlpha1 & (0x7 << 9)) >> 9],
|
||||||
|
colorTableAlpha[(dataAlpha1 & (0x7 << 12)) >> 12],
|
||||||
|
colorTableAlpha[(dataAlpha1 & (0x7 << 15)) >> 15],
|
||||||
|
colorTableAlpha[(dataAlpha1 & (0x7 << 18)) >> 18],
|
||||||
|
colorTableAlpha[(dataAlpha1 & (0x7 << 21)) >> 21],
|
||||||
|
};
|
||||||
|
static_assert(std::extent_v<decltype(pixelsAlpha)> == BC3_BLOCK_PIXELS * BC3_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
for (auto curHeight = 0u; curHeight < height; curHeight++)
|
||||||
|
{
|
||||||
|
for (auto curWidth = 0u; curWidth < width; curWidth++)
|
||||||
|
{
|
||||||
|
out[curHeight * outPitch + curWidth * outPixelSize + outOffsetR] = pixelsRed[curHeight * BC3_BLOCK_PIXELS + curWidth];
|
||||||
|
out[curHeight * outPitch + curWidth * outPixelSize + outOffsetG] = pixelsGreen[curHeight * BC3_BLOCK_PIXELS + curWidth];
|
||||||
|
out[curHeight * outPitch + curWidth * outPixelSize + outOffsetB] = pixelsBlue[curHeight * BC3_BLOCK_PIXELS + curWidth];
|
||||||
|
out[curHeight * outPitch + curWidth * outPixelSize + outOffsetA] = pixelsAlpha[curHeight * BC3_BLOCK_PIXELS + curWidth];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace image
|
||||||
|
{
|
||||||
|
std::unique_ptr<Texture> DecompressorBc3::Decompress(const Texture& input, const ImageFormat* targetFormat)
|
||||||
|
{
|
||||||
|
assert(targetFormat->GetType() == ImageFormatType::UNSIGNED);
|
||||||
|
if (targetFormat->GetType() != ImageFormatType::UNSIGNED)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const auto* unsignedTargetFormat = dynamic_cast<const ImageFormatUnsigned*>(targetFormat);
|
||||||
|
|
||||||
|
const auto outPixelSize = unsignedTargetFormat->m_bits_per_pixel / 8u;
|
||||||
|
|
||||||
|
// Only support formats with byte aligned channels
|
||||||
|
const auto redByteAligned = unsignedTargetFormat->m_r_size == 8 && (unsignedTargetFormat->m_r_offset % 8) == 0;
|
||||||
|
const auto greenByteAligned = unsignedTargetFormat->m_g_size == 8 && (unsignedTargetFormat->m_g_offset % 8) == 0;
|
||||||
|
const auto blueByteAligned = unsignedTargetFormat->m_b_size == 8 && (unsignedTargetFormat->m_b_offset % 8) == 0;
|
||||||
|
const auto alphaByteAligned = unsignedTargetFormat->m_a_size == 8 && (unsignedTargetFormat->m_a_offset % 8) == 0;
|
||||||
|
assert(redByteAligned);
|
||||||
|
assert(greenByteAligned);
|
||||||
|
assert(blueByteAligned);
|
||||||
|
assert(alphaByteAligned);
|
||||||
|
if (!redByteAligned || !greenByteAligned || !blueByteAligned || !alphaByteAligned)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const auto outOffsetR = unsignedTargetFormat->m_r_offset / 8;
|
||||||
|
const auto outOffsetG = unsignedTargetFormat->m_g_offset / 8;
|
||||||
|
const auto outOffsetB = unsignedTargetFormat->m_b_offset / 8;
|
||||||
|
const auto outOffsetA = unsignedTargetFormat->m_a_offset / 8;
|
||||||
|
|
||||||
|
const auto width = input.GetWidth();
|
||||||
|
const auto height = input.GetHeight();
|
||||||
|
const auto depth = input.GetDepth();
|
||||||
|
|
||||||
|
auto result = Texture::CreateForType(input.GetTextureType(), targetFormat, width, height, depth, input.HasMipMaps());
|
||||||
|
result->Allocate();
|
||||||
|
|
||||||
|
const auto faceCount = result->GetFaceCount();
|
||||||
|
const auto mipCount = result->HasMipMaps() ? result->GetMipMapCount() : 1;
|
||||||
|
assert(mipCount == input.HasMipMaps() ? input.GetMipMapCount() : 1);
|
||||||
|
|
||||||
|
for (auto mipLevel = 0; mipLevel < mipCount; mipLevel++)
|
||||||
|
{
|
||||||
|
const auto mipWidth = std::max<unsigned>(width >> mipLevel, 1u);
|
||||||
|
const auto mipHeight = std::max<unsigned>(height >> mipLevel, 1u);
|
||||||
|
const auto mipDepth = std::max<unsigned>(depth >> mipLevel, 1u);
|
||||||
|
const unsigned mipPitch = outPixelSize * mipWidth;
|
||||||
|
assert(mipPitch == result->GetFormat()->GetPitch(mipLevel, width));
|
||||||
|
|
||||||
|
for (auto face = 0; face < faceCount; face++)
|
||||||
|
{
|
||||||
|
const auto* bufferIn = input.GetBufferForMipLevel(mipLevel, face);
|
||||||
|
auto* bufferOut = result->GetBufferForMipLevel(mipLevel, face);
|
||||||
|
|
||||||
|
for (auto curDepth = 0u; curDepth < mipDepth; curDepth++)
|
||||||
|
{
|
||||||
|
const auto fullBlocksWidth = utils::AlignToPrevious(mipWidth, BC3_BLOCK_PIXELS);
|
||||||
|
const auto fullBlocksHeight = utils::AlignToPrevious(mipHeight, BC3_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
for (auto curHeight = 0u; curHeight < fullBlocksHeight; curHeight += BC3_BLOCK_PIXELS)
|
||||||
|
{
|
||||||
|
for (auto curWidth = 0u; curWidth < fullBlocksWidth; curWidth += BC3_BLOCK_PIXELS)
|
||||||
|
{
|
||||||
|
DecompressBlock(bufferIn, bufferOut, mipPitch, outPixelSize, outOffsetR, outOffsetG, outOffsetB, outOffsetA);
|
||||||
|
bufferIn += BC3_BLOCK_SIZE;
|
||||||
|
bufferOut += outPixelSize * BC3_BLOCK_PIXELS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullBlocksWidth < mipWidth)
|
||||||
|
{
|
||||||
|
const auto edgeBlockWidth = mipWidth - fullBlocksWidth;
|
||||||
|
DecompressBlockEdge(
|
||||||
|
bufferIn, bufferOut, edgeBlockWidth, BC3_BLOCK_PIXELS, mipPitch, outPixelSize, outOffsetR, outOffsetG, outOffsetB, outOffsetA);
|
||||||
|
bufferIn += BC3_BLOCK_SIZE;
|
||||||
|
bufferOut += outPixelSize * edgeBlockWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferOut += mipPitch * (BC3_BLOCK_PIXELS - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullBlocksHeight < mipHeight)
|
||||||
|
{
|
||||||
|
const auto edgeBlockHeight = mipHeight - fullBlocksHeight;
|
||||||
|
for (auto curWidth = 0u; curWidth < fullBlocksWidth; curWidth += BC3_BLOCK_PIXELS)
|
||||||
|
{
|
||||||
|
DecompressBlockEdge(
|
||||||
|
bufferIn, bufferOut, BC3_BLOCK_PIXELS, edgeBlockHeight, mipPitch, outPixelSize, outOffsetR, outOffsetG, outOffsetB, outOffsetA);
|
||||||
|
bufferIn += BC3_BLOCK_SIZE;
|
||||||
|
bufferOut += outPixelSize * BC3_BLOCK_PIXELS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullBlocksWidth < mipWidth)
|
||||||
|
{
|
||||||
|
const auto edgeBlockWidth = mipWidth - fullBlocksWidth;
|
||||||
|
DecompressBlockEdge(
|
||||||
|
bufferIn, bufferOut, edgeBlockWidth, edgeBlockHeight, mipPitch, outPixelSize, outOffsetR, outOffsetG, outOffsetB, outOffsetA);
|
||||||
|
bufferIn += BC3_BLOCK_SIZE;
|
||||||
|
bufferOut += outPixelSize * edgeBlockWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferOut += mipPitch * (BC3_BLOCK_PIXELS - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} // namespace image
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ImageDecompressor.h"
|
||||||
|
|
||||||
|
namespace image
|
||||||
|
{
|
||||||
|
class DecompressorBc3 final : public ImageDecompressor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::unique_ptr<Texture> Decompress(const Texture& input, const ImageFormat* targetFormat) override;
|
||||||
|
};
|
||||||
|
} // namespace image
|
||||||
@@ -0,0 +1,251 @@
|
|||||||
|
#include "DecompressorBc4.h"
|
||||||
|
|
||||||
|
#include "Utils/Alignment.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr auto BC4_BLOCK_PIXELS = 4u;
|
||||||
|
constexpr auto BC4_CHANNEL_SIZE = 8u;
|
||||||
|
constexpr auto BC4_BLOCK_SIZE = BC4_CHANNEL_SIZE;
|
||||||
|
|
||||||
|
void SetupColorTable(const uint8_t* in, uint8_t (&colorTableRed)[8])
|
||||||
|
{
|
||||||
|
#define INTERPOLATE_BC4(colorTable, val0, val1) \
|
||||||
|
static_cast<uint8_t>( \
|
||||||
|
(static_cast<uint16_t>(val0) * static_cast<uint16_t>((colorTable)[0]) + static_cast<uint16_t>(val1) * static_cast<uint16_t>((colorTable)[1])) \
|
||||||
|
/ ((val0) + (val1)))
|
||||||
|
|
||||||
|
colorTableRed[0] = in[0];
|
||||||
|
colorTableRed[1] = in[1];
|
||||||
|
|
||||||
|
if (colorTableRed[0] > colorTableRed[1])
|
||||||
|
{
|
||||||
|
// 6 interpolated color values
|
||||||
|
colorTableRed[2] = INTERPOLATE_BC4(colorTableRed, 6, 1);
|
||||||
|
colorTableRed[3] = INTERPOLATE_BC4(colorTableRed, 5, 2);
|
||||||
|
colorTableRed[4] = INTERPOLATE_BC4(colorTableRed, 4, 3);
|
||||||
|
colorTableRed[5] = INTERPOLATE_BC4(colorTableRed, 3, 4);
|
||||||
|
colorTableRed[6] = INTERPOLATE_BC4(colorTableRed, 2, 5);
|
||||||
|
colorTableRed[7] = INTERPOLATE_BC4(colorTableRed, 1, 6);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 4 interpolated color values
|
||||||
|
colorTableRed[2] = INTERPOLATE_BC4(colorTableRed, 4, 1);
|
||||||
|
colorTableRed[3] = INTERPOLATE_BC4(colorTableRed, 3, 2);
|
||||||
|
colorTableRed[4] = INTERPOLATE_BC4(colorTableRed, 2, 3);
|
||||||
|
colorTableRed[5] = INTERPOLATE_BC4(colorTableRed, 1, 4);
|
||||||
|
colorTableRed[6] = std::numeric_limits<uint8_t>::min();
|
||||||
|
colorTableRed[7] = std::numeric_limits<uint8_t>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef INTERPOLATE_BC4
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecompressBlock(const uint8_t* in,
|
||||||
|
uint8_t* out,
|
||||||
|
const unsigned outPitch,
|
||||||
|
const unsigned outPixelSize,
|
||||||
|
const unsigned outOffsetR,
|
||||||
|
const unsigned outOffsetA,
|
||||||
|
const bool hasAlpha)
|
||||||
|
{
|
||||||
|
uint8_t colorTableRed[8];
|
||||||
|
SetupColorTable(in, colorTableRed);
|
||||||
|
const uint32_t dataRed0 = in[2] | static_cast<uint32_t>(in[3] << 8u) | static_cast<uint32_t>(in[4] << 16u);
|
||||||
|
const uint32_t dataRed1 = in[5] | static_cast<uint32_t>(in[6] << 8u) | static_cast<uint32_t>(in[7] << 16u);
|
||||||
|
|
||||||
|
out[0 * outPitch + 0 * outPixelSize + outOffsetR] = colorTableRed[(dataRed0 & (0x7 << 0)) >> 0];
|
||||||
|
out[0 * outPitch + 1 * outPixelSize + outOffsetR] = colorTableRed[(dataRed0 & (0x7 << 3)) >> 3];
|
||||||
|
out[0 * outPitch + 2 * outPixelSize + outOffsetR] = colorTableRed[(dataRed0 & (0x7 << 6)) >> 6];
|
||||||
|
out[0 * outPitch + 3 * outPixelSize + outOffsetR] = colorTableRed[(dataRed0 & (0x7 << 9)) >> 9];
|
||||||
|
out[1 * outPitch + 0 * outPixelSize + outOffsetR] = colorTableRed[(dataRed0 & (0x7 << 12)) >> 12];
|
||||||
|
out[1 * outPitch + 1 * outPixelSize + outOffsetR] = colorTableRed[(dataRed0 & (0x7 << 15)) >> 15];
|
||||||
|
out[1 * outPitch + 2 * outPixelSize + outOffsetR] = colorTableRed[(dataRed0 & (0x7 << 18)) >> 18];
|
||||||
|
out[1 * outPitch + 3 * outPixelSize + outOffsetR] = colorTableRed[(dataRed0 & (0x7 << 21)) >> 21];
|
||||||
|
out[2 * outPitch + 0 * outPixelSize + outOffsetR] = colorTableRed[(dataRed1 & (0x7 << 0)) >> 0];
|
||||||
|
out[2 * outPitch + 1 * outPixelSize + outOffsetR] = colorTableRed[(dataRed1 & (0x7 << 3)) >> 3];
|
||||||
|
out[2 * outPitch + 2 * outPixelSize + outOffsetR] = colorTableRed[(dataRed1 & (0x7 << 6)) >> 6];
|
||||||
|
out[2 * outPitch + 3 * outPixelSize + outOffsetR] = colorTableRed[(dataRed1 & (0x7 << 9)) >> 9];
|
||||||
|
out[3 * outPitch + 0 * outPixelSize + outOffsetR] = colorTableRed[(dataRed1 & (0x7 << 12)) >> 12];
|
||||||
|
out[3 * outPitch + 1 * outPixelSize + outOffsetR] = colorTableRed[(dataRed1 & (0x7 << 15)) >> 15];
|
||||||
|
out[3 * outPitch + 2 * outPixelSize + outOffsetR] = colorTableRed[(dataRed1 & (0x7 << 18)) >> 18];
|
||||||
|
out[3 * outPitch + 3 * outPixelSize + outOffsetR] = colorTableRed[(dataRed1 & (0x7 << 21)) >> 21];
|
||||||
|
|
||||||
|
if (hasAlpha)
|
||||||
|
{
|
||||||
|
for (auto curHeight = 0u; curHeight < BC4_BLOCK_PIXELS; curHeight++)
|
||||||
|
{
|
||||||
|
for (auto curWidth = 0u; curWidth < BC4_BLOCK_PIXELS; curWidth++)
|
||||||
|
{
|
||||||
|
out[curHeight * outPitch + curWidth * outPixelSize + outOffsetA] = std::numeric_limits<uint8_t>::max();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecompressBlockEdge(const uint8_t* in,
|
||||||
|
uint8_t* out,
|
||||||
|
const unsigned width,
|
||||||
|
const unsigned height,
|
||||||
|
const unsigned outPitch,
|
||||||
|
const unsigned outPixelSize,
|
||||||
|
const unsigned outOffsetR,
|
||||||
|
const unsigned outOffsetA,
|
||||||
|
const bool hasAlpha)
|
||||||
|
{
|
||||||
|
assert(width <= BC4_BLOCK_PIXELS);
|
||||||
|
assert(height <= BC4_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
uint8_t colorTableRed[8];
|
||||||
|
SetupColorTable(in, colorTableRed);
|
||||||
|
|
||||||
|
const uint32_t dataRed0 = in[2] | static_cast<uint32_t>(in[3] << 8u) | static_cast<uint32_t>(in[4] << 16u);
|
||||||
|
const uint32_t dataRed1 = in[5] | static_cast<uint32_t>(in[6] << 8u) | static_cast<uint32_t>(in[7] << 16u);
|
||||||
|
|
||||||
|
const uint8_t pixelsRed[]{
|
||||||
|
colorTableRed[(dataRed0 & (0x7 << 0)) >> 0],
|
||||||
|
colorTableRed[(dataRed0 & (0x7 << 3)) >> 3],
|
||||||
|
colorTableRed[(dataRed0 & (0x7 << 6)) >> 6],
|
||||||
|
colorTableRed[(dataRed0 & (0x7 << 9)) >> 9],
|
||||||
|
colorTableRed[(dataRed0 & (0x7 << 12)) >> 12],
|
||||||
|
colorTableRed[(dataRed0 & (0x7 << 15)) >> 15],
|
||||||
|
colorTableRed[(dataRed0 & (0x7 << 18)) >> 18],
|
||||||
|
colorTableRed[(dataRed0 & (0x7 << 21)) >> 21],
|
||||||
|
colorTableRed[(dataRed1 & (0x7 << 0)) >> 0],
|
||||||
|
colorTableRed[(dataRed1 & (0x7 << 3)) >> 3],
|
||||||
|
colorTableRed[(dataRed1 & (0x7 << 6)) >> 6],
|
||||||
|
colorTableRed[(dataRed1 & (0x7 << 9)) >> 9],
|
||||||
|
colorTableRed[(dataRed1 & (0x7 << 12)) >> 12],
|
||||||
|
colorTableRed[(dataRed1 & (0x7 << 15)) >> 15],
|
||||||
|
colorTableRed[(dataRed1 & (0x7 << 18)) >> 18],
|
||||||
|
colorTableRed[(dataRed1 & (0x7 << 21)) >> 21],
|
||||||
|
};
|
||||||
|
static_assert(std::extent_v<decltype(pixelsRed)> == BC4_BLOCK_PIXELS * BC4_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
for (auto curHeight = 0u; curHeight < height; curHeight++)
|
||||||
|
{
|
||||||
|
for (auto curWidth = 0u; curWidth < width; curWidth++)
|
||||||
|
{
|
||||||
|
out[curHeight * outPitch + curWidth * outPixelSize + outOffsetR] = pixelsRed[curHeight * BC4_BLOCK_PIXELS + curWidth];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasAlpha)
|
||||||
|
{
|
||||||
|
for (auto curHeight = 0u; curHeight < height; curHeight++)
|
||||||
|
{
|
||||||
|
for (auto curWidth = 0u; curWidth < width; curWidth++)
|
||||||
|
{
|
||||||
|
out[curHeight * outPitch + curWidth * outPixelSize + outOffsetA] = std::numeric_limits<uint8_t>::max();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace image
|
||||||
|
{
|
||||||
|
std::unique_ptr<Texture> DecompressorBc4::Decompress(const Texture& input, const ImageFormat* targetFormat)
|
||||||
|
{
|
||||||
|
assert(targetFormat->GetType() == ImageFormatType::UNSIGNED);
|
||||||
|
if (targetFormat->GetType() != ImageFormatType::UNSIGNED)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const auto* unsignedTargetFormat = dynamic_cast<const ImageFormatUnsigned*>(targetFormat);
|
||||||
|
|
||||||
|
const auto outPixelSize = unsignedTargetFormat->m_bits_per_pixel / 8u;
|
||||||
|
|
||||||
|
// Only support formats with byte aligned channels
|
||||||
|
const auto redByteAligned = unsignedTargetFormat->m_r_size == 8 && (unsignedTargetFormat->m_r_offset % 8) == 0;
|
||||||
|
const auto hasAlpha = unsignedTargetFormat->m_a_size > 0;
|
||||||
|
const auto alphaByteAligned = unsignedTargetFormat->m_a_size == 8 && (unsignedTargetFormat->m_a_offset % 8) == 0;
|
||||||
|
assert(redByteAligned);
|
||||||
|
assert(!hasAlpha || alphaByteAligned);
|
||||||
|
if (!redByteAligned || (hasAlpha && !alphaByteAligned))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const auto outOffsetR = unsignedTargetFormat->m_r_offset / 8;
|
||||||
|
const auto outOffsetA = unsignedTargetFormat->m_a_offset / 8;
|
||||||
|
|
||||||
|
const auto width = input.GetWidth();
|
||||||
|
const auto height = input.GetHeight();
|
||||||
|
const auto depth = input.GetDepth();
|
||||||
|
|
||||||
|
auto result = Texture::CreateForType(input.GetTextureType(), targetFormat, width, height, depth, input.HasMipMaps());
|
||||||
|
result->Allocate();
|
||||||
|
|
||||||
|
const auto faceCount = result->GetFaceCount();
|
||||||
|
const auto mipCount = result->HasMipMaps() ? result->GetMipMapCount() : 1;
|
||||||
|
assert(mipCount == input.HasMipMaps() ? input.GetMipMapCount() : 1);
|
||||||
|
|
||||||
|
for (auto mipLevel = 0; mipLevel < mipCount; mipLevel++)
|
||||||
|
{
|
||||||
|
const auto mipWidth = std::max<unsigned>(width >> mipLevel, 1u);
|
||||||
|
const auto mipHeight = std::max<unsigned>(height >> mipLevel, 1u);
|
||||||
|
const auto mipDepth = std::max<unsigned>(depth >> mipLevel, 1u);
|
||||||
|
const unsigned mipPitch = outPixelSize * mipWidth;
|
||||||
|
assert(mipPitch == result->GetFormat()->GetPitch(mipLevel, width));
|
||||||
|
|
||||||
|
for (auto face = 0; face < faceCount; face++)
|
||||||
|
{
|
||||||
|
const auto* bufferIn = input.GetBufferForMipLevel(mipLevel, face);
|
||||||
|
auto* bufferOut = result->GetBufferForMipLevel(mipLevel, face);
|
||||||
|
|
||||||
|
for (auto curDepth = 0u; curDepth < mipDepth; curDepth++)
|
||||||
|
{
|
||||||
|
const auto fullBlocksWidth = utils::AlignToPrevious(mipWidth, BC4_BLOCK_PIXELS);
|
||||||
|
const auto fullBlocksHeight = utils::AlignToPrevious(mipHeight, BC4_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
for (auto curHeight = 0u; curHeight < fullBlocksHeight; curHeight += BC4_BLOCK_PIXELS)
|
||||||
|
{
|
||||||
|
for (auto curWidth = 0u; curWidth < fullBlocksWidth; curWidth += BC4_BLOCK_PIXELS)
|
||||||
|
{
|
||||||
|
DecompressBlock(bufferIn, bufferOut, mipPitch, outPixelSize, outOffsetR, outOffsetA, hasAlpha);
|
||||||
|
bufferIn += BC4_BLOCK_SIZE;
|
||||||
|
bufferOut += outPixelSize * BC4_BLOCK_PIXELS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullBlocksWidth < mipWidth)
|
||||||
|
{
|
||||||
|
const auto edgeBlockWidth = mipWidth - fullBlocksWidth;
|
||||||
|
DecompressBlockEdge(
|
||||||
|
bufferIn, bufferOut, edgeBlockWidth, BC4_BLOCK_PIXELS, mipPitch, outPixelSize, outOffsetR, outOffsetA, hasAlpha);
|
||||||
|
bufferIn += BC4_BLOCK_SIZE;
|
||||||
|
bufferOut += outPixelSize * edgeBlockWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferOut += mipPitch * (BC4_BLOCK_PIXELS - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullBlocksHeight < mipHeight)
|
||||||
|
{
|
||||||
|
const auto edgeBlockHeight = mipHeight - fullBlocksHeight;
|
||||||
|
for (auto curWidth = 0u; curWidth < fullBlocksWidth; curWidth += BC4_BLOCK_PIXELS)
|
||||||
|
{
|
||||||
|
DecompressBlockEdge(
|
||||||
|
bufferIn, bufferOut, BC4_BLOCK_PIXELS, edgeBlockHeight, mipPitch, outPixelSize, outOffsetR, outOffsetA, hasAlpha);
|
||||||
|
bufferIn += BC4_BLOCK_SIZE;
|
||||||
|
bufferOut += outPixelSize * BC4_BLOCK_PIXELS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullBlocksWidth < mipWidth)
|
||||||
|
{
|
||||||
|
const auto edgeBlockWidth = mipWidth - fullBlocksWidth;
|
||||||
|
DecompressBlockEdge(bufferIn, bufferOut, edgeBlockWidth, edgeBlockHeight, mipPitch, outPixelSize, outOffsetR, outOffsetA, hasAlpha);
|
||||||
|
bufferIn += BC4_BLOCK_SIZE;
|
||||||
|
bufferOut += outPixelSize * edgeBlockWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferOut += mipPitch * (BC4_BLOCK_PIXELS - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} // namespace image
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ImageDecompressor.h"
|
||||||
|
|
||||||
|
namespace image
|
||||||
|
{
|
||||||
|
class DecompressorBc4 final : public ImageDecompressor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::unique_ptr<Texture> Decompress(const Texture& input, const ImageFormat* targetFormat) override;
|
||||||
|
};
|
||||||
|
} // namespace image
|
||||||
@@ -0,0 +1,343 @@
|
|||||||
|
#include "DecompressorBc5.h"
|
||||||
|
|
||||||
|
#include "Utils/Alignment.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr auto BC5_BLOCK_PIXELS = 4u;
|
||||||
|
constexpr auto BC5_CHANNEL_SIZE = 8u;
|
||||||
|
constexpr auto BC5_BLOCK_SIZE = BC5_CHANNEL_SIZE * 2u;
|
||||||
|
|
||||||
|
void SetupColorTables(const uint8_t* in, uint8_t (&colorTableRed)[8], uint8_t (&colorTableGreen)[8])
|
||||||
|
{
|
||||||
|
#define INTERPOLATE_BC5(colorTable, val0, val1) \
|
||||||
|
static_cast<uint8_t>( \
|
||||||
|
(static_cast<uint16_t>(val0) * static_cast<uint16_t>((colorTable)[0]) + static_cast<uint16_t>(val1) * static_cast<uint16_t>((colorTable)[1])) \
|
||||||
|
/ ((val0) + (val1)))
|
||||||
|
|
||||||
|
colorTableRed[0] = in[0];
|
||||||
|
colorTableRed[1] = in[1];
|
||||||
|
|
||||||
|
if (colorTableRed[0] > colorTableRed[1])
|
||||||
|
{
|
||||||
|
// 6 interpolated color values
|
||||||
|
colorTableRed[2] = INTERPOLATE_BC5(colorTableRed, 6, 1);
|
||||||
|
colorTableRed[3] = INTERPOLATE_BC5(colorTableRed, 5, 2);
|
||||||
|
colorTableRed[4] = INTERPOLATE_BC5(colorTableRed, 4, 3);
|
||||||
|
colorTableRed[5] = INTERPOLATE_BC5(colorTableRed, 3, 4);
|
||||||
|
colorTableRed[6] = INTERPOLATE_BC5(colorTableRed, 2, 5);
|
||||||
|
colorTableRed[7] = INTERPOLATE_BC5(colorTableRed, 1, 6);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 4 interpolated color values
|
||||||
|
colorTableRed[2] = INTERPOLATE_BC5(colorTableRed, 4, 1);
|
||||||
|
colorTableRed[3] = INTERPOLATE_BC5(colorTableRed, 3, 2);
|
||||||
|
colorTableRed[4] = INTERPOLATE_BC5(colorTableRed, 2, 3);
|
||||||
|
colorTableRed[5] = INTERPOLATE_BC5(colorTableRed, 1, 4);
|
||||||
|
colorTableRed[6] = std::numeric_limits<uint8_t>::min();
|
||||||
|
colorTableRed[7] = std::numeric_limits<uint8_t>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
colorTableGreen[0] = in[BC5_CHANNEL_SIZE + 0];
|
||||||
|
colorTableGreen[1] = in[BC5_CHANNEL_SIZE + 1];
|
||||||
|
|
||||||
|
if (colorTableGreen[0] > colorTableGreen[1])
|
||||||
|
{
|
||||||
|
// 6 interpolated color values
|
||||||
|
colorTableGreen[2] = INTERPOLATE_BC5(colorTableGreen, 6, 1);
|
||||||
|
colorTableGreen[3] = INTERPOLATE_BC5(colorTableGreen, 5, 2);
|
||||||
|
colorTableGreen[4] = INTERPOLATE_BC5(colorTableGreen, 4, 3);
|
||||||
|
colorTableGreen[5] = INTERPOLATE_BC5(colorTableGreen, 3, 4);
|
||||||
|
colorTableGreen[6] = INTERPOLATE_BC5(colorTableGreen, 2, 5);
|
||||||
|
colorTableGreen[7] = INTERPOLATE_BC5(colorTableGreen, 1, 6);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 4 interpolated color values
|
||||||
|
colorTableGreen[2] = INTERPOLATE_BC5(colorTableGreen, 4, 1);
|
||||||
|
colorTableGreen[3] = INTERPOLATE_BC5(colorTableGreen, 3, 2);
|
||||||
|
colorTableGreen[4] = INTERPOLATE_BC5(colorTableGreen, 2, 3);
|
||||||
|
colorTableGreen[5] = INTERPOLATE_BC5(colorTableGreen, 1, 4);
|
||||||
|
colorTableGreen[6] = std::numeric_limits<uint8_t>::min();
|
||||||
|
colorTableGreen[7] = std::numeric_limits<uint8_t>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef INTERPOLATE_BC5
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecompressBlock(const uint8_t* in,
|
||||||
|
uint8_t* out,
|
||||||
|
const unsigned outPitch,
|
||||||
|
const unsigned outPixelSize,
|
||||||
|
const unsigned outOffsetR,
|
||||||
|
const unsigned outOffsetG,
|
||||||
|
const unsigned outOffsetA,
|
||||||
|
const bool hasAlpha)
|
||||||
|
{
|
||||||
|
uint8_t colorTableRed[8];
|
||||||
|
uint8_t colorTableGreen[8];
|
||||||
|
SetupColorTables(in, colorTableRed, colorTableGreen);
|
||||||
|
const uint32_t dataRed0 = in[2] | static_cast<uint32_t>(in[3] << 8u) | static_cast<uint32_t>(in[4] << 16u);
|
||||||
|
const uint32_t dataRed1 = in[5] | static_cast<uint32_t>(in[6] << 8u) | static_cast<uint32_t>(in[7] << 16u);
|
||||||
|
const uint32_t dataGreen0 =
|
||||||
|
in[BC5_CHANNEL_SIZE + 2] | static_cast<uint32_t>(in[BC5_CHANNEL_SIZE + 3] << 8) | static_cast<uint32_t>(in[BC5_CHANNEL_SIZE + 4] << 16);
|
||||||
|
const uint32_t dataGreen1 =
|
||||||
|
in[BC5_CHANNEL_SIZE + 5] | static_cast<uint32_t>(in[BC5_CHANNEL_SIZE + 6] << 8) | static_cast<uint32_t>(in[BC5_CHANNEL_SIZE + 7] << 16);
|
||||||
|
|
||||||
|
out[0 * outPitch + 0 * outPixelSize + outOffsetR] = colorTableRed[(dataRed0 & (0x7 << 0)) >> 0];
|
||||||
|
out[0 * outPitch + 0 * outPixelSize + outOffsetG] = colorTableGreen[(dataGreen0 & (0x7 << 0)) >> 0];
|
||||||
|
|
||||||
|
out[0 * outPitch + 1 * outPixelSize + outOffsetR] = colorTableRed[(dataRed0 & (0x7 << 3)) >> 3];
|
||||||
|
out[0 * outPitch + 1 * outPixelSize + outOffsetG] = colorTableGreen[(dataGreen0 & (0x7 << 3)) >> 3];
|
||||||
|
|
||||||
|
out[0 * outPitch + 2 * outPixelSize + outOffsetR] = colorTableRed[(dataRed0 & (0x7 << 6)) >> 6];
|
||||||
|
out[0 * outPitch + 2 * outPixelSize + outOffsetG] = colorTableGreen[(dataGreen0 & (0x7 << 6)) >> 6];
|
||||||
|
|
||||||
|
out[0 * outPitch + 3 * outPixelSize + outOffsetR] = colorTableRed[(dataRed0 & (0x7 << 9)) >> 9];
|
||||||
|
out[0 * outPitch + 3 * outPixelSize + outOffsetG] = colorTableGreen[(dataGreen0 & (0x7 << 9)) >> 9];
|
||||||
|
|
||||||
|
out[1 * outPitch + 0 * outPixelSize + outOffsetR] = colorTableRed[(dataRed0 & (0x7 << 12)) >> 12];
|
||||||
|
out[1 * outPitch + 0 * outPixelSize + outOffsetG] = colorTableGreen[(dataGreen0 & (0x7 << 12)) >> 12];
|
||||||
|
|
||||||
|
out[1 * outPitch + 1 * outPixelSize + outOffsetR] = colorTableRed[(dataRed0 & (0x7 << 15)) >> 15];
|
||||||
|
out[1 * outPitch + 1 * outPixelSize + outOffsetG] = colorTableGreen[(dataGreen0 & (0x7 << 15)) >> 15];
|
||||||
|
|
||||||
|
out[1 * outPitch + 2 * outPixelSize + outOffsetR] = colorTableRed[(dataRed0 & (0x7 << 18)) >> 18];
|
||||||
|
out[1 * outPitch + 2 * outPixelSize + outOffsetG] = colorTableGreen[(dataGreen0 & (0x7 << 18)) >> 18];
|
||||||
|
|
||||||
|
out[1 * outPitch + 3 * outPixelSize + outOffsetR] = colorTableRed[(dataRed0 & (0x7 << 21)) >> 21];
|
||||||
|
out[1 * outPitch + 3 * outPixelSize + outOffsetG] = colorTableGreen[(dataGreen0 & (0x7 << 21)) >> 21];
|
||||||
|
|
||||||
|
out[2 * outPitch + 0 * outPixelSize + outOffsetR] = colorTableRed[(dataRed1 & (0x7 << 0)) >> 0];
|
||||||
|
out[2 * outPitch + 0 * outPixelSize + outOffsetG] = colorTableGreen[(dataGreen1 & (0x7 << 0)) >> 0];
|
||||||
|
|
||||||
|
out[2 * outPitch + 1 * outPixelSize + outOffsetR] = colorTableRed[(dataRed1 & (0x7 << 3)) >> 3];
|
||||||
|
out[2 * outPitch + 1 * outPixelSize + outOffsetG] = colorTableGreen[(dataGreen1 & (0x7 << 3)) >> 3];
|
||||||
|
|
||||||
|
out[2 * outPitch + 2 * outPixelSize + outOffsetR] = colorTableRed[(dataRed1 & (0x7 << 6)) >> 6];
|
||||||
|
out[2 * outPitch + 2 * outPixelSize + outOffsetG] = colorTableGreen[(dataGreen1 & (0x7 << 6)) >> 6];
|
||||||
|
|
||||||
|
out[2 * outPitch + 3 * outPixelSize + outOffsetR] = colorTableRed[(dataRed1 & (0x7 << 9)) >> 9];
|
||||||
|
out[2 * outPitch + 3 * outPixelSize + outOffsetG] = colorTableGreen[(dataGreen1 & (0x7 << 9)) >> 9];
|
||||||
|
|
||||||
|
out[3 * outPitch + 0 * outPixelSize + outOffsetR] = colorTableRed[(dataRed1 & (0x7 << 12)) >> 12];
|
||||||
|
out[3 * outPitch + 0 * outPixelSize + outOffsetG] = colorTableGreen[(dataGreen1 & (0x7 << 12)) >> 12];
|
||||||
|
|
||||||
|
out[3 * outPitch + 1 * outPixelSize + outOffsetR] = colorTableRed[(dataRed1 & (0x7 << 15)) >> 15];
|
||||||
|
out[3 * outPitch + 1 * outPixelSize + outOffsetG] = colorTableGreen[(dataGreen1 & (0x7 << 15)) >> 15];
|
||||||
|
|
||||||
|
out[3 * outPitch + 2 * outPixelSize + outOffsetR] = colorTableRed[(dataRed1 & (0x7 << 18)) >> 18];
|
||||||
|
out[3 * outPitch + 2 * outPixelSize + outOffsetG] = colorTableGreen[(dataGreen1 & (0x7 << 18)) >> 18];
|
||||||
|
|
||||||
|
out[3 * outPitch + 3 * outPixelSize + outOffsetR] = colorTableRed[(dataRed1 & (0x7 << 21)) >> 21];
|
||||||
|
out[3 * outPitch + 3 * outPixelSize + outOffsetG] = colorTableGreen[(dataGreen1 & (0x7 << 21)) >> 21];
|
||||||
|
|
||||||
|
if (hasAlpha)
|
||||||
|
{
|
||||||
|
for (auto curHeight = 0u; curHeight < BC5_BLOCK_PIXELS; curHeight++)
|
||||||
|
{
|
||||||
|
for (auto curWidth = 0u; curWidth < BC5_BLOCK_PIXELS; curWidth++)
|
||||||
|
{
|
||||||
|
out[curHeight * outPitch + curWidth * outPixelSize + outOffsetA] = std::numeric_limits<uint8_t>::max();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecompressBlockEdge(const uint8_t* in,
|
||||||
|
uint8_t* out,
|
||||||
|
const unsigned width,
|
||||||
|
const unsigned height,
|
||||||
|
const unsigned outPitch,
|
||||||
|
const unsigned outPixelSize,
|
||||||
|
const unsigned outOffsetR,
|
||||||
|
const unsigned outOffsetG,
|
||||||
|
const unsigned outOffsetA,
|
||||||
|
const bool hasAlpha)
|
||||||
|
{
|
||||||
|
assert(width <= BC5_BLOCK_PIXELS);
|
||||||
|
assert(height <= BC5_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
uint8_t colorTableRed[8];
|
||||||
|
uint8_t colorTableGreen[8];
|
||||||
|
SetupColorTables(in, colorTableRed, colorTableGreen);
|
||||||
|
|
||||||
|
const uint32_t dataRed0 = in[2] | static_cast<uint32_t>(in[3] << 8u) | static_cast<uint32_t>(in[4] << 16u);
|
||||||
|
const uint32_t dataRed1 = in[5] | static_cast<uint32_t>(in[6] << 8u) | static_cast<uint32_t>(in[7] << 16u);
|
||||||
|
const uint32_t dataGreen0 =
|
||||||
|
in[BC5_CHANNEL_SIZE + 2] | static_cast<uint32_t>(in[BC5_CHANNEL_SIZE + 3] << 8) | static_cast<uint32_t>(in[BC5_CHANNEL_SIZE + 4] << 16);
|
||||||
|
const uint32_t dataGreen1 =
|
||||||
|
in[BC5_CHANNEL_SIZE + 5] | static_cast<uint32_t>(in[BC5_CHANNEL_SIZE + 6] << 8) | static_cast<uint32_t>(in[BC5_CHANNEL_SIZE + 7] << 16);
|
||||||
|
|
||||||
|
const uint8_t pixelsRed[]{
|
||||||
|
colorTableRed[(dataRed0 & (0x7 << 0)) >> 0],
|
||||||
|
colorTableRed[(dataRed0 & (0x7 << 3)) >> 3],
|
||||||
|
colorTableRed[(dataRed0 & (0x7 << 6)) >> 6],
|
||||||
|
colorTableRed[(dataRed0 & (0x7 << 9)) >> 9],
|
||||||
|
colorTableRed[(dataRed0 & (0x7 << 12)) >> 12],
|
||||||
|
colorTableRed[(dataRed0 & (0x7 << 15)) >> 15],
|
||||||
|
colorTableRed[(dataRed0 & (0x7 << 18)) >> 18],
|
||||||
|
colorTableRed[(dataRed0 & (0x7 << 21)) >> 21],
|
||||||
|
colorTableRed[(dataRed1 & (0x7 << 0)) >> 0],
|
||||||
|
colorTableRed[(dataRed1 & (0x7 << 3)) >> 3],
|
||||||
|
colorTableRed[(dataRed1 & (0x7 << 6)) >> 6],
|
||||||
|
colorTableRed[(dataRed1 & (0x7 << 9)) >> 9],
|
||||||
|
colorTableRed[(dataRed1 & (0x7 << 12)) >> 12],
|
||||||
|
colorTableRed[(dataRed1 & (0x7 << 15)) >> 15],
|
||||||
|
colorTableRed[(dataRed1 & (0x7 << 18)) >> 18],
|
||||||
|
colorTableRed[(dataRed1 & (0x7 << 21)) >> 21],
|
||||||
|
};
|
||||||
|
static_assert(std::extent_v<decltype(pixelsRed)> == BC5_BLOCK_PIXELS * BC5_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
const uint8_t pixelsGreen[]{
|
||||||
|
colorTableGreen[(dataGreen0 & (0x7 << 0)) >> 0],
|
||||||
|
colorTableGreen[(dataGreen0 & (0x7 << 3)) >> 3],
|
||||||
|
colorTableGreen[(dataGreen0 & (0x7 << 6)) >> 6],
|
||||||
|
colorTableGreen[(dataGreen0 & (0x7 << 9)) >> 9],
|
||||||
|
colorTableGreen[(dataGreen0 & (0x7 << 12)) >> 12],
|
||||||
|
colorTableGreen[(dataGreen0 & (0x7 << 15)) >> 15],
|
||||||
|
colorTableGreen[(dataGreen0 & (0x7 << 18)) >> 18],
|
||||||
|
colorTableGreen[(dataGreen0 & (0x7 << 21)) >> 21],
|
||||||
|
colorTableGreen[(dataGreen1 & (0x7 << 0)) >> 0],
|
||||||
|
colorTableGreen[(dataGreen1 & (0x7 << 3)) >> 3],
|
||||||
|
colorTableGreen[(dataGreen1 & (0x7 << 6)) >> 6],
|
||||||
|
colorTableGreen[(dataGreen1 & (0x7 << 9)) >> 9],
|
||||||
|
colorTableGreen[(dataGreen1 & (0x7 << 12)) >> 12],
|
||||||
|
colorTableGreen[(dataGreen1 & (0x7 << 15)) >> 15],
|
||||||
|
colorTableGreen[(dataGreen1 & (0x7 << 18)) >> 18],
|
||||||
|
colorTableGreen[(dataGreen1 & (0x7 << 21)) >> 21],
|
||||||
|
};
|
||||||
|
static_assert(std::extent_v<decltype(pixelsGreen)> == BC5_BLOCK_PIXELS * BC5_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
for (auto curHeight = 0u; curHeight < height; curHeight++)
|
||||||
|
{
|
||||||
|
for (auto curWidth = 0u; curWidth < width; curWidth++)
|
||||||
|
{
|
||||||
|
out[curHeight * outPitch + curWidth * outPixelSize + outOffsetR] = pixelsRed[curHeight * BC5_BLOCK_PIXELS + curWidth];
|
||||||
|
out[curHeight * outPitch + curWidth * outPixelSize + outOffsetG] = pixelsGreen[curHeight * BC5_BLOCK_PIXELS + curWidth];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasAlpha)
|
||||||
|
{
|
||||||
|
for (auto curHeight = 0u; curHeight < height; curHeight++)
|
||||||
|
{
|
||||||
|
for (auto curWidth = 0u; curWidth < width; curWidth++)
|
||||||
|
{
|
||||||
|
out[curHeight * outPitch + curWidth * outPixelSize + outOffsetA] = std::numeric_limits<uint8_t>::max();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace image
|
||||||
|
{
|
||||||
|
std::unique_ptr<Texture> DecompressorBc5::Decompress(const Texture& input, const ImageFormat* targetFormat)
|
||||||
|
{
|
||||||
|
assert(targetFormat->GetType() == ImageFormatType::UNSIGNED);
|
||||||
|
if (targetFormat->GetType() != ImageFormatType::UNSIGNED)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const auto* unsignedTargetFormat = dynamic_cast<const ImageFormatUnsigned*>(targetFormat);
|
||||||
|
|
||||||
|
const auto outPixelSize = unsignedTargetFormat->m_bits_per_pixel / 8u;
|
||||||
|
|
||||||
|
// Only support formats with byte aligned channels
|
||||||
|
const auto redByteAligned = unsignedTargetFormat->m_r_size == 8 && (unsignedTargetFormat->m_r_offset % 8) == 0;
|
||||||
|
const auto greenByteAligned = unsignedTargetFormat->m_g_size == 8 && (unsignedTargetFormat->m_g_offset % 8) == 0;
|
||||||
|
const auto hasAlpha = unsignedTargetFormat->m_a_size > 0;
|
||||||
|
const auto alphaByteAligned = unsignedTargetFormat->m_a_size == 8 && (unsignedTargetFormat->m_a_offset % 8) == 0;
|
||||||
|
assert(redByteAligned);
|
||||||
|
assert(greenByteAligned);
|
||||||
|
assert(!hasAlpha || alphaByteAligned);
|
||||||
|
if (!redByteAligned || !greenByteAligned || (hasAlpha && !alphaByteAligned))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const auto outOffsetR = unsignedTargetFormat->m_r_offset / 8;
|
||||||
|
const auto outOffsetG = unsignedTargetFormat->m_g_offset / 8;
|
||||||
|
const auto outOffsetA = unsignedTargetFormat->m_a_offset / 8;
|
||||||
|
|
||||||
|
const auto width = input.GetWidth();
|
||||||
|
const auto height = input.GetHeight();
|
||||||
|
const auto depth = input.GetDepth();
|
||||||
|
|
||||||
|
auto result = Texture::CreateForType(input.GetTextureType(), targetFormat, width, height, depth, input.HasMipMaps());
|
||||||
|
result->Allocate();
|
||||||
|
|
||||||
|
const auto faceCount = result->GetFaceCount();
|
||||||
|
const auto mipCount = result->HasMipMaps() ? result->GetMipMapCount() : 1;
|
||||||
|
assert(mipCount == input.HasMipMaps() ? input.GetMipMapCount() : 1);
|
||||||
|
|
||||||
|
for (auto mipLevel = 0; mipLevel < mipCount; mipLevel++)
|
||||||
|
{
|
||||||
|
const auto mipWidth = std::max<unsigned>(width >> mipLevel, 1u);
|
||||||
|
const auto mipHeight = std::max<unsigned>(height >> mipLevel, 1u);
|
||||||
|
const auto mipDepth = std::max<unsigned>(depth >> mipLevel, 1u);
|
||||||
|
const unsigned mipPitch = outPixelSize * mipWidth;
|
||||||
|
assert(mipPitch == result->GetFormat()->GetPitch(mipLevel, width));
|
||||||
|
|
||||||
|
for (auto face = 0; face < faceCount; face++)
|
||||||
|
{
|
||||||
|
const auto* bufferIn = input.GetBufferForMipLevel(mipLevel, face);
|
||||||
|
auto* bufferOut = result->GetBufferForMipLevel(mipLevel, face);
|
||||||
|
|
||||||
|
for (auto curDepth = 0u; curDepth < mipDepth; curDepth++)
|
||||||
|
{
|
||||||
|
const auto fullBlocksWidth = utils::AlignToPrevious(mipWidth, BC5_BLOCK_PIXELS);
|
||||||
|
const auto fullBlocksHeight = utils::AlignToPrevious(mipHeight, BC5_BLOCK_PIXELS);
|
||||||
|
|
||||||
|
for (auto curHeight = 0u; curHeight < fullBlocksHeight; curHeight += BC5_BLOCK_PIXELS)
|
||||||
|
{
|
||||||
|
for (auto curWidth = 0u; curWidth < fullBlocksWidth; curWidth += BC5_BLOCK_PIXELS)
|
||||||
|
{
|
||||||
|
DecompressBlock(bufferIn, bufferOut, mipPitch, outPixelSize, outOffsetR, outOffsetG, outOffsetA, hasAlpha);
|
||||||
|
bufferIn += BC5_BLOCK_SIZE;
|
||||||
|
bufferOut += outPixelSize * BC5_BLOCK_PIXELS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullBlocksWidth < mipWidth)
|
||||||
|
{
|
||||||
|
const auto edgeBlockWidth = mipWidth - fullBlocksWidth;
|
||||||
|
DecompressBlockEdge(
|
||||||
|
bufferIn, bufferOut, edgeBlockWidth, BC5_BLOCK_PIXELS, mipPitch, outPixelSize, outOffsetR, outOffsetG, outOffsetA, hasAlpha);
|
||||||
|
bufferIn += BC5_BLOCK_SIZE;
|
||||||
|
bufferOut += outPixelSize * edgeBlockWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferOut += mipPitch * (BC5_BLOCK_PIXELS - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullBlocksHeight < mipHeight)
|
||||||
|
{
|
||||||
|
const auto edgeBlockHeight = mipHeight - fullBlocksHeight;
|
||||||
|
for (auto curWidth = 0u; curWidth < fullBlocksWidth; curWidth += BC5_BLOCK_PIXELS)
|
||||||
|
{
|
||||||
|
DecompressBlockEdge(
|
||||||
|
bufferIn, bufferOut, BC5_BLOCK_PIXELS, edgeBlockHeight, mipPitch, outPixelSize, outOffsetR, outOffsetG, outOffsetA, hasAlpha);
|
||||||
|
bufferIn += BC5_BLOCK_SIZE;
|
||||||
|
bufferOut += outPixelSize * BC5_BLOCK_PIXELS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullBlocksWidth < mipWidth)
|
||||||
|
{
|
||||||
|
const auto edgeBlockWidth = mipWidth - fullBlocksWidth;
|
||||||
|
DecompressBlockEdge(
|
||||||
|
bufferIn, bufferOut, edgeBlockWidth, edgeBlockHeight, mipPitch, outPixelSize, outOffsetR, outOffsetG, outOffsetA, hasAlpha);
|
||||||
|
bufferIn += BC5_BLOCK_SIZE;
|
||||||
|
bufferOut += outPixelSize * edgeBlockWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferOut += mipPitch * (BC5_BLOCK_PIXELS - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} // namespace image
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ImageDecompressor.h"
|
||||||
|
|
||||||
|
namespace image
|
||||||
|
{
|
||||||
|
class DecompressorBc5 final : public ImageDecompressor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::unique_ptr<Texture> Decompress(const Texture& input, const ImageFormat* targetFormat) override;
|
||||||
|
};
|
||||||
|
} // namespace image
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#include "ImageDecompressor.h"
|
||||||
|
|
||||||
|
#include "DecompressorBc1.h"
|
||||||
|
#include "DecompressorBc2.h"
|
||||||
|
#include "DecompressorBc3.h"
|
||||||
|
#include "DecompressorBc4.h"
|
||||||
|
#include "DecompressorBc5.h"
|
||||||
|
|
||||||
|
namespace image
|
||||||
|
{
|
||||||
|
ImageDecompressor* ImageDecompressor::GetDecompressorForFormat(const ImageFormatId formatId)
|
||||||
|
{
|
||||||
|
switch (formatId)
|
||||||
|
{
|
||||||
|
case ImageFormatId::BC1:
|
||||||
|
{
|
||||||
|
static DecompressorBc1 bc1;
|
||||||
|
return &bc1;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ImageFormatId::BC2:
|
||||||
|
{
|
||||||
|
static DecompressorBc2 bc2;
|
||||||
|
return &bc2;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ImageFormatId::BC3:
|
||||||
|
{
|
||||||
|
static DecompressorBc3 bc3;
|
||||||
|
return &bc3;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ImageFormatId::BC4:
|
||||||
|
{
|
||||||
|
static DecompressorBc4 bc4;
|
||||||
|
return &bc4;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ImageFormatId::BC5:
|
||||||
|
{
|
||||||
|
static DecompressorBc5 bc5;
|
||||||
|
return &bc5;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace image
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Image/Texture.h"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace image
|
||||||
|
{
|
||||||
|
class ImageDecompressor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ImageDecompressor() = default;
|
||||||
|
virtual ~ImageDecompressor() = default;
|
||||||
|
ImageDecompressor(const ImageDecompressor& other) = default;
|
||||||
|
ImageDecompressor(ImageDecompressor&& other) noexcept = default;
|
||||||
|
ImageDecompressor& operator=(const ImageDecompressor& other) = default;
|
||||||
|
ImageDecompressor& operator=(ImageDecompressor&& other) noexcept = default;
|
||||||
|
|
||||||
|
virtual std::unique_ptr<Texture> Decompress(const Texture& input, const ImageFormat* targetFormat) = 0;
|
||||||
|
|
||||||
|
static ImageDecompressor* GetDecompressorForFormat(ImageFormatId formatId);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace image
|
||||||
@@ -87,7 +87,7 @@ namespace
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto* imageFormat : ImageFormat::ALL_FORMATS)
|
for (const auto* imageFormat : format::ALL)
|
||||||
{
|
{
|
||||||
if (imageFormat->GetDxgiFormat() == headerDx10.dxgiFormat)
|
if (imageFormat->GetDxgiFormat() == headerDx10.dxgiFormat)
|
||||||
{
|
{
|
||||||
@@ -105,25 +105,25 @@ namespace
|
|||||||
switch (pf.dwFourCC)
|
switch (pf.dwFourCC)
|
||||||
{
|
{
|
||||||
case utils::MakeMagic32('D', 'X', 'T', '1'):
|
case utils::MakeMagic32('D', 'X', 'T', '1'):
|
||||||
m_format = &ImageFormat::FORMAT_BC1;
|
m_format = &format::BC1;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case utils::MakeMagic32('D', 'X', 'T', '3'):
|
case utils::MakeMagic32('D', 'X', 'T', '3'):
|
||||||
m_format = &ImageFormat::FORMAT_BC2;
|
m_format = &format::BC2;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case utils::MakeMagic32('D', 'X', 'T', '5'):
|
case utils::MakeMagic32('D', 'X', 'T', '5'):
|
||||||
m_format = &ImageFormat::FORMAT_BC3;
|
m_format = &format::BC3;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case utils::MakeMagic32('A', 'T', 'I', '1'):
|
case utils::MakeMagic32('A', 'T', 'I', '1'):
|
||||||
case utils::MakeMagic32('B', 'C', '4', 'U'):
|
case utils::MakeMagic32('B', 'C', '4', 'U'):
|
||||||
m_format = &ImageFormat::FORMAT_BC4;
|
m_format = &format::BC4;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case utils::MakeMagic32('A', 'T', 'I', '2'):
|
case utils::MakeMagic32('A', 'T', 'I', '2'):
|
||||||
case utils::MakeMagic32('B', 'C', '5', 'U'):
|
case utils::MakeMagic32('B', 'C', '5', 'U'):
|
||||||
m_format = &ImageFormat::FORMAT_BC5;
|
m_format = &format::BC5;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case utils::MakeMagic32('D', 'X', '1', '0'):
|
case utils::MakeMagic32('D', 'X', '1', '0'):
|
||||||
@@ -165,7 +165,7 @@ namespace
|
|||||||
ExtractSizeAndOffsetFromMask(pf.dwBBitMask, bOffset, bSize);
|
ExtractSizeAndOffsetFromMask(pf.dwBBitMask, bOffset, bSize);
|
||||||
ExtractSizeAndOffsetFromMask(pf.dwABitMask, aOffset, aSize);
|
ExtractSizeAndOffsetFromMask(pf.dwABitMask, aOffset, aSize);
|
||||||
|
|
||||||
for (const auto* imageFormat : ImageFormat::ALL_FORMATS)
|
for (const auto* imageFormat : format::ALL)
|
||||||
{
|
{
|
||||||
if (imageFormat->GetType() != ImageFormatType::UNSIGNED)
|
if (imageFormat->GetType() != ImageFormatType::UNSIGNED)
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -4,30 +4,14 @@
|
|||||||
#include "Image/TextureConverter.h"
|
#include "Image/TextureConverter.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
using namespace image;
|
using namespace image;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
const std::map<ImageFormatId, ImageFormatId> DDS_CONVERSION_TABLE{
|
|
||||||
{ImageFormatId::R8_G8_B8, ImageFormatId::B8_G8_R8_X8},
|
|
||||||
};
|
|
||||||
|
|
||||||
class DdsWriterInternal
|
class DdsWriterInternal
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static bool SupportsImageFormat(const ImageFormat* imageFormat)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string GetFileExtension()
|
|
||||||
{
|
|
||||||
return ".dds";
|
|
||||||
}
|
|
||||||
|
|
||||||
DdsWriterInternal(std::ostream& stream, const Texture* texture)
|
DdsWriterInternal(std::ostream& stream, const Texture* texture)
|
||||||
: m_stream(stream),
|
: m_stream(stream),
|
||||||
m_texture(texture),
|
m_texture(texture),
|
||||||
@@ -37,8 +21,6 @@ namespace
|
|||||||
|
|
||||||
void DumpImage()
|
void DumpImage()
|
||||||
{
|
{
|
||||||
ConvertTextureIfNecessary();
|
|
||||||
|
|
||||||
DDS_HEADER header{};
|
DDS_HEADER header{};
|
||||||
PopulateDdsHeader(header);
|
PopulateDdsHeader(header);
|
||||||
|
|
||||||
@@ -207,37 +189,39 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConvertTextureIfNecessary()
|
|
||||||
{
|
|
||||||
const auto entry = DDS_CONVERSION_TABLE.find(m_texture->GetFormat()->GetId());
|
|
||||||
|
|
||||||
if (entry != DDS_CONVERSION_TABLE.end())
|
|
||||||
{
|
|
||||||
TextureConverter converter(m_texture, ImageFormat::ALL_FORMATS[static_cast<unsigned>(entry->second)]);
|
|
||||||
m_converted_texture = converter.Convert();
|
|
||||||
m_texture = m_converted_texture.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& m_stream;
|
std::ostream& m_stream;
|
||||||
const Texture* m_texture;
|
const Texture* m_texture;
|
||||||
std::unique_ptr<Texture> m_converted_texture;
|
|
||||||
bool m_use_dx10_extension;
|
bool m_use_dx10_extension;
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace image
|
namespace image
|
||||||
{
|
{
|
||||||
DdsWriter::~DdsWriter() = default;
|
std::unique_ptr<Texture> ConvertTextureForDdsFileOutputIfNecessary(const Texture* texture)
|
||||||
|
{
|
||||||
|
static const std::unordered_map<ImageFormatId, ImageFormatId> ddsConversionTable{
|
||||||
|
{ImageFormatId::R8_G8_B8, ImageFormatId::B8_G8_R8_X8},
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto entry = ddsConversionTable.find(texture->GetFormat()->GetId());
|
||||||
|
|
||||||
|
if (entry != ddsConversionTable.end())
|
||||||
|
{
|
||||||
|
TextureConverter converter(texture, ImageFormat::GetImageFormatById(entry->second));
|
||||||
|
return converter.Convert();
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool DdsWriter::SupportsImageFormat(const ImageFormat* imageFormat)
|
bool DdsWriter::SupportsImageFormat(const ImageFormat* imageFormat)
|
||||||
{
|
{
|
||||||
return DdsWriterInternal::SupportsImageFormat(imageFormat);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DdsWriter::GetFileExtension()
|
std::string DdsWriter::GetFileExtension()
|
||||||
{
|
{
|
||||||
return DdsWriterInternal::GetFileExtension();
|
return ".dds";
|
||||||
}
|
}
|
||||||
|
|
||||||
void DdsWriter::DumpImage(std::ostream& stream, const Texture* texture)
|
void DdsWriter::DumpImage(std::ostream& stream, const Texture* texture)
|
||||||
|
|||||||
@@ -2,13 +2,15 @@
|
|||||||
|
|
||||||
#include "ImageWriter.h"
|
#include "ImageWriter.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace image
|
namespace image
|
||||||
{
|
{
|
||||||
|
std::unique_ptr<Texture> ConvertTextureForDdsFileOutputIfNecessary(const Texture* texture);
|
||||||
|
|
||||||
class DdsWriter final : public ImageWriter
|
class DdsWriter final : public ImageWriter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
~DdsWriter() override;
|
|
||||||
|
|
||||||
bool SupportsImageFormat(const ImageFormat* imageFormat) override;
|
bool SupportsImageFormat(const ImageFormat* imageFormat) override;
|
||||||
std::string GetFileExtension() override;
|
std::string GetFileExtension() override;
|
||||||
void DumpImage(std::ostream& stream, const Texture* texture) override;
|
void DumpImage(std::ostream& stream, const Texture* texture) override;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace image
|
|||||||
|
|
||||||
const ImageFormat* Dx12TextureLoader::GetFormatForDx12Format() const
|
const ImageFormat* Dx12TextureLoader::GetFormatForDx12Format() const
|
||||||
{
|
{
|
||||||
for (const auto* i : ImageFormat::ALL_FORMATS)
|
for (const auto* i : format::ALL)
|
||||||
{
|
{
|
||||||
if (i->GetDxgiFormat() == m_format)
|
if (i->GetDxgiFormat() == m_format)
|
||||||
return i;
|
return i;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace image
|
|||||||
|
|
||||||
const ImageFormat* Dx9TextureLoader::GetFormatForDx9Format() const
|
const ImageFormat* Dx9TextureLoader::GetFormatForDx9Format() const
|
||||||
{
|
{
|
||||||
for (const auto* i : ImageFormat::ALL_FORMATS)
|
for (const auto* i : format::ALL)
|
||||||
{
|
{
|
||||||
if (i->GetD3DFormat() == m_format)
|
if (i->GetD3DFormat() == m_format)
|
||||||
return i;
|
return i;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ namespace
|
|||||||
{
|
{
|
||||||
const char* IMAGE_FORMAT_NAMES[]{
|
const char* IMAGE_FORMAT_NAMES[]{
|
||||||
"R8_G8_B8",
|
"R8_G8_B8",
|
||||||
|
"B8_G8_R8",
|
||||||
"B8_G8_R8_X8",
|
"B8_G8_R8_X8",
|
||||||
"R8_G8_B8_A8",
|
"R8_G8_B8_A8",
|
||||||
"B8_G8_R8_A8",
|
"B8_G8_R8_A8",
|
||||||
@@ -24,26 +25,29 @@ namespace
|
|||||||
|
|
||||||
namespace image
|
namespace image
|
||||||
{
|
{
|
||||||
const char* GetImageFormatName(ImageFormatId id)
|
ImageFormat::ImageFormat(const ImageFormatId id, std::string name, const oat::D3DFORMAT d3dFormat, const oat::DXGI_FORMAT dxgiFormat)
|
||||||
{
|
|
||||||
if (id < ImageFormatId::MAX)
|
|
||||||
return IMAGE_FORMAT_NAMES[static_cast<unsigned>(id)];
|
|
||||||
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageFormat::ImageFormat(const ImageFormatId id, const oat::D3DFORMAT d3dFormat, const oat::DXGI_FORMAT dxgiFormat)
|
|
||||||
: m_id(id),
|
: m_id(id),
|
||||||
|
m_name(std::move(name)),
|
||||||
m_d3d_format(d3dFormat),
|
m_d3d_format(d3dFormat),
|
||||||
m_dxgi_format(dxgiFormat)
|
m_dxgi_format(dxgiFormat)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ImageFormat* ImageFormat::GetImageFormatById(const ImageFormatId id)
|
||||||
|
{
|
||||||
|
return format::ALL[std::to_underlying(id)];
|
||||||
|
}
|
||||||
|
|
||||||
ImageFormatId ImageFormat::GetId() const
|
ImageFormatId ImageFormat::GetId() const
|
||||||
{
|
{
|
||||||
return m_id;
|
return m_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string& ImageFormat::GetName() const
|
||||||
|
{
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
oat::D3DFORMAT ImageFormat::GetD3DFormat() const
|
oat::D3DFORMAT ImageFormat::GetD3DFormat() const
|
||||||
{
|
{
|
||||||
return m_d3d_format;
|
return m_d3d_format;
|
||||||
@@ -55,6 +59,7 @@ namespace image
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImageFormatUnsigned::ImageFormatUnsigned(const ImageFormatId id,
|
ImageFormatUnsigned::ImageFormatUnsigned(const ImageFormatId id,
|
||||||
|
std::string name,
|
||||||
const oat::D3DFORMAT d3dFormat,
|
const oat::D3DFORMAT d3dFormat,
|
||||||
const oat::DXGI_FORMAT dxgiFormat,
|
const oat::DXGI_FORMAT dxgiFormat,
|
||||||
const unsigned bitsPerPixel,
|
const unsigned bitsPerPixel,
|
||||||
@@ -66,7 +71,7 @@ namespace image
|
|||||||
const unsigned bSize,
|
const unsigned bSize,
|
||||||
const unsigned aOffset,
|
const unsigned aOffset,
|
||||||
const unsigned aSize)
|
const unsigned aSize)
|
||||||
: ImageFormat(id, d3dFormat, dxgiFormat),
|
: ImageFormat(id, std::move(name), d3dFormat, dxgiFormat),
|
||||||
m_bits_per_pixel(bitsPerPixel),
|
m_bits_per_pixel(bitsPerPixel),
|
||||||
m_r_offset(rOffset),
|
m_r_offset(rOffset),
|
||||||
m_r_size(rSize),
|
m_r_size(rSize),
|
||||||
@@ -109,11 +114,43 @@ namespace image
|
|||||||
return mipWidth * mipHeight * mipDepth * m_bits_per_pixel / 8;
|
return mipWidth * mipHeight * mipDepth * m_bits_per_pixel / 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageFormatBlockCompressed::ImageFormatBlockCompressed(
|
bool ImageFormatUnsigned::HasR() const
|
||||||
const ImageFormatId id, const oat::D3DFORMAT d3dFormat, const oat::DXGI_FORMAT dxgiFormat, const unsigned blockSize, const unsigned bitsPerBlock)
|
{
|
||||||
: ImageFormat(id, d3dFormat, dxgiFormat),
|
return m_r_size > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImageFormatUnsigned::HasG() const
|
||||||
|
{
|
||||||
|
return m_g_size > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImageFormatUnsigned::HasB() const
|
||||||
|
{
|
||||||
|
return m_b_size > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImageFormatUnsigned::HasA() const
|
||||||
|
{
|
||||||
|
return m_a_size > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageFormatBlockCompressed::ImageFormatBlockCompressed(const ImageFormatId id,
|
||||||
|
std::string name,
|
||||||
|
const oat::D3DFORMAT d3dFormat,
|
||||||
|
const oat::DXGI_FORMAT dxgiFormat,
|
||||||
|
const unsigned blockSize,
|
||||||
|
const unsigned bitsPerBlock,
|
||||||
|
const bool hasR,
|
||||||
|
const bool hasG,
|
||||||
|
const bool hasB,
|
||||||
|
const bool hasA)
|
||||||
|
: ImageFormat(id, std::move(name), d3dFormat, dxgiFormat),
|
||||||
m_block_size(blockSize),
|
m_block_size(blockSize),
|
||||||
m_bits_per_block(bitsPerBlock)
|
m_bits_per_block(bitsPerBlock),
|
||||||
|
m_has_r(hasR),
|
||||||
|
m_has_g(hasG),
|
||||||
|
m_has_b(hasB),
|
||||||
|
m_has_a(hasA)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,57 +189,73 @@ namespace image
|
|||||||
return blockCount * m_bits_per_block / 8;
|
return blockCount * m_bits_per_block / 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImageFormatUnsigned::HasR() const
|
bool ImageFormatBlockCompressed::HasR() const
|
||||||
{
|
{
|
||||||
return m_r_size > 0;
|
return m_has_r;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImageFormatUnsigned::HasG() const
|
bool ImageFormatBlockCompressed::HasG() const
|
||||||
{
|
{
|
||||||
return m_g_size > 0;
|
return m_has_g;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImageFormatUnsigned::HasB() const
|
bool ImageFormatBlockCompressed::HasB() const
|
||||||
{
|
{
|
||||||
return m_b_size > 0;
|
return m_has_b;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImageFormatUnsigned::HasA() const
|
bool ImageFormatBlockCompressed::HasA() const
|
||||||
{
|
{
|
||||||
return m_a_size > 0;
|
return m_has_a;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ImageFormatUnsigned ImageFormat::FORMAT_R8_G8_B8(ImageFormatId::R8_G8_B8, oat::D3DFMT_R8G8B8, oat::DXGI_FORMAT_UNKNOWN, 24, 0, 8, 8, 8, 16, 8, 0, 0);
|
namespace format
|
||||||
const ImageFormatUnsigned
|
{
|
||||||
ImageFormat::FORMAT_B8_G8_R8_X8(ImageFormatId::B8_G8_R8_X8, oat::D3DFMT_X8R8G8B8, oat::DXGI_FORMAT_B8G8R8X8_UNORM, 32, 16, 8, 8, 8, 0, 8, 0, 0);
|
const ImageFormatUnsigned R8_G8_B8(ImageFormatId::R8_G8_B8, "R8_G8_B8", oat::D3DFMT_R8G8B8, oat::DXGI_FORMAT_UNKNOWN, 24, 0, 8, 8, 8, 16, 8, 0, 0);
|
||||||
const ImageFormatUnsigned
|
const ImageFormatUnsigned B8_G8_R8(ImageFormatId::B8_G8_R8, "B8_G8_R8", oat::D3DFMT_UNKNOWN, oat::DXGI_FORMAT_UNKNOWN, 24, 16, 8, 8, 8, 0, 8, 0, 0);
|
||||||
ImageFormat::FORMAT_R8_G8_B8_A8(ImageFormatId::R8_G8_B8_A8, oat::D3DFMT_A8B8G8R8, oat::DXGI_FORMAT_R8G8B8A8_UNORM, 32, 0, 8, 8, 8, 16, 8, 24, 8);
|
const ImageFormatUnsigned
|
||||||
const ImageFormatUnsigned
|
B8_G8_R8_X8(ImageFormatId::B8_G8_R8_X8, "B8_G8_R8_X8", oat::D3DFMT_X8R8G8B8, oat::DXGI_FORMAT_B8G8R8X8_UNORM, 32, 16, 8, 8, 8, 0, 8, 0, 0);
|
||||||
ImageFormat::FORMAT_B8_G8_R8_A8(ImageFormatId::B8_G8_R8_A8, oat::D3DFMT_A8R8G8B8, oat::DXGI_FORMAT_B8G8R8A8_UNORM, 32, 16, 8, 8, 8, 0, 8, 24, 8);
|
const ImageFormatUnsigned
|
||||||
const ImageFormatUnsigned ImageFormat::FORMAT_A8(ImageFormatId::A8, oat::D3DFMT_A8, oat::DXGI_FORMAT_A8_UNORM, 8, 0, 0, 0, 0, 0, 0, 0, 8);
|
R8_G8_B8_A8(ImageFormatId::R8_G8_B8_A8, "R8_G8_B8_A8", oat::D3DFMT_A8B8G8R8, oat::DXGI_FORMAT_R8G8B8A8_UNORM, 32, 0, 8, 8, 8, 16, 8, 24, 8);
|
||||||
const ImageFormatUnsigned ImageFormat::FORMAT_R16_G16_B16_A16_FLOAT(
|
const ImageFormatUnsigned
|
||||||
ImageFormatId::R16_G16_B16_A16_FLOAT, oat::D3DFMT_A16B16G16R16F, oat::DXGI_FORMAT_R16G16B16A16_FLOAT, 128, 0, 0, 0, 0, 0, 0, 0, 8);
|
B8_G8_R8_A8(ImageFormatId::B8_G8_R8_A8, "B8_G8_R8_A8", oat::D3DFMT_A8R8G8B8, oat::DXGI_FORMAT_B8G8R8A8_UNORM, 32, 16, 8, 8, 8, 0, 8, 24, 8);
|
||||||
const ImageFormatUnsigned ImageFormat::FORMAT_R8(ImageFormatId::R8, oat::D3DFMT_L8, oat::DXGI_FORMAT_R8_UNORM, 8, 0, 8, 0, 0, 0, 0, 0, 0);
|
const ImageFormatUnsigned A8(ImageFormatId::A8, "A8", oat::D3DFMT_A8, oat::DXGI_FORMAT_A8_UNORM, 8, 0, 0, 0, 0, 0, 0, 0, 8);
|
||||||
const ImageFormatUnsigned ImageFormat::FORMAT_R8_A8(ImageFormatId::R8_A8, oat::D3DFMT_A8L8, oat::DXGI_FORMAT_UNKNOWN, 16, 0, 8, 0, 0, 0, 0, 8, 8);
|
const ImageFormatUnsigned R16_G16_B16_A16_FLOAT(ImageFormatId::R16_G16_B16_A16_FLOAT,
|
||||||
const ImageFormatBlockCompressed ImageFormat::FORMAT_BC1(ImageFormatId::BC1, oat::D3DFMT_DXT1, oat::DXGI_FORMAT_BC1_UNORM, 4, 64);
|
"R16_G16_B16_A16_FLOAT",
|
||||||
const ImageFormatBlockCompressed ImageFormat::FORMAT_BC2(ImageFormatId::BC2, oat::D3DFMT_DXT3, oat::DXGI_FORMAT_BC2_UNORM, 4, 128);
|
oat::D3DFMT_A16B16G16R16F,
|
||||||
const ImageFormatBlockCompressed ImageFormat::FORMAT_BC3(ImageFormatId::BC3, oat::D3DFMT_DXT5, oat::DXGI_FORMAT_BC3_UNORM, 4, 128);
|
oat::DXGI_FORMAT_R16G16B16A16_FLOAT,
|
||||||
const ImageFormatBlockCompressed ImageFormat::FORMAT_BC4(ImageFormatId::BC4, oat::D3DFMT_UNKNOWN, oat::DXGI_FORMAT_BC4_UNORM, 4, 64);
|
128,
|
||||||
const ImageFormatBlockCompressed ImageFormat::FORMAT_BC5(ImageFormatId::BC5, oat::D3DFMT_UNKNOWN, oat::DXGI_FORMAT_BC5_UNORM, 4, 128);
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
8);
|
||||||
|
const ImageFormatUnsigned R8(ImageFormatId::R8, "R8", oat::D3DFMT_L8, oat::DXGI_FORMAT_R8_UNORM, 8, 0, 8, 0, 0, 0, 0, 0, 0);
|
||||||
|
const ImageFormatUnsigned R8_A8(ImageFormatId::R8_A8, "R8_A8", oat::D3DFMT_A8L8, oat::DXGI_FORMAT_UNKNOWN, 16, 0, 8, 0, 0, 0, 0, 8, 8);
|
||||||
|
const ImageFormatBlockCompressed BC1(ImageFormatId::BC1, "BC1", oat::D3DFMT_DXT1, oat::DXGI_FORMAT_BC1_UNORM, 4, 64, true, true, true, true);
|
||||||
|
const ImageFormatBlockCompressed BC2(ImageFormatId::BC2, "BC2", oat::D3DFMT_DXT3, oat::DXGI_FORMAT_BC2_UNORM, 4, 128, true, true, true, true);
|
||||||
|
const ImageFormatBlockCompressed BC3(ImageFormatId::BC3, "BC3", oat::D3DFMT_DXT5, oat::DXGI_FORMAT_BC3_UNORM, 4, 128, true, true, true, true);
|
||||||
|
const ImageFormatBlockCompressed BC4(ImageFormatId::BC4, "BC4", oat::D3DFMT_UNKNOWN, oat::DXGI_FORMAT_BC4_UNORM, 4, 64, true, false, false, false);
|
||||||
|
const ImageFormatBlockCompressed BC5(ImageFormatId::BC5, "BC5", oat::D3DFMT_UNKNOWN, oat::DXGI_FORMAT_BC5_UNORM, 4, 128, true, true, false, false);
|
||||||
|
|
||||||
const ImageFormat* const ImageFormat::ALL_FORMATS[static_cast<unsigned>(ImageFormatId::MAX)]{
|
const ImageFormat* const ALL[static_cast<unsigned>(ImageFormatId::MAX)]{
|
||||||
&FORMAT_R8_G8_B8,
|
&R8_G8_B8,
|
||||||
&FORMAT_B8_G8_R8_X8,
|
&B8_G8_R8,
|
||||||
&FORMAT_R8_G8_B8_A8,
|
&B8_G8_R8_X8,
|
||||||
&FORMAT_B8_G8_R8_A8,
|
&R8_G8_B8_A8,
|
||||||
&FORMAT_A8,
|
&B8_G8_R8_A8,
|
||||||
&FORMAT_R16_G16_B16_A16_FLOAT,
|
&A8,
|
||||||
&FORMAT_R8,
|
&R16_G16_B16_A16_FLOAT,
|
||||||
&FORMAT_R8_A8,
|
&R8,
|
||||||
&FORMAT_BC1,
|
&R8_A8,
|
||||||
&FORMAT_BC2,
|
&BC1,
|
||||||
&FORMAT_BC3,
|
&BC2,
|
||||||
&FORMAT_BC4,
|
&BC3,
|
||||||
&FORMAT_BC5,
|
&BC4,
|
||||||
};
|
&BC5,
|
||||||
|
};
|
||||||
|
} // namespace format
|
||||||
} // namespace image
|
} // namespace image
|
||||||
|
|||||||
@@ -5,12 +5,14 @@
|
|||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace image
|
namespace image
|
||||||
{
|
{
|
||||||
enum class ImageFormatId : std::uint8_t
|
enum class ImageFormatId : std::uint8_t
|
||||||
{
|
{
|
||||||
R8_G8_B8,
|
R8_G8_B8,
|
||||||
|
B8_G8_R8,
|
||||||
B8_G8_R8_X8,
|
B8_G8_R8_X8,
|
||||||
R8_G8_B8_A8,
|
R8_G8_B8_A8,
|
||||||
B8_G8_R8_A8,
|
B8_G8_R8_A8,
|
||||||
@@ -24,12 +26,9 @@ namespace image
|
|||||||
BC4,
|
BC4,
|
||||||
BC5,
|
BC5,
|
||||||
|
|
||||||
MAX,
|
MAX
|
||||||
UNKNOWN
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* GetImageFormatName(ImageFormatId id);
|
|
||||||
|
|
||||||
enum class ImageFormatType : std::uint8_t
|
enum class ImageFormatType : std::uint8_t
|
||||||
{
|
{
|
||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
@@ -42,17 +41,12 @@ namespace image
|
|||||||
|
|
||||||
class ImageFormat
|
class ImageFormat
|
||||||
{
|
{
|
||||||
ImageFormatId m_id;
|
|
||||||
oat::D3DFORMAT m_d3d_format;
|
|
||||||
oat::DXGI_FORMAT m_dxgi_format;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
ImageFormat(ImageFormatId id, oat::D3DFORMAT d3dFormat, oat::DXGI_FORMAT dxgiFormat);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static const ImageFormat* GetImageFormatById(ImageFormatId id);
|
||||||
virtual ~ImageFormat() = default;
|
virtual ~ImageFormat() = default;
|
||||||
|
|
||||||
[[nodiscard]] ImageFormatId GetId() const;
|
[[nodiscard]] ImageFormatId GetId() const;
|
||||||
|
[[nodiscard]] const std::string& GetName() const;
|
||||||
[[nodiscard]] oat::D3DFORMAT GetD3DFormat() const;
|
[[nodiscard]] oat::D3DFORMAT GetD3DFormat() const;
|
||||||
[[nodiscard]] oat::DXGI_FORMAT GetDxgiFormat() const;
|
[[nodiscard]] oat::DXGI_FORMAT GetDxgiFormat() const;
|
||||||
|
|
||||||
@@ -60,36 +54,26 @@ namespace image
|
|||||||
[[nodiscard]] virtual size_t GetPitch(unsigned mipLevel, unsigned width) const = 0;
|
[[nodiscard]] virtual size_t GetPitch(unsigned mipLevel, unsigned width) const = 0;
|
||||||
[[nodiscard]] virtual size_t GetSizeOfMipLevel(unsigned mipLevel, unsigned width, unsigned height, unsigned depth) const = 0;
|
[[nodiscard]] virtual size_t GetSizeOfMipLevel(unsigned mipLevel, unsigned width, unsigned height, unsigned depth) const = 0;
|
||||||
|
|
||||||
static const ImageFormatUnsigned FORMAT_R8_G8_B8;
|
[[nodiscard]] virtual bool HasR() const = 0;
|
||||||
static const ImageFormatUnsigned FORMAT_B8_G8_R8_X8;
|
[[nodiscard]] virtual bool HasG() const = 0;
|
||||||
static const ImageFormatUnsigned FORMAT_R8_G8_B8_A8;
|
[[nodiscard]] virtual bool HasB() const = 0;
|
||||||
static const ImageFormatUnsigned FORMAT_B8_G8_R8_A8;
|
[[nodiscard]] virtual bool HasA() const = 0;
|
||||||
static const ImageFormatUnsigned FORMAT_A8;
|
|
||||||
static const ImageFormatUnsigned FORMAT_R16_G16_B16_A16_FLOAT; // TODO: Float not unsigned
|
protected:
|
||||||
static const ImageFormatUnsigned FORMAT_R8;
|
ImageFormat(ImageFormatId id, std::string name, oat::D3DFORMAT d3dFormat, oat::DXGI_FORMAT dxgiFormat);
|
||||||
static const ImageFormatUnsigned FORMAT_R8_A8;
|
|
||||||
static const ImageFormatBlockCompressed FORMAT_BC1;
|
private:
|
||||||
static const ImageFormatBlockCompressed FORMAT_BC2;
|
ImageFormatId m_id;
|
||||||
static const ImageFormatBlockCompressed FORMAT_BC3;
|
std::string m_name;
|
||||||
static const ImageFormatBlockCompressed FORMAT_BC4;
|
oat::D3DFORMAT m_d3d_format;
|
||||||
static const ImageFormatBlockCompressed FORMAT_BC5;
|
oat::DXGI_FORMAT m_dxgi_format;
|
||||||
static const ImageFormat* const ALL_FORMATS[static_cast<unsigned>(ImageFormatId::MAX)];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ImageFormatUnsigned final : public ImageFormat
|
class ImageFormatUnsigned final : public ImageFormat
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
unsigned m_bits_per_pixel;
|
|
||||||
unsigned m_r_offset;
|
|
||||||
unsigned m_r_size;
|
|
||||||
unsigned m_g_offset;
|
|
||||||
unsigned m_g_size;
|
|
||||||
unsigned m_b_offset;
|
|
||||||
unsigned m_b_size;
|
|
||||||
unsigned m_a_offset;
|
|
||||||
unsigned m_a_size;
|
|
||||||
|
|
||||||
ImageFormatUnsigned(ImageFormatId id,
|
ImageFormatUnsigned(ImageFormatId id,
|
||||||
|
std::string name,
|
||||||
oat::D3DFORMAT d3dFormat,
|
oat::D3DFORMAT d3dFormat,
|
||||||
oat::DXGI_FORMAT dxgiFormat,
|
oat::DXGI_FORMAT dxgiFormat,
|
||||||
unsigned bitsPerPixel,
|
unsigned bitsPerPixel,
|
||||||
@@ -106,22 +90,70 @@ namespace image
|
|||||||
[[nodiscard]] size_t GetPitch(unsigned mipLevel, unsigned width) const override;
|
[[nodiscard]] size_t GetPitch(unsigned mipLevel, unsigned width) const override;
|
||||||
[[nodiscard]] size_t GetSizeOfMipLevel(unsigned mipLevel, unsigned width, unsigned height, unsigned depth) const override;
|
[[nodiscard]] size_t GetSizeOfMipLevel(unsigned mipLevel, unsigned width, unsigned height, unsigned depth) const override;
|
||||||
|
|
||||||
[[nodiscard]] bool HasR() const;
|
[[nodiscard]] bool HasR() const override;
|
||||||
[[nodiscard]] bool HasG() const;
|
[[nodiscard]] bool HasG() const override;
|
||||||
[[nodiscard]] bool HasB() const;
|
[[nodiscard]] bool HasB() const override;
|
||||||
[[nodiscard]] bool HasA() const;
|
[[nodiscard]] bool HasA() const override;
|
||||||
|
|
||||||
|
unsigned m_bits_per_pixel;
|
||||||
|
unsigned m_r_offset;
|
||||||
|
unsigned m_r_size;
|
||||||
|
unsigned m_g_offset;
|
||||||
|
unsigned m_g_size;
|
||||||
|
unsigned m_b_offset;
|
||||||
|
unsigned m_b_size;
|
||||||
|
unsigned m_a_offset;
|
||||||
|
unsigned m_a_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ImageFormatBlockCompressed final : public ImageFormat
|
class ImageFormatBlockCompressed final : public ImageFormat
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
unsigned m_block_size;
|
ImageFormatBlockCompressed(ImageFormatId id,
|
||||||
unsigned m_bits_per_block;
|
std::string name,
|
||||||
|
oat::D3DFORMAT d3dFormat,
|
||||||
ImageFormatBlockCompressed(ImageFormatId id, oat::D3DFORMAT d3dFormat, oat::DXGI_FORMAT dxgiFormat, unsigned blockSize, unsigned bitsPerBlock);
|
oat::DXGI_FORMAT dxgiFormat,
|
||||||
|
unsigned blockSize,
|
||||||
|
unsigned bitsPerBlock,
|
||||||
|
bool hasR,
|
||||||
|
bool hasG,
|
||||||
|
bool hasB,
|
||||||
|
bool hasA);
|
||||||
|
|
||||||
[[nodiscard]] ImageFormatType GetType() const override;
|
[[nodiscard]] ImageFormatType GetType() const override;
|
||||||
[[nodiscard]] size_t GetPitch(unsigned mipLevel, unsigned width) const override;
|
[[nodiscard]] size_t GetPitch(unsigned mipLevel, unsigned width) const override;
|
||||||
[[nodiscard]] size_t GetSizeOfMipLevel(unsigned mipLevel, unsigned width, unsigned height, unsigned depth) const override;
|
[[nodiscard]] size_t GetSizeOfMipLevel(unsigned mipLevel, unsigned width, unsigned height, unsigned depth) const override;
|
||||||
|
|
||||||
|
[[nodiscard]] bool HasR() const override;
|
||||||
|
[[nodiscard]] bool HasG() const override;
|
||||||
|
[[nodiscard]] bool HasB() const override;
|
||||||
|
[[nodiscard]] bool HasA() const override;
|
||||||
|
|
||||||
|
unsigned m_block_size;
|
||||||
|
unsigned m_bits_per_block;
|
||||||
|
bool m_has_r;
|
||||||
|
bool m_has_g;
|
||||||
|
bool m_has_b;
|
||||||
|
bool m_has_a;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace format
|
||||||
|
{
|
||||||
|
extern const ImageFormatUnsigned R8_G8_B8;
|
||||||
|
extern const ImageFormatUnsigned B8_G8_R8;
|
||||||
|
extern const ImageFormatUnsigned B8_G8_R8_X8;
|
||||||
|
extern const ImageFormatUnsigned R8_G8_B8_A8;
|
||||||
|
extern const ImageFormatUnsigned B8_G8_R8_A8;
|
||||||
|
extern const ImageFormatUnsigned A8;
|
||||||
|
extern const ImageFormatUnsigned R16_G16_B16_A16_FLOAT; // TODO: Float not unsigned
|
||||||
|
extern const ImageFormatUnsigned R8;
|
||||||
|
extern const ImageFormatUnsigned R8_A8;
|
||||||
|
extern const ImageFormatBlockCompressed BC1;
|
||||||
|
extern const ImageFormatBlockCompressed BC2;
|
||||||
|
extern const ImageFormatBlockCompressed BC3;
|
||||||
|
extern const ImageFormatBlockCompressed BC4;
|
||||||
|
extern const ImageFormatBlockCompressed BC5;
|
||||||
|
|
||||||
|
extern const ImageFormat* const ALL[std::to_underlying(ImageFormatId::MAX)];
|
||||||
|
} // namespace format
|
||||||
} // namespace image
|
} // namespace image
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <iostream>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
using namespace image;
|
using namespace image;
|
||||||
@@ -17,23 +16,23 @@ namespace
|
|||||||
switch (static_cast<iwi6::IwiFormat>(format))
|
switch (static_cast<iwi6::IwiFormat>(format))
|
||||||
{
|
{
|
||||||
case iwi6::IwiFormat::IMG_FORMAT_BITMAP_RGBA:
|
case iwi6::IwiFormat::IMG_FORMAT_BITMAP_RGBA:
|
||||||
return &ImageFormat::FORMAT_R8_G8_B8_A8;
|
return &format::R8_G8_B8_A8;
|
||||||
case iwi6::IwiFormat::IMG_FORMAT_BITMAP_RGB:
|
case iwi6::IwiFormat::IMG_FORMAT_BITMAP_RGB:
|
||||||
return &ImageFormat::FORMAT_R8_G8_B8;
|
return &format::R8_G8_B8;
|
||||||
case iwi6::IwiFormat::IMG_FORMAT_BITMAP_ALPHA:
|
case iwi6::IwiFormat::IMG_FORMAT_BITMAP_ALPHA:
|
||||||
return &ImageFormat::FORMAT_A8;
|
return &format::A8;
|
||||||
case iwi6::IwiFormat::IMG_FORMAT_DXT1:
|
case iwi6::IwiFormat::IMG_FORMAT_DXT1:
|
||||||
return &ImageFormat::FORMAT_BC1;
|
return &format::BC1;
|
||||||
case iwi6::IwiFormat::IMG_FORMAT_DXT3:
|
case iwi6::IwiFormat::IMG_FORMAT_DXT3:
|
||||||
return &ImageFormat::FORMAT_BC2;
|
return &format::BC2;
|
||||||
case iwi6::IwiFormat::IMG_FORMAT_DXT5:
|
case iwi6::IwiFormat::IMG_FORMAT_DXT5:
|
||||||
return &ImageFormat::FORMAT_BC3;
|
return &format::BC3;
|
||||||
case iwi6::IwiFormat::IMG_FORMAT_DXN:
|
case iwi6::IwiFormat::IMG_FORMAT_DXN:
|
||||||
return &ImageFormat::FORMAT_BC5;
|
return &format::BC5;
|
||||||
case iwi6::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE_ALPHA:
|
case iwi6::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE_ALPHA:
|
||||||
return &ImageFormat::FORMAT_R8_A8;
|
return &format::R8_A8;
|
||||||
case iwi6::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE:
|
case iwi6::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE:
|
||||||
return &ImageFormat::FORMAT_R8;
|
return &format::R8;
|
||||||
case iwi6::IwiFormat::IMG_FORMAT_WAVELET_RGBA: // used
|
case iwi6::IwiFormat::IMG_FORMAT_WAVELET_RGBA: // used
|
||||||
case iwi6::IwiFormat::IMG_FORMAT_WAVELET_RGB: // used
|
case iwi6::IwiFormat::IMG_FORMAT_WAVELET_RGB: // used
|
||||||
case iwi6::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE_ALPHA:
|
case iwi6::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE_ALPHA:
|
||||||
@@ -122,23 +121,23 @@ namespace
|
|||||||
switch (static_cast<iwi8::IwiFormat>(format))
|
switch (static_cast<iwi8::IwiFormat>(format))
|
||||||
{
|
{
|
||||||
case iwi8::IwiFormat::IMG_FORMAT_BITMAP_RGBA:
|
case iwi8::IwiFormat::IMG_FORMAT_BITMAP_RGBA:
|
||||||
return &ImageFormat::FORMAT_R8_G8_B8_A8;
|
return &format::R8_G8_B8_A8;
|
||||||
case iwi8::IwiFormat::IMG_FORMAT_BITMAP_RGB:
|
case iwi8::IwiFormat::IMG_FORMAT_BITMAP_RGB:
|
||||||
return &ImageFormat::FORMAT_R8_G8_B8;
|
return &format::R8_G8_B8;
|
||||||
case iwi8::IwiFormat::IMG_FORMAT_BITMAP_ALPHA:
|
case iwi8::IwiFormat::IMG_FORMAT_BITMAP_ALPHA:
|
||||||
return &ImageFormat::FORMAT_A8;
|
return &format::A8;
|
||||||
case iwi8::IwiFormat::IMG_FORMAT_DXT1:
|
case iwi8::IwiFormat::IMG_FORMAT_DXT1:
|
||||||
return &ImageFormat::FORMAT_BC1;
|
return &format::BC1;
|
||||||
case iwi8::IwiFormat::IMG_FORMAT_DXT3:
|
case iwi8::IwiFormat::IMG_FORMAT_DXT3:
|
||||||
return &ImageFormat::FORMAT_BC2;
|
return &format::BC2;
|
||||||
case iwi8::IwiFormat::IMG_FORMAT_DXT5:
|
case iwi8::IwiFormat::IMG_FORMAT_DXT5:
|
||||||
return &ImageFormat::FORMAT_BC3;
|
return &format::BC3;
|
||||||
case iwi8::IwiFormat::IMG_FORMAT_DXN:
|
case iwi8::IwiFormat::IMG_FORMAT_DXN:
|
||||||
return &ImageFormat::FORMAT_BC5;
|
return &format::BC5;
|
||||||
case iwi8::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE_ALPHA:
|
case iwi8::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE_ALPHA:
|
||||||
return &ImageFormat::FORMAT_R8_A8;
|
return &format::R8_A8;
|
||||||
case iwi8::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE:
|
case iwi8::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE:
|
||||||
return &ImageFormat::FORMAT_R8;
|
return &format::R8;
|
||||||
case iwi8::IwiFormat::IMG_FORMAT_WAVELET_RGBA: // used
|
case iwi8::IwiFormat::IMG_FORMAT_WAVELET_RGBA: // used
|
||||||
case iwi8::IwiFormat::IMG_FORMAT_WAVELET_RGB: // used
|
case iwi8::IwiFormat::IMG_FORMAT_WAVELET_RGB: // used
|
||||||
case iwi8::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE_ALPHA:
|
case iwi8::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE_ALPHA:
|
||||||
@@ -251,23 +250,23 @@ namespace
|
|||||||
switch (static_cast<iwi13::IwiFormat>(format))
|
switch (static_cast<iwi13::IwiFormat>(format))
|
||||||
{
|
{
|
||||||
case iwi13::IwiFormat::IMG_FORMAT_BITMAP_RGBA:
|
case iwi13::IwiFormat::IMG_FORMAT_BITMAP_RGBA:
|
||||||
return &ImageFormat::FORMAT_R8_G8_B8_A8;
|
return &format::R8_G8_B8_A8;
|
||||||
case iwi13::IwiFormat::IMG_FORMAT_BITMAP_RGB:
|
case iwi13::IwiFormat::IMG_FORMAT_BITMAP_RGB:
|
||||||
return &ImageFormat::FORMAT_R8_G8_B8;
|
return &format::R8_G8_B8;
|
||||||
case iwi13::IwiFormat::IMG_FORMAT_BITMAP_ALPHA:
|
case iwi13::IwiFormat::IMG_FORMAT_BITMAP_ALPHA:
|
||||||
return &ImageFormat::FORMAT_A8;
|
return &format::A8;
|
||||||
case iwi13::IwiFormat::IMG_FORMAT_DXT1:
|
case iwi13::IwiFormat::IMG_FORMAT_DXT1:
|
||||||
return &ImageFormat::FORMAT_BC1;
|
return &format::BC1;
|
||||||
case iwi13::IwiFormat::IMG_FORMAT_DXT3:
|
case iwi13::IwiFormat::IMG_FORMAT_DXT3:
|
||||||
return &ImageFormat::FORMAT_BC2;
|
return &format::BC2;
|
||||||
case iwi13::IwiFormat::IMG_FORMAT_DXT5:
|
case iwi13::IwiFormat::IMG_FORMAT_DXT5:
|
||||||
return &ImageFormat::FORMAT_BC3;
|
return &format::BC3;
|
||||||
case iwi13::IwiFormat::IMG_FORMAT_DXN:
|
case iwi13::IwiFormat::IMG_FORMAT_DXN:
|
||||||
return &ImageFormat::FORMAT_BC5;
|
return &format::BC5;
|
||||||
case iwi13::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE_ALPHA:
|
case iwi13::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE_ALPHA:
|
||||||
return &ImageFormat::FORMAT_R8_A8;
|
return &format::R8_A8;
|
||||||
case iwi13::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE:
|
case iwi13::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE:
|
||||||
return &ImageFormat::FORMAT_R8;
|
return &format::R8;
|
||||||
case iwi13::IwiFormat::IMG_FORMAT_WAVELET_RGBA: // used
|
case iwi13::IwiFormat::IMG_FORMAT_WAVELET_RGBA: // used
|
||||||
case iwi13::IwiFormat::IMG_FORMAT_WAVELET_RGB: // used
|
case iwi13::IwiFormat::IMG_FORMAT_WAVELET_RGB: // used
|
||||||
case iwi13::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE_ALPHA:
|
case iwi13::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE_ALPHA:
|
||||||
@@ -299,7 +298,7 @@ namespace
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto* format = GetFormat6(header.format);
|
const auto* format = GetFormat13(header.format);
|
||||||
if (format == nullptr)
|
if (format == nullptr)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
@@ -362,26 +361,26 @@ namespace
|
|||||||
switch (static_cast<iwi27::IwiFormat>(format))
|
switch (static_cast<iwi27::IwiFormat>(format))
|
||||||
{
|
{
|
||||||
case iwi27::IwiFormat::IMG_FORMAT_BITMAP_RGBA:
|
case iwi27::IwiFormat::IMG_FORMAT_BITMAP_RGBA:
|
||||||
return &ImageFormat::FORMAT_R8_G8_B8_A8;
|
return &format::R8_G8_B8_A8;
|
||||||
case iwi27::IwiFormat::IMG_FORMAT_BITMAP_ALPHA:
|
case iwi27::IwiFormat::IMG_FORMAT_BITMAP_ALPHA:
|
||||||
return &ImageFormat::FORMAT_A8;
|
return &format::A8;
|
||||||
case iwi27::IwiFormat::IMG_FORMAT_DXT1:
|
case iwi27::IwiFormat::IMG_FORMAT_DXT1:
|
||||||
return &ImageFormat::FORMAT_BC1;
|
return &format::BC1;
|
||||||
case iwi27::IwiFormat::IMG_FORMAT_DXT3:
|
case iwi27::IwiFormat::IMG_FORMAT_DXT3:
|
||||||
return &ImageFormat::FORMAT_BC2;
|
return &format::BC2;
|
||||||
case iwi27::IwiFormat::IMG_FORMAT_DXT5:
|
case iwi27::IwiFormat::IMG_FORMAT_DXT5:
|
||||||
return &ImageFormat::FORMAT_BC3;
|
return &format::BC3;
|
||||||
case iwi27::IwiFormat::IMG_FORMAT_DXN:
|
case iwi27::IwiFormat::IMG_FORMAT_DXN:
|
||||||
return &ImageFormat::FORMAT_BC5;
|
return &format::BC5;
|
||||||
case iwi27::IwiFormat::IMG_FORMAT_A16B16G16R16F:
|
case iwi27::IwiFormat::IMG_FORMAT_A16B16G16R16F:
|
||||||
assert(false); // Unsupported yet
|
assert(false); // Unsupported yet
|
||||||
return &ImageFormat::FORMAT_R16_G16_B16_A16_FLOAT;
|
return &format::R16_G16_B16_A16_FLOAT;
|
||||||
case iwi27::IwiFormat::IMG_FORMAT_BITMAP_RGB:
|
case iwi27::IwiFormat::IMG_FORMAT_BITMAP_RGB:
|
||||||
return &ImageFormat::FORMAT_R8_G8_B8;
|
return &format::R8_G8_B8;
|
||||||
case iwi27::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE_ALPHA:
|
case iwi27::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE_ALPHA:
|
||||||
return &ImageFormat::FORMAT_R8_A8;
|
return &format::R8_A8;
|
||||||
case iwi27::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE:
|
case iwi27::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE:
|
||||||
return &ImageFormat::FORMAT_R8;
|
return &format::R8;
|
||||||
case iwi27::IwiFormat::IMG_FORMAT_WAVELET_RGBA:
|
case iwi27::IwiFormat::IMG_FORMAT_WAVELET_RGBA:
|
||||||
case iwi27::IwiFormat::IMG_FORMAT_WAVELET_RGB:
|
case iwi27::IwiFormat::IMG_FORMAT_WAVELET_RGB:
|
||||||
case iwi27::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE_ALPHA:
|
case iwi27::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE_ALPHA:
|
||||||
|
|||||||
@@ -10,36 +10,46 @@ namespace image
|
|||||||
// ================= Texture ====================
|
// ================= Texture ====================
|
||||||
// ==============================================
|
// ==============================================
|
||||||
Texture::Texture(const ImageFormat* format, const bool mipMaps)
|
Texture::Texture(const ImageFormat* format, const bool mipMaps)
|
||||||
|
: m_format(format),
|
||||||
|
m_has_mip_maps(mipMaps)
|
||||||
{
|
{
|
||||||
m_format = format;
|
|
||||||
m_has_mip_maps = mipMaps;
|
|
||||||
m_data = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture::Texture(Texture&& other) noexcept
|
Texture::Texture(Texture&& other) noexcept
|
||||||
|
: m_format(other.m_format),
|
||||||
|
m_has_mip_maps(other.m_has_mip_maps),
|
||||||
|
m_data(std::move(other.m_data))
|
||||||
{
|
{
|
||||||
m_format = other.m_format;
|
|
||||||
m_has_mip_maps = other.m_has_mip_maps;
|
|
||||||
m_data = other.m_data;
|
|
||||||
|
|
||||||
other.m_data = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture& Texture::operator=(Texture&& other) noexcept
|
Texture& Texture::operator=(Texture&& other) noexcept
|
||||||
{
|
{
|
||||||
m_format = other.m_format;
|
m_format = other.m_format;
|
||||||
m_has_mip_maps = other.m_has_mip_maps;
|
m_has_mip_maps = other.m_has_mip_maps;
|
||||||
m_data = other.m_data;
|
m_data = std::move(other.m_data);
|
||||||
|
|
||||||
other.m_data = nullptr;
|
other.m_data = nullptr;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture::~Texture()
|
Texture::~Texture() = default;
|
||||||
|
|
||||||
|
std::unique_ptr<Texture> Texture::CreateForType(
|
||||||
|
const TextureType type, const ImageFormat* format, const unsigned width, const unsigned height, const unsigned depth, const bool mipMaps)
|
||||||
{
|
{
|
||||||
delete[] m_data;
|
switch (type)
|
||||||
m_data = nullptr;
|
{
|
||||||
|
case TextureType::T_2D:
|
||||||
|
return std::make_unique<Texture2D>(format, width, height, mipMaps);
|
||||||
|
case TextureType::T_3D:
|
||||||
|
return std::make_unique<Texture3D>(format, width, height, depth, mipMaps);
|
||||||
|
case TextureType::T_CUBE:
|
||||||
|
return std::make_unique<TextureCube>(format, width, height, mipMaps);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(false);
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ImageFormat* Texture::GetFormat() const
|
const ImageFormat* Texture::GetFormat() const
|
||||||
@@ -59,8 +69,8 @@ namespace image
|
|||||||
|
|
||||||
if (storageRequirement > 0)
|
if (storageRequirement > 0)
|
||||||
{
|
{
|
||||||
m_data = new uint8_t[storageRequirement];
|
m_data = std::make_unique<uint8_t[]>(storageRequirement);
|
||||||
memset(m_data, 0, storageRequirement);
|
memset(m_data.get(), 0, storageRequirement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +228,7 @@ namespace image
|
|||||||
// ==============================================
|
// ==============================================
|
||||||
// =============== TextureCube ==================
|
// =============== TextureCube ==================
|
||||||
// ==============================================
|
// ==============================================
|
||||||
const int TextureCube::FACE_COUNT = 6;
|
static constexpr int FACE_COUNT = 6;
|
||||||
|
|
||||||
TextureCube::TextureCube(const ImageFormat* format, const unsigned width, const unsigned height)
|
TextureCube::TextureCube(const ImageFormat* format, const unsigned width, const unsigned height)
|
||||||
: Texture2D(format, width, height)
|
: Texture2D(format, width, height)
|
||||||
@@ -381,7 +391,7 @@ namespace image
|
|||||||
|
|
||||||
int Texture3D::GetMipMapCount() const
|
int Texture3D::GetMipMapCount() const
|
||||||
{
|
{
|
||||||
unsigned maxDimension = std::max(std::max(m_width, m_height), m_depth);
|
unsigned maxDimension = std::max({m_width, m_height, m_depth});
|
||||||
|
|
||||||
int mipMapCount = 0;
|
int mipMapCount = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ImageFormat.h"
|
#include "ImageFormat.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace image
|
namespace image
|
||||||
{
|
{
|
||||||
@@ -14,20 +16,13 @@ namespace image
|
|||||||
|
|
||||||
class Texture
|
class Texture
|
||||||
{
|
{
|
||||||
protected:
|
|
||||||
const ImageFormat* m_format;
|
|
||||||
bool m_has_mip_maps;
|
|
||||||
uint8_t* m_data;
|
|
||||||
|
|
||||||
Texture(const ImageFormat* format, bool mipMaps);
|
|
||||||
Texture(Texture&& other) noexcept;
|
|
||||||
|
|
||||||
Texture& operator=(Texture&& other) noexcept;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Texture(const Texture& other) = delete;
|
Texture(const Texture& other) = delete;
|
||||||
virtual ~Texture();
|
virtual ~Texture();
|
||||||
|
|
||||||
|
static std::unique_ptr<Texture>
|
||||||
|
CreateForType(TextureType type, const ImageFormat* format, unsigned width, unsigned height, unsigned depth, bool mipMaps);
|
||||||
|
|
||||||
Texture& operator=(const Texture& other) = delete;
|
Texture& operator=(const Texture& other) = delete;
|
||||||
|
|
||||||
[[nodiscard]] virtual TextureType GetTextureType() const = 0;
|
[[nodiscard]] virtual TextureType GetTextureType() const = 0;
|
||||||
@@ -49,14 +44,20 @@ namespace image
|
|||||||
|
|
||||||
[[nodiscard]] bool HasMipMaps() const;
|
[[nodiscard]] bool HasMipMaps() const;
|
||||||
[[nodiscard]] virtual int GetMipMapCount() const = 0;
|
[[nodiscard]] virtual int GetMipMapCount() const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const ImageFormat* m_format;
|
||||||
|
bool m_has_mip_maps;
|
||||||
|
std::unique_ptr<uint8_t[]> m_data;
|
||||||
|
|
||||||
|
Texture(const ImageFormat* format, bool mipMaps);
|
||||||
|
Texture(Texture&& other) noexcept;
|
||||||
|
|
||||||
|
Texture& operator=(Texture&& other) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Texture2D : public Texture
|
class Texture2D : public Texture
|
||||||
{
|
{
|
||||||
protected:
|
|
||||||
unsigned m_width;
|
|
||||||
unsigned m_height;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Texture2D(const ImageFormat* format, unsigned width, unsigned height);
|
Texture2D(const ImageFormat* format, unsigned width, unsigned height);
|
||||||
Texture2D(const ImageFormat* format, unsigned width, unsigned height, bool mipMaps);
|
Texture2D(const ImageFormat* format, unsigned width, unsigned height, bool mipMaps);
|
||||||
@@ -79,12 +80,14 @@ namespace image
|
|||||||
[[nodiscard]] const uint8_t* GetBufferForMipLevel(int mipLevel, int face) const override;
|
[[nodiscard]] const uint8_t* GetBufferForMipLevel(int mipLevel, int face) const override;
|
||||||
|
|
||||||
[[nodiscard]] int GetMipMapCount() const override;
|
[[nodiscard]] int GetMipMapCount() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
unsigned m_width;
|
||||||
|
unsigned m_height;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TextureCube final : public Texture2D
|
class TextureCube final : public Texture2D
|
||||||
{
|
{
|
||||||
static const int FACE_COUNT;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TextureCube(const ImageFormat* format, unsigned width, unsigned height);
|
TextureCube(const ImageFormat* format, unsigned width, unsigned height);
|
||||||
TextureCube(const ImageFormat* format, unsigned width, unsigned height, bool mipMaps);
|
TextureCube(const ImageFormat* format, unsigned width, unsigned height, bool mipMaps);
|
||||||
@@ -105,10 +108,6 @@ namespace image
|
|||||||
|
|
||||||
class Texture3D final : public Texture
|
class Texture3D final : public Texture
|
||||||
{
|
{
|
||||||
unsigned m_width;
|
|
||||||
unsigned m_height;
|
|
||||||
unsigned m_depth;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Texture3D(const ImageFormat* format, unsigned width, unsigned height, unsigned depth);
|
Texture3D(const ImageFormat* format, unsigned width, unsigned height, unsigned depth);
|
||||||
Texture3D(const ImageFormat* format, unsigned width, unsigned height, unsigned depth, bool mipMaps);
|
Texture3D(const ImageFormat* format, unsigned width, unsigned height, unsigned depth, bool mipMaps);
|
||||||
@@ -131,5 +130,10 @@ namespace image
|
|||||||
[[nodiscard]] const uint8_t* GetBufferForMipLevel(int mipLevel, int face) const override;
|
[[nodiscard]] const uint8_t* GetBufferForMipLevel(int mipLevel, int face) const override;
|
||||||
|
|
||||||
[[nodiscard]] int GetMipMapCount() const override;
|
[[nodiscard]] int GetMipMapCount() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned m_width;
|
||||||
|
unsigned m_height;
|
||||||
|
unsigned m_depth;
|
||||||
};
|
};
|
||||||
} // namespace image
|
} // namespace image
|
||||||
|
|||||||
@@ -2,16 +2,24 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
namespace image
|
namespace
|
||||||
{
|
{
|
||||||
constexpr uint64_t TextureConverter::Mask1(const unsigned length)
|
constexpr uint64_t Mask1(const unsigned length)
|
||||||
{
|
{
|
||||||
if (length >= sizeof(uint64_t) * 8)
|
if (length >= sizeof(uint64_t) * 8)
|
||||||
return UINT64_MAX;
|
return std::numeric_limits<uint64_t>::max();
|
||||||
|
|
||||||
return UINT64_MAX >> (sizeof(uint64_t) * 8 - length);
|
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)
|
void TextureConverter::SetPixelFunctions(const unsigned inBitCount, const unsigned outBitCount)
|
||||||
{
|
{
|
||||||
switch (inBitCount)
|
switch (inBitCount)
|
||||||
@@ -150,10 +158,13 @@ namespace image
|
|||||||
const auto gInputMask = inputFormat->HasG() ? Mask1(inputFormat->m_g_size) << inputFormat->m_g_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 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 aInputMask = inputFormat->HasA() ? Mask1(inputFormat->m_a_size) << inputFormat->m_a_offset : 0;
|
||||||
|
const auto aOutputMask = outputFormat->HasA() ? Mask1(outputFormat->m_a_size) << outputFormat->m_a_offset : 0;
|
||||||
const bool rConvert = rInputMask != 0 && outputFormat->m_r_size > 0;
|
const bool rConvert = rInputMask != 0 && outputFormat->m_r_size > 0;
|
||||||
const bool gConvert = gInputMask != 0 && outputFormat->m_g_size > 0;
|
const bool gConvert = gInputMask != 0 && outputFormat->m_g_size > 0;
|
||||||
const bool bConvert = bInputMask != 0 && outputFormat->m_b_size > 0;
|
const bool bConvert = bInputMask != 0 && outputFormat->m_b_size > 0;
|
||||||
const bool aConvert = aInputMask != 0 && outputFormat->m_a_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++)
|
for (auto mipLevel = 0; mipLevel < mipCount; mipLevel++)
|
||||||
{
|
{
|
||||||
@@ -176,8 +187,13 @@ namespace image
|
|||||||
outPixel |= (inPixel & gInputMask) >> inputFormat->m_g_offset << outputFormat->m_g_offset;
|
outPixel |= (inPixel & gInputMask) >> inputFormat->m_g_offset << outputFormat->m_g_offset;
|
||||||
if (bConvert)
|
if (bConvert)
|
||||||
outPixel |= (inPixel & bInputMask) >> inputFormat->m_b_offset << outputFormat->m_b_offset;
|
outPixel |= (inPixel & bInputMask) >> inputFormat->m_b_offset << outputFormat->m_b_offset;
|
||||||
|
|
||||||
if (aConvert)
|
if (aConvert)
|
||||||
outPixel |= (inPixel & aInputMask) >> inputFormat->m_a_offset << outputFormat->m_a_offset;
|
{
|
||||||
|
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);
|
m_write_pixel_func(&outputBuffer[outputOffset], outPixel, outputFormat->m_bits_per_pixel);
|
||||||
}
|
}
|
||||||
@@ -194,8 +210,8 @@ namespace image
|
|||||||
|
|
||||||
SetPixelFunctions(inputFormat->m_bits_per_pixel, outputFormat->m_bits_per_pixel);
|
SetPixelFunctions(inputFormat->m_bits_per_pixel, outputFormat->m_bits_per_pixel);
|
||||||
|
|
||||||
if (inputFormat->m_r_size == outputFormat->m_r_size && inputFormat->m_g_size == outputFormat->m_g_size
|
if (CanReorder(inputFormat->m_r_size, outputFormat->m_r_size) && CanReorder(inputFormat->m_g_size, outputFormat->m_g_size)
|
||||||
&& inputFormat->m_b_size == outputFormat->m_b_size && inputFormat->m_a_size == outputFormat->m_a_size)
|
&& CanReorder(inputFormat->m_b_size, outputFormat->m_b_size) && CanReorder(inputFormat->m_a_size, outputFormat->m_a_size))
|
||||||
{
|
{
|
||||||
ReorderUnsignedToUnsigned();
|
ReorderUnsignedToUnsigned();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ namespace image
|
|||||||
std::unique_ptr<Texture> Convert();
|
std::unique_ptr<Texture> Convert();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr uint64_t Mask1(unsigned length);
|
|
||||||
void SetPixelFunctions(unsigned inBitCount, unsigned outBitCount);
|
void SetPixelFunctions(unsigned inBitCount, unsigned outBitCount);
|
||||||
|
|
||||||
void CreateOutputTexture();
|
void CreateOutputTexture();
|
||||||
|
|||||||
@@ -100,16 +100,20 @@ namespace image
|
|||||||
void CLASS_NAME::DumpAsset(AssetDumpingContext& context, const XAssetInfo<AssetImage::Type>& asset)
|
void CLASS_NAME::DumpAsset(AssetDumpingContext& context, const XAssetInfo<AssetImage::Type>& asset)
|
||||||
{
|
{
|
||||||
const auto* image = asset.Asset();
|
const auto* image = asset.Asset();
|
||||||
const auto texture = CONVERTER_NAME().Convert(asset, context.m_obj_search_path);
|
auto texture = CONVERTER_NAME().Convert(asset, context.m_obj_search_path);
|
||||||
if (!texture)
|
if (!texture)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
auto convertedTexture = ConvertTextureForDdsFileOutputIfNecessary(texture.get());
|
||||||
|
if (convertedTexture)
|
||||||
|
texture = std::move(convertedTexture);
|
||||||
|
|
||||||
if (!m_writer->SupportsImageFormat(texture->GetFormat()))
|
if (!m_writer->SupportsImageFormat(texture->GetFormat()))
|
||||||
{
|
{
|
||||||
con::warn("Not dumping image {} as {} does not support the image format {}",
|
con::warn("Not dumping image {} as {} does not support the image format {}",
|
||||||
image->name,
|
image->name,
|
||||||
GetImageOutputFormatName(ObjWriting::Configuration.ImageOutputFormat),
|
GetImageOutputFormatName(ObjWriting::Configuration.ImageOutputFormat),
|
||||||
GetImageFormatName(texture->GetFormat()->GetId()));
|
texture->GetFormat()->GetName());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ namespace image::image_format
|
|||||||
{
|
{
|
||||||
TEST_CASE("ImageFormat: EnsureAllFormatsArrayIndicesAreIds", "[image]")
|
TEST_CASE("ImageFormat: EnsureAllFormatsArrayIndicesAreIds", "[image]")
|
||||||
{
|
{
|
||||||
REQUIRE(static_cast<unsigned int>(ImageFormatId::MAX) == std::extent_v<decltype(ImageFormat::ALL_FORMATS)>);
|
REQUIRE(static_cast<unsigned int>(ImageFormatId::MAX) == std::extent_v<decltype(format::ALL)>);
|
||||||
|
|
||||||
for (unsigned i = 0; i < std::extent_v<decltype(ImageFormat::ALL_FORMATS)>; i++)
|
for (unsigned i = 0; i < std::extent_v<decltype(format::ALL)>; i++)
|
||||||
{
|
{
|
||||||
REQUIRE(ImageFormat::ALL_FORMATS[i] != nullptr);
|
REQUIRE(format::ALL[i] != nullptr);
|
||||||
REQUIRE(i == static_cast<unsigned>(ImageFormat::ALL_FORMATS[i]->GetId()));
|
REQUIRE(i == static_cast<unsigned>(format::ALL[i]->GetId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace image::image_format
|
} // namespace image::image_format
|
||||||
|
|||||||
Reference in New Issue
Block a user