Files
armorpaint/paint/plugins/io_dds/tinyddsloader.h
T
luboslenco 3dbdf2b3ed Cleanup
2025-08-07 14:58:38 +02:00

1515 lines
53 KiB
C

//
// Copyright(c) 2019 benikabocha.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#ifndef TINYDDSLOADER_H_
#define TINYDDSLOADER_H_
#include <iostream>
#include <vector>
namespace tinyddsloader {
enum Result {
Success,
ErrorFileOpen,
ErrorRead,
ErrorMagicWord,
ErrorSize,
ErrorVerify,
ErrorNotSupported,
ErrorInvalidData,
};
class DDSFile {
public:
static const char Magic[4];
enum class PixelFormatFlagBits : uint32_t {
FourCC = 0x00000004,
RGB = 0x00000040,
RGBA = 0x00000041,
Luminance = 0x00020000,
LuminanceA = 0x00020001,
AlphaPixels = 0x00000001,
Alpha = 0x00000002,
Palette8 = 0x00000020,
Palette8A = 0x00000021,
BumpDUDV = 0x00080000
};
enum class DXGIFormat : uint32_t {
Unknown = 0,
R32G32B32A32_Typeless = 1,
R32G32B32A32_Float = 2,
R32G32B32A32_UInt = 3,
R32G32B32A32_SInt = 4,
R32G32B32_Typeless = 5,
R32G32B32_Float = 6,
R32G32B32_UInt = 7,
R32G32B32_SInt = 8,
R16G16B16A16_Typeless = 9,
R16G16B16A16_Float = 10,
R16G16B16A16_UNorm = 11,
R16G16B16A16_UInt = 12,
R16G16B16A16_SNorm = 13,
R16G16B16A16_SInt = 14,
R32G32_Typeless = 15,
R32G32_Float = 16,
R32G32_UInt = 17,
R32G32_SInt = 18,
R32G8X24_Typeless = 19,
D32_Float_S8X24_UInt = 20,
R32_Float_X8X24_Typeless = 21,
X32_Typeless_G8X24_UInt = 22,
R10G10B10A2_Typeless = 23,
R10G10B10A2_UNorm = 24,
R10G10B10A2_UInt = 25,
R11G11B10_Float = 26,
R8G8B8A8_Typeless = 27,
R8G8B8A8_UNorm = 28,
R8G8B8A8_UNorm_SRGB = 29,
R8G8B8A8_UInt = 30,
R8G8B8A8_SNorm = 31,
R8G8B8A8_SInt = 32,
R16G16_Typeless = 33,
R16G16_Float = 34,
R16G16_UNorm = 35,
R16G16_UInt = 36,
R16G16_SNorm = 37,
R16G16_SInt = 38,
R32_Typeless = 39,
D32_Float = 40,
R32_Float = 41,
R32_UInt = 42,
R32_SInt = 43,
R24G8_Typeless = 44,
D24_UNorm_S8_UInt = 45,
R24_UNorm_X8_Typeless = 46,
X24_Typeless_G8_UInt = 47,
R8G8_Typeless = 48,
R8G8_UNorm = 49,
R8G8_UInt = 50,
R8G8_SNorm = 51,
R8G8_SInt = 52,
R16_Typeless = 53,
R16_Float = 54,
D16_UNorm = 55,
R16_UNorm = 56,
R16_UInt = 57,
R16_SNorm = 58,
R16_SInt = 59,
R8_Typeless = 60,
R8_UNorm = 61,
R8_UInt = 62,
R8_SNorm = 63,
R8_SInt = 64,
A8_UNorm = 65,
R1_UNorm = 66,
R9G9B9E5_SHAREDEXP = 67,
R8G8_B8G8_UNorm = 68,
G8R8_G8B8_UNorm = 69,
BC1_Typeless = 70,
BC1_UNorm = 71,
BC1_UNorm_SRGB = 72,
BC2_Typeless = 73,
BC2_UNorm = 74,
BC2_UNorm_SRGB = 75,
BC3_Typeless = 76,
BC3_UNorm = 77,
BC3_UNorm_SRGB = 78,
BC4_Typeless = 79,
BC4_UNorm = 80,
BC4_SNorm = 81,
BC5_Typeless = 82,
BC5_UNorm = 83,
BC5_SNorm = 84,
B5G6R5_UNorm = 85,
B5G5R5A1_UNorm = 86,
B8G8R8A8_UNorm = 87,
B8G8R8X8_UNorm = 88,
R10G10B10_XR_BIAS_A2_UNorm = 89,
B8G8R8A8_Typeless = 90,
B8G8R8A8_UNorm_SRGB = 91,
B8G8R8X8_Typeless = 92,
B8G8R8X8_UNorm_SRGB = 93,
BC6H_Typeless = 94,
BC6H_UF16 = 95,
BC6H_SF16 = 96,
BC7_Typeless = 97,
BC7_UNorm = 98,
BC7_UNorm_SRGB = 99,
AYUV = 100,
Y410 = 101,
Y416 = 102,
NV12 = 103,
P010 = 104,
P016 = 105,
YUV420_OPAQUE = 106,
YUY2 = 107,
Y210 = 108,
Y216 = 109,
NV11 = 110,
AI44 = 111,
IA44 = 112,
P8 = 113,
A8P8 = 114,
B4G4R4A4_UNorm = 115,
P208 = 130,
V208 = 131,
V408 = 132,
};
enum class HeaderFlagBits : uint32_t {
Height = 0x00000002,
Width = 0x00000004,
Texture = 0x00001007,
Mipmap = 0x00020000,
Volume = 0x00800000,
Pitch = 0x00000008,
LinearSize = 0x00080000,
};
enum class HeaderCaps2FlagBits : uint32_t {
CubemapPositiveX = 0x00000600,
CubemapNegativeX = 0x00000a00,
CubemapPositiveY = 0x00001200,
CubemapNegativeY = 0x00002200,
CubemapPositiveZ = 0x00004200,
CubemapNegativeZ = 0x00008200,
CubemapAllFaces = CubemapPositiveX | CubemapNegativeX |
CubemapPositiveY | CubemapNegativeY |
CubemapPositiveZ | CubemapNegativeZ,
Volume = 0x00200000,
};
struct PixelFormat {
uint32_t m_size;
uint32_t m_flags;
uint32_t m_fourCC;
uint32_t m_bitCount;
uint32_t m_RBitMask;
uint32_t m_GBitMask;
uint32_t m_BBitMask;
uint32_t m_ABitMask;
};
struct Header {
uint32_t m_size;
uint32_t m_flags;
uint32_t m_height;
uint32_t m_width;
uint32_t m_pitchOrLinerSize;
uint32_t m_depth;
uint32_t m_mipMapCount;
uint32_t m_reserved1[11];
PixelFormat m_pixelFormat;
uint32_t m_caps;
uint32_t m_caps2;
uint32_t m_caps3;
uint32_t m_caps4;
uint32_t m_reserved2;
};
enum class TextureDimension : uint32_t {
Unknown = 0,
Texture1D = 2,
Texture2D = 3,
Texture3D = 4
};
enum class DXT10MiscFlagBits : uint32_t { TextureCube = 0x4 };
struct HeaderDXT10 {
DXGIFormat m_format;
TextureDimension m_resourceDimension;
uint32_t m_miscFlag;
uint32_t m_arraySize;
uint32_t m_miscFlag2;
};
struct ImageData {
uint32_t m_width;
uint32_t m_height;
uint32_t m_depth;
void* m_mem;
uint32_t m_memPitch;
uint32_t m_memSlicePitch;
};
struct BC1Block {
uint16_t m_color0;
uint16_t m_color1;
uint8_t m_row0;
uint8_t m_row1;
uint8_t m_row2;
uint8_t m_row3;
};
struct BC2Block {
uint16_t m_alphaRow0;
uint16_t m_alphaRow1;
uint16_t m_alphaRow2;
uint16_t m_alphaRow3;
uint16_t m_color0;
uint16_t m_color1;
uint8_t m_row0;
uint8_t m_row1;
uint8_t m_row2;
uint8_t m_row3;
};
struct BC3Block {
uint8_t m_alpha0;
uint8_t m_alpha1;
uint8_t m_alphaR0;
uint8_t m_alphaR1;
uint8_t m_alphaR2;
uint8_t m_alphaR3;
uint8_t m_alphaR4;
uint8_t m_alphaR5;
uint16_t m_color0;
uint16_t m_color1;
uint8_t m_row0;
uint8_t m_row1;
uint8_t m_row2;
uint8_t m_row3;
};
struct BC4Block {
uint8_t m_red0;
uint8_t m_red1;
uint8_t m_redR0;
uint8_t m_redR1;
uint8_t m_redR2;
uint8_t m_redR3;
uint8_t m_redR4;
uint8_t m_redR5;
};
struct BC5Block {
uint8_t m_red0;
uint8_t m_red1;
uint8_t m_redR0;
uint8_t m_redR1;
uint8_t m_redR2;
uint8_t m_redR3;
uint8_t m_redR4;
uint8_t m_redR5;
uint8_t m_green0;
uint8_t m_green1;
uint8_t m_greenR0;
uint8_t m_greenR1;
uint8_t m_greenR2;
uint8_t m_greenR3;
uint8_t m_greenR4;
uint8_t m_greenR5;
};
public:
static bool IsCompressed(DXGIFormat fmt);
static uint32_t MakeFourCC(char ch0, char ch1, char ch2, char ch3);
static DXGIFormat GetDXGIFormat(const PixelFormat& pf);
static uint32_t GetBitsPerPixel(DXGIFormat fmt);
Result Load(const char* filepath);
Result Load(std::istream& input);
Result Load(const uint8_t* data, size_t size);
Result Load(std::vector<uint8_t>&& dds);
const ImageData* GetImageData(uint32_t mipIdx = 0,
uint32_t arrayIdx = 0) const {
if (mipIdx < m_mipCount && arrayIdx < m_arraySize) {
return &m_imageDatas[m_mipCount * arrayIdx + mipIdx];
}
return nullptr;
}
bool Flip();
uint32_t GetWidth() const { return m_width; }
uint32_t GetHeight() const { return m_height; }
uint32_t GetDepth() const { return m_depth; }
uint32_t GetMipCount() const { return m_mipCount; }
uint32_t GetArraySize() const { return m_arraySize; }
DXGIFormat GetFormat() const { return m_format; }
bool IsCubemap() const { return m_isCubemap; }
TextureDimension GetTextureDimension() const { return m_texDim; }
private:
void GetImageInfo(uint32_t w, uint32_t h, DXGIFormat fmt,
uint32_t* outNumBytes, uint32_t* outRowBytes,
uint32_t* outNumRows);
bool FlipImage(ImageData& imageData);
bool FlipCompressedImage(ImageData& imageData);
void FlipCompressedImageBC1(ImageData& imageData);
void FlipCompressedImageBC2(ImageData& imageData);
void FlipCompressedImageBC3(ImageData& imageData);
void FlipCompressedImageBC4(ImageData& imageData);
void FlipCompressedImageBC5(ImageData& imageData);
private:
std::vector<uint8_t> m_dds;
std::vector<ImageData> m_imageDatas;
uint32_t m_height;
uint32_t m_width;
uint32_t m_depth;
uint32_t m_mipCount;
uint32_t m_arraySize;
DXGIFormat m_format;
bool m_isCubemap;
TextureDimension m_texDim;
};
} // namespace tinyddsloader
#ifdef TINYDDSLOADER_IMPLEMENTATION
#if _WIN32
#undef min
#undef max
#endif // _Win32
#include <algorithm>
#include <fstream>
namespace tinyddsloader {
const char DDSFile::Magic[4] = {'D', 'D', 'S', ' '};
bool DDSFile::IsCompressed(DXGIFormat fmt) {
switch (fmt) {
case DXGIFormat::BC1_Typeless:
case DXGIFormat::BC1_UNorm:
case DXGIFormat::BC1_UNorm_SRGB:
case DXGIFormat::BC2_Typeless:
case DXGIFormat::BC2_UNorm:
case DXGIFormat::BC2_UNorm_SRGB:
case DXGIFormat::BC3_Typeless:
case DXGIFormat::BC3_UNorm:
case DXGIFormat::BC3_UNorm_SRGB:
case DXGIFormat::BC4_Typeless:
case DXGIFormat::BC4_UNorm:
case DXGIFormat::BC4_SNorm:
case DXGIFormat::BC5_Typeless:
case DXGIFormat::BC5_UNorm:
case DXGIFormat::BC5_SNorm:
case DXGIFormat::BC6H_Typeless:
case DXGIFormat::BC6H_UF16:
case DXGIFormat::BC6H_SF16:
case DXGIFormat::BC7_Typeless:
case DXGIFormat::BC7_UNorm:
case DXGIFormat::BC7_UNorm_SRGB:
return true;
}
return false;
}
uint32_t DDSFile::MakeFourCC(char ch0, char ch1, char ch2, char ch3) {
return (uint32_t(uint8_t(ch0)) | (uint32_t(uint8_t(ch1)) << 8) |
(uint32_t(uint8_t(ch2)) << 16) | (uint32_t(uint8_t(ch3)) << 24));
}
DDSFile::DXGIFormat DDSFile::GetDXGIFormat(const PixelFormat& pf) {
if (pf.m_flags & uint32_t(PixelFormatFlagBits::RGB)) {
switch (pf.m_bitCount) {
case 32:
if (pf.m_RBitMask == 0x000000ff &&
pf.m_GBitMask == 0x0000ff00 &&
pf.m_BBitMask == 0x00ff0000 &&
pf.m_ABitMask == 0xff000000) {
return DXGIFormat::R8G8B8A8_UNorm;
}
if (pf.m_RBitMask == 0x00ff0000 &&
pf.m_GBitMask == 0x0000ff00 &&
pf.m_BBitMask == 0x000000ff &&
pf.m_ABitMask == 0xff000000) {
return DXGIFormat::B8G8R8A8_UNorm;
}
if (pf.m_RBitMask == 0x00ff0000 &&
pf.m_GBitMask == 0x0000ff00 &&
pf.m_BBitMask == 0x000000ff &&
pf.m_ABitMask == 0x00000000) {
return DXGIFormat::B8G8R8X8_UNorm;
}
if (pf.m_RBitMask == 0x0000ffff &&
pf.m_GBitMask == 0xffff0000 &&
pf.m_BBitMask == 0x00000000 &&
pf.m_ABitMask == 0x00000000) {
return DXGIFormat::R16G16_UNorm;
}
if (pf.m_RBitMask == 0xffffffff &&
pf.m_GBitMask == 0x00000000 &&
pf.m_BBitMask == 0x00000000 &&
pf.m_ABitMask == 0x00000000) {
return DXGIFormat::R32_Float;
}
break;
case 24:
break;
case 16:
if (pf.m_RBitMask == 0x7c00 && pf.m_GBitMask == 0x03e0 &&
pf.m_BBitMask == 0x001f && pf.m_ABitMask == 0x8000) {
return DXGIFormat::B5G5R5A1_UNorm;
}
if (pf.m_RBitMask == 0xf800 && pf.m_GBitMask == 0x07e0 &&
pf.m_BBitMask == 0x001f && pf.m_ABitMask == 0x0000) {
return DXGIFormat::B5G6R5_UNorm;
}
if (pf.m_RBitMask == 0x0f00 && pf.m_GBitMask == 0x00f0 &&
pf.m_BBitMask == 0x000f && pf.m_ABitMask == 0xf000) {
return DXGIFormat::B4G4R4A4_UNorm;
}
break;
default:
break;
}
} else if (pf.m_flags & uint32_t(PixelFormatFlagBits::Luminance)) {
if (8 == pf.m_bitCount) {
if (pf.m_RBitMask == 0x000000ff && pf.m_GBitMask == 0x00000000 &&
pf.m_BBitMask == 0x00000000 && pf.m_ABitMask == 0x00000000) {
return DXGIFormat::R8_UNorm;
}
if (pf.m_RBitMask == 0x000000ff && pf.m_GBitMask == 0x0000ff00 &&
pf.m_BBitMask == 0x00000000 && pf.m_ABitMask == 0x00000000) {
return DXGIFormat::R8G8_UNorm;
}
}
if (16 == pf.m_bitCount) {
if (pf.m_RBitMask == 0x0000ffff && pf.m_GBitMask == 0x00000000 &&
pf.m_BBitMask == 0x00000000 && pf.m_ABitMask == 0x00000000) {
return DXGIFormat::R16_UNorm;
}
if (pf.m_RBitMask == 0x000000ff && pf.m_GBitMask == 0x0000ff00 &&
pf.m_BBitMask == 0x00000000 && pf.m_ABitMask == 0x00000000) {
return DXGIFormat::R8G8_UNorm;
}
}
} else if (pf.m_flags & uint32_t(PixelFormatFlagBits::Alpha)) {
if (8 == pf.m_bitCount) {
return DXGIFormat::A8_UNorm;
}
} else if (pf.m_flags & uint32_t(PixelFormatFlagBits::BumpDUDV)) {
if (16 == pf.m_bitCount) {
if (pf.m_RBitMask == 0x00ff && pf.m_GBitMask == 0xff00 &&
pf.m_BBitMask == 0x0000 && pf.m_ABitMask == 0x0000) {
return DXGIFormat::R8G8_SNorm;
}
}
if (32 == pf.m_bitCount) {
if (pf.m_RBitMask == 0x000000ff && pf.m_GBitMask == 0x0000ff00 &&
pf.m_BBitMask == 0x00ff0000 && pf.m_ABitMask == 0xff000000) {
return DXGIFormat::R8G8B8A8_SNorm;
}
if (pf.m_RBitMask == 0x0000ffff && pf.m_GBitMask == 0xffff0000 &&
pf.m_BBitMask == 0x00000000 && pf.m_ABitMask == 0x00000000) {
return DXGIFormat::R16G16_SNorm;
}
}
} else if (pf.m_flags & uint32_t(PixelFormatFlagBits::FourCC)) {
if (MakeFourCC('D', 'X', 'T', '1') == pf.m_fourCC) {
return DXGIFormat::BC1_UNorm;
}
if (MakeFourCC('D', 'X', 'T', '3') == pf.m_fourCC) {
return DXGIFormat::BC2_UNorm;
}
if (MakeFourCC('D', 'X', 'T', '5') == pf.m_fourCC) {
return DXGIFormat::BC3_UNorm;
}
if (MakeFourCC('D', 'X', 'T', '4') == pf.m_fourCC) {
return DXGIFormat::BC2_UNorm;
}
if (MakeFourCC('D', 'X', 'T', '5') == pf.m_fourCC) {
return DXGIFormat::BC3_UNorm;
}
if (MakeFourCC('A', 'T', 'I', '1') == pf.m_fourCC) {
return DXGIFormat::BC4_UNorm;
}
if (MakeFourCC('B', 'C', '4', 'U') == pf.m_fourCC) {
return DXGIFormat::BC4_UNorm;
}
if (MakeFourCC('B', 'C', '4', 'S') == pf.m_fourCC) {
return DXGIFormat::BC4_SNorm;
}
if (MakeFourCC('A', 'T', 'I', '2') == pf.m_fourCC) {
return DXGIFormat::BC5_UNorm;
}
if (MakeFourCC('B', 'C', '5', 'U') == pf.m_fourCC) {
return DXGIFormat::BC5_UNorm;
}
if (MakeFourCC('B', 'C', '5', 'S') == pf.m_fourCC) {
return DXGIFormat::BC5_SNorm;
}
if (MakeFourCC('R', 'G', 'B', 'G') == pf.m_fourCC) {
return DXGIFormat::R8G8_B8G8_UNorm;
}
if (MakeFourCC('G', 'R', 'G', 'B') == pf.m_fourCC) {
return DXGIFormat::G8R8_G8B8_UNorm;
}
if (MakeFourCC('Y', 'U', 'Y', '2') == pf.m_fourCC) {
return DXGIFormat::YUY2;
}
switch (pf.m_fourCC) {
case 36:
return DXGIFormat::R16G16B16A16_UNorm;
case 110:
return DXGIFormat::R16G16B16A16_SNorm;
case 111:
return DXGIFormat::R16_Float;
case 112:
return DXGIFormat::R16G16_Float;
case 113:
return DXGIFormat::R16G16B16A16_Float;
case 114:
return DXGIFormat::R32_Float;
case 115:
return DXGIFormat::R32G32_Float;
case 116:
return DXGIFormat::R32G32B32A32_Float;
}
}
return DXGIFormat::Unknown;
}
uint32_t DDSFile::GetBitsPerPixel(DXGIFormat fmt) {
switch (fmt) {
case DXGIFormat::R32G32B32A32_Typeless:
case DXGIFormat::R32G32B32A32_Float:
case DXGIFormat::R32G32B32A32_UInt:
case DXGIFormat::R32G32B32A32_SInt:
return 128;
case DXGIFormat::R32G32B32_Typeless:
case DXGIFormat::R32G32B32_Float:
case DXGIFormat::R32G32B32_UInt:
case DXGIFormat::R32G32B32_SInt:
return 96;
case DXGIFormat::R16G16B16A16_Typeless:
case DXGIFormat::R16G16B16A16_Float:
case DXGIFormat::R16G16B16A16_UNorm:
case DXGIFormat::R16G16B16A16_UInt:
case DXGIFormat::R16G16B16A16_SNorm:
case DXGIFormat::R16G16B16A16_SInt:
case DXGIFormat::R32G32_Typeless:
case DXGIFormat::R32G32_Float:
case DXGIFormat::R32G32_UInt:
case DXGIFormat::R32G32_SInt:
case DXGIFormat::R32G8X24_Typeless:
case DXGIFormat::D32_Float_S8X24_UInt:
case DXGIFormat::R32_Float_X8X24_Typeless:
case DXGIFormat::X32_Typeless_G8X24_UInt:
case DXGIFormat::Y416:
case DXGIFormat::Y210:
case DXGIFormat::Y216:
return 64;
case DXGIFormat::R10G10B10A2_Typeless:
case DXGIFormat::R10G10B10A2_UNorm:
case DXGIFormat::R10G10B10A2_UInt:
case DXGIFormat::R11G11B10_Float:
case DXGIFormat::R8G8B8A8_Typeless:
case DXGIFormat::R8G8B8A8_UNorm:
case DXGIFormat::R8G8B8A8_UNorm_SRGB:
case DXGIFormat::R8G8B8A8_UInt:
case DXGIFormat::R8G8B8A8_SNorm:
case DXGIFormat::R8G8B8A8_SInt:
case DXGIFormat::R16G16_Typeless:
case DXGIFormat::R16G16_Float:
case DXGIFormat::R16G16_UNorm:
case DXGIFormat::R16G16_UInt:
case DXGIFormat::R16G16_SNorm:
case DXGIFormat::R16G16_SInt:
case DXGIFormat::R32_Typeless:
case DXGIFormat::D32_Float:
case DXGIFormat::R32_Float:
case DXGIFormat::R32_UInt:
case DXGIFormat::R32_SInt:
case DXGIFormat::R24G8_Typeless:
case DXGIFormat::D24_UNorm_S8_UInt:
case DXGIFormat::R24_UNorm_X8_Typeless:
case DXGIFormat::X24_Typeless_G8_UInt:
case DXGIFormat::R9G9B9E5_SHAREDEXP:
case DXGIFormat::R8G8_B8G8_UNorm:
case DXGIFormat::G8R8_G8B8_UNorm:
case DXGIFormat::B8G8R8A8_UNorm:
case DXGIFormat::B8G8R8X8_UNorm:
case DXGIFormat::R10G10B10_XR_BIAS_A2_UNorm:
case DXGIFormat::B8G8R8A8_Typeless:
case DXGIFormat::B8G8R8A8_UNorm_SRGB:
case DXGIFormat::B8G8R8X8_Typeless:
case DXGIFormat::B8G8R8X8_UNorm_SRGB:
case DXGIFormat::AYUV:
case DXGIFormat::Y410:
case DXGIFormat::YUY2:
return 32;
case DXGIFormat::P010:
case DXGIFormat::P016:
return 24;
case DXGIFormat::R8G8_Typeless:
case DXGIFormat::R8G8_UNorm:
case DXGIFormat::R8G8_UInt:
case DXGIFormat::R8G8_SNorm:
case DXGIFormat::R8G8_SInt:
case DXGIFormat::R16_Typeless:
case DXGIFormat::R16_Float:
case DXGIFormat::D16_UNorm:
case DXGIFormat::R16_UNorm:
case DXGIFormat::R16_UInt:
case DXGIFormat::R16_SNorm:
case DXGIFormat::R16_SInt:
case DXGIFormat::B5G6R5_UNorm:
case DXGIFormat::B5G5R5A1_UNorm:
case DXGIFormat::A8P8:
case DXGIFormat::B4G4R4A4_UNorm:
return 16;
case DXGIFormat::NV12:
case DXGIFormat::YUV420_OPAQUE:
case DXGIFormat::NV11:
return 12;
case DXGIFormat::R8_Typeless:
case DXGIFormat::R8_UNorm:
case DXGIFormat::R8_UInt:
case DXGIFormat::R8_SNorm:
case DXGIFormat::R8_SInt:
case DXGIFormat::A8_UNorm:
case DXGIFormat::AI44:
case DXGIFormat::IA44:
case DXGIFormat::P8:
return 8;
case DXGIFormat::R1_UNorm:
return 1;
case DXGIFormat::BC1_Typeless:
case DXGIFormat::BC1_UNorm:
case DXGIFormat::BC1_UNorm_SRGB:
case DXGIFormat::BC4_Typeless:
case DXGIFormat::BC4_UNorm:
case DXGIFormat::BC4_SNorm:
return 4;
case DXGIFormat::BC2_Typeless:
case DXGIFormat::BC2_UNorm:
case DXGIFormat::BC2_UNorm_SRGB:
case DXGIFormat::BC3_Typeless:
case DXGIFormat::BC3_UNorm:
case DXGIFormat::BC3_UNorm_SRGB:
case DXGIFormat::BC5_Typeless:
case DXGIFormat::BC5_UNorm:
case DXGIFormat::BC5_SNorm:
case DXGIFormat::BC6H_Typeless:
case DXGIFormat::BC6H_UF16:
case DXGIFormat::BC6H_SF16:
case DXGIFormat::BC7_Typeless:
case DXGIFormat::BC7_UNorm:
case DXGIFormat::BC7_UNorm_SRGB:
return 8;
default:
return 0;
}
}
Result DDSFile::Load(const char* filepath) {
std::ifstream ifs(filepath, std::ios_base::binary);
if (!ifs.is_open()) {
return Result::ErrorFileOpen;
}
return Load(ifs);
}
Result DDSFile::Load(std::istream& input) {
m_dds.clear();
input.seekg(0, std::ios_base::beg);
auto begPos = input.tellg();
input.seekg(0, std::ios_base::end);
auto endPos = input.tellg();
input.seekg(0, std::ios_base::beg);
auto fileSize = endPos - begPos;
if (fileSize == 0) {
return Result::ErrorRead;
}
std::vector<uint8_t> dds(fileSize);
input.read(reinterpret_cast<char*>(dds.data()), fileSize);
if (input.bad()) {
return Result::ErrorRead;
}
return Load(std::move(dds));
}
Result DDSFile::Load(const uint8_t* data, size_t size) {
std::vector<uint8_t> dds(data, data + size);
return Load(std::move(dds));
}
Result DDSFile::Load(std::vector<uint8_t>&& dds) {
m_dds.clear();
if (dds.size() < 4) {
return Result::ErrorSize;
}
for (int i = 0; i < 4; i++) {
if (dds[i] != Magic[i]) {
return Result::ErrorMagicWord;
}
}
if ((sizeof(uint32_t) + sizeof(Header)) >= dds.size()) {
return Result::ErrorSize;
}
auto header =
reinterpret_cast<const Header*>(dds.data() + sizeof(uint32_t));
if (header->m_size != sizeof(Header) ||
header->m_pixelFormat.m_size != sizeof(PixelFormat)) {
return Result::ErrorVerify;
}
bool dxt10Header = false;
if ((header->m_pixelFormat.m_flags &
uint32_t(PixelFormatFlagBits::FourCC)) &&
(MakeFourCC('D', 'X', '1', '0') == header->m_pixelFormat.m_fourCC)) {
if ((sizeof(uint32_t) + sizeof(Header) + sizeof(HeaderDXT10)) >=
dds.size()) {
return Result::ErrorSize;
}
dxt10Header = true;
}
ptrdiff_t offset = sizeof(uint32_t) + sizeof(Header) +
(dxt10Header ? sizeof(HeaderDXT10) : 0);
m_height = header->m_height;
m_width = header->m_width;
m_texDim = TextureDimension::Unknown;
m_arraySize = 1;
m_format = DXGIFormat::Unknown;
m_isCubemap = false;
m_mipCount = header->m_mipMapCount;
if (0 == m_mipCount) {
m_mipCount = 1;
}
if (dxt10Header) {
auto dxt10Header = reinterpret_cast<const HeaderDXT10*>(
reinterpret_cast<const char*>(header) + sizeof(Header));
m_arraySize = dxt10Header->m_arraySize;
if (m_arraySize == 0) {
return Result::ErrorInvalidData;
}
switch (dxt10Header->m_format) {
case DXGIFormat::AI44:
case DXGIFormat::IA44:
case DXGIFormat::P8:
case DXGIFormat::A8P8:
return Result::ErrorNotSupported;
default:
if (GetBitsPerPixel(dxt10Header->m_format) == 0) {
return Result::ErrorNotSupported;
}
}
m_format = dxt10Header->m_format;
switch (dxt10Header->m_resourceDimension) {
case TextureDimension::Texture1D:
if ((header->m_flags & uint32_t(HeaderFlagBits::Height) &&
(m_height != 1))) {
return Result::ErrorInvalidData;
}
m_height = m_depth = 1;
break;
case TextureDimension::Texture2D:
if (dxt10Header->m_miscFlag &
uint32_t(DXT10MiscFlagBits::TextureCube)) {
m_arraySize *= 6;
m_isCubemap = true;
}
m_depth = 1;
break;
case TextureDimension::Texture3D:
if (!(header->m_flags & uint32_t(HeaderFlagBits::Volume))) {
return Result::ErrorInvalidData;
}
if (m_arraySize > 1) {
return Result::ErrorNotSupported;
}
break;
default:
return Result::ErrorNotSupported;
}
m_texDim = dxt10Header->m_resourceDimension;
} else {
m_format = GetDXGIFormat(header->m_pixelFormat);
if (m_format == DXGIFormat::Unknown) {
return Result::ErrorNotSupported;
}
if (header->m_flags & uint32_t(HeaderFlagBits::Volume)) {
m_texDim = TextureDimension::Texture3D;
} else {
auto caps2 = header->m_caps2 &
uint32_t(HeaderCaps2FlagBits::CubemapAllFaces);
if (caps2) {
if (caps2 != uint32_t(HeaderCaps2FlagBits::CubemapAllFaces)) {
return Result::ErrorNotSupported;
}
m_arraySize = 6;
m_isCubemap = true;
}
m_depth = 1;
m_texDim = TextureDimension::Texture2D;
}
}
std::vector<ImageData> imageDatas(m_mipCount * m_arraySize);
uint8_t* srcBits = dds.data() + offset;
uint8_t* endBits = dds.data() + dds.size();
uint32_t idx = 0;
for (uint32_t j = 0; j < m_arraySize; j++) {
uint32_t w = m_width;
uint32_t h = m_height;
uint32_t d = m_depth;
for (uint32_t i = 0; i < m_mipCount; i++) {
uint32_t numBytes;
uint32_t rowBytes;
GetImageInfo(w, h, m_format, &numBytes, &rowBytes, nullptr);
imageDatas[idx].m_width = w;
imageDatas[idx].m_height = h;
imageDatas[idx].m_depth = d;
imageDatas[idx].m_mem = srcBits;
imageDatas[idx].m_memPitch = rowBytes;
imageDatas[idx].m_memSlicePitch = numBytes;
idx++;
if (srcBits + (numBytes * d) > endBits) {
return Result::ErrorInvalidData;
}
srcBits += numBytes * d;
w = std::max<uint32_t>(1, w / 2);
h = std::max<uint32_t>(1, h / 2);
d = std::max<uint32_t>(1, d / 2);
}
}
m_dds = std::move(dds);
m_imageDatas = std::move(imageDatas);
return Result::Success;
}
void DDSFile::GetImageInfo(uint32_t w, uint32_t h, DXGIFormat fmt,
uint32_t* outNumBytes, uint32_t* outRowBytes,
uint32_t* outNumRows) {
uint32_t numBytes = 0;
uint32_t rowBytes = 0;
uint32_t numRows = 0;
bool bc = false;
bool packed = false;
bool planar = false;
uint32_t bpe = 0;
switch (fmt) {
case DXGIFormat::BC1_Typeless:
case DXGIFormat::BC1_UNorm:
case DXGIFormat::BC1_UNorm_SRGB:
case DXGIFormat::BC4_Typeless:
case DXGIFormat::BC4_UNorm:
case DXGIFormat::BC4_SNorm:
bc = true;
bpe = 8;
break;
case DXGIFormat::BC2_Typeless:
case DXGIFormat::BC2_UNorm:
case DXGIFormat::BC2_UNorm_SRGB:
case DXGIFormat::BC3_Typeless:
case DXGIFormat::BC3_UNorm:
case DXGIFormat::BC3_UNorm_SRGB:
case DXGIFormat::BC5_Typeless:
case DXGIFormat::BC5_UNorm:
case DXGIFormat::BC5_SNorm:
case DXGIFormat::BC6H_Typeless:
case DXGIFormat::BC6H_UF16:
case DXGIFormat::BC6H_SF16:
case DXGIFormat::BC7_Typeless:
case DXGIFormat::BC7_UNorm:
case DXGIFormat::BC7_UNorm_SRGB:
bc = true;
bpe = 16;
break;
case DXGIFormat::R8G8_B8G8_UNorm:
case DXGIFormat::G8R8_G8B8_UNorm:
case DXGIFormat::YUY2:
packed = true;
bpe = 4;
break;
case DXGIFormat::Y210:
case DXGIFormat::Y216:
packed = true;
bpe = 8;
break;
case DXGIFormat::NV12:
case DXGIFormat::YUV420_OPAQUE:
planar = true;
bpe = 2;
break;
case DXGIFormat::P010:
case DXGIFormat::P016:
planar = true;
bpe = 4;
break;
default:
break;
}
if (bc) {
uint32_t numBlocksWide = 0;
if (w > 0) {
numBlocksWide = std::max<uint32_t>(1, (w + 3) / 4);
}
uint32_t numBlocksHigh = 0;
if (h > 0) {
numBlocksHigh = std::max<uint32_t>(1, (h + 3) / 4);
}
rowBytes = numBlocksWide * bpe;
numRows = numBlocksHigh;
numBytes = rowBytes * numBlocksHigh;
} else if (packed) {
rowBytes = ((w + 1) >> 1) * bpe;
numRows = h;
numBytes = rowBytes * h;
} else if (fmt == DXGIFormat::NV11) {
rowBytes = ((w + 3) >> 2) * 4;
numRows = h * 2;
numBytes = rowBytes + numRows;
} else if (planar) {
rowBytes = ((w + 1) >> 1) * bpe;
numBytes = (rowBytes * h) + ((rowBytes * h + 1) >> 1);
numRows = h + ((h + 1) >> 1);
} else {
uint32_t bpp = GetBitsPerPixel(fmt);
rowBytes = (w * bpp + 7) / 8;
numRows = h;
numBytes = rowBytes * h;
}
if (outNumBytes) {
*outNumBytes = numBytes;
}
if (outRowBytes) {
*outRowBytes = rowBytes;
}
if (outNumRows) {
*outNumRows = numRows;
}
}
bool DDSFile::Flip() {
if (IsCompressed(m_format)) {
for (auto& imageData : m_imageDatas) {
if (!FlipCompressedImage(imageData)) {
return false;
}
}
} else {
for (auto& imageData : m_imageDatas) {
if (!FlipImage(imageData)) {
return false;
}
}
}
return true;
}
bool DDSFile::FlipImage(ImageData& imageData) {
for (uint32_t y = 0; y < imageData.m_height / 2; y++) {
auto line0 = (uint8_t*)imageData.m_mem + y * imageData.m_memPitch;
auto line1 = (uint8_t*)imageData.m_mem +
(imageData.m_height - y - 1) * imageData.m_memPitch;
for (uint32_t i = 0; i < imageData.m_memPitch; i++) {
std::swap(*line0, *line1);
line0++;
line1++;
}
}
return true;
}
bool DDSFile::FlipCompressedImage(ImageData& imageData) {
if (DXGIFormat::BC1_Typeless == m_format ||
DXGIFormat::BC1_UNorm == m_format ||
DXGIFormat::BC1_UNorm_SRGB == m_format) {
FlipCompressedImageBC1(imageData);
return true;
} else if (DXGIFormat::BC2_Typeless == m_format ||
DXGIFormat::BC2_UNorm == m_format ||
DXGIFormat::BC2_UNorm_SRGB == m_format) {
FlipCompressedImageBC2(imageData);
return true;
} else if (DXGIFormat::BC3_Typeless == m_format ||
DXGIFormat::BC3_UNorm == m_format ||
DXGIFormat::BC3_UNorm_SRGB == m_format) {
FlipCompressedImageBC3(imageData);
return true;
} else if (DXGIFormat::BC4_Typeless == m_format ||
DXGIFormat::BC4_UNorm == m_format ||
DXGIFormat::BC4_SNorm == m_format) {
FlipCompressedImageBC4(imageData);
return true;
} else if (DXGIFormat::BC5_Typeless == m_format ||
DXGIFormat::BC5_UNorm == m_format ||
DXGIFormat::BC5_SNorm == m_format) {
FlipCompressedImageBC5(imageData);
return true;
}
return false;
}
void DDSFile::FlipCompressedImageBC1(ImageData& imageData) {
uint32_t numXBlocks = (imageData.m_width + 3) / 4;
uint32_t numYBlocks = (imageData.m_height + 3) / 4;
if (imageData.m_height == 1) {
} else if (imageData.m_height == 2) {
auto blocks = (BC1Block*)imageData.m_mem;
for (uint32_t x = 0; x < numXBlocks; x++) {
auto block = blocks + x;
std::swap(block->m_row0, block->m_row1);
std::swap(block->m_row2, block->m_row3);
}
} else {
for (uint32_t y = 0; y < (numYBlocks + 1) / 2; y++) {
auto blocks0 = (BC1Block*)((uint8_t*)imageData.m_mem +
imageData.m_memPitch * y);
auto blocks1 =
(BC1Block*)((uint8_t*)imageData.m_mem +
imageData.m_memPitch * (numYBlocks - y - 1));
for (uint32_t x = 0; x < numXBlocks; x++) {
auto block0 = blocks0 + x;
auto block1 = blocks1 + x;
if (blocks0 != blocks1) {
std::swap(block0->m_color0, block1->m_color0);
std::swap(block0->m_color1, block1->m_color1);
std::swap(block0->m_row0, block1->m_row3);
std::swap(block0->m_row1, block1->m_row2);
std::swap(block0->m_row2, block1->m_row1);
std::swap(block0->m_row3, block1->m_row0);
} else {
std::swap(block0->m_row0, block0->m_row3);
std::swap(block0->m_row1, block0->m_row2);
}
}
}
}
}
void DDSFile::FlipCompressedImageBC2(ImageData& imageData) {
uint32_t numXBlocks = (imageData.m_width + 3) / 4;
uint32_t numYBlocks = (imageData.m_height + 3) / 4;
if (imageData.m_height == 1) {
} else if (imageData.m_height == 2) {
auto blocks = (BC2Block*)imageData.m_mem;
for (uint32_t x = 0; x < numXBlocks; x++) {
auto block = blocks + x;
std::swap(block->m_alphaRow0, block->m_alphaRow1);
std::swap(block->m_alphaRow2, block->m_alphaRow3);
std::swap(block->m_row0, block->m_row1);
std::swap(block->m_row2, block->m_row3);
}
} else {
for (uint32_t y = 0; y < (numYBlocks + 1) / 2; y++) {
auto blocks0 = (BC2Block*)((uint8_t*)imageData.m_mem +
imageData.m_memPitch * y);
auto blocks1 =
(BC2Block*)((uint8_t*)imageData.m_mem +
imageData.m_memPitch * (numYBlocks - y - 1));
for (uint32_t x = 0; x < numXBlocks; x++) {
auto block0 = blocks0 + x;
auto block1 = blocks1 + x;
if (block0 != block1) {
std::swap(block0->m_alphaRow0, block1->m_alphaRow3);
std::swap(block0->m_alphaRow1, block1->m_alphaRow2);
std::swap(block0->m_alphaRow2, block1->m_alphaRow1);
std::swap(block0->m_alphaRow3, block1->m_alphaRow0);
std::swap(block0->m_color0, block1->m_color0);
std::swap(block0->m_color1, block1->m_color1);
std::swap(block0->m_row0, block1->m_row3);
std::swap(block0->m_row1, block1->m_row2);
std::swap(block0->m_row2, block1->m_row1);
std::swap(block0->m_row3, block1->m_row0);
} else {
std::swap(block0->m_alphaRow0, block0->m_alphaRow3);
std::swap(block0->m_alphaRow1, block0->m_alphaRow2);
std::swap(block0->m_row0, block0->m_row3);
std::swap(block0->m_row1, block0->m_row2);
}
}
}
}
}
void DDSFile::FlipCompressedImageBC3(ImageData& imageData) {
uint32_t numXBlocks = (imageData.m_width + 3) / 4;
uint32_t numYBlocks = (imageData.m_height + 3) / 4;
if (imageData.m_height == 1) {
} else if (imageData.m_height == 2) {
auto blocks = (BC3Block*)imageData.m_mem;
for (uint32_t x = 0; x < numXBlocks; x++) {
auto block = blocks + x;
uint8_t r0 = (block->m_alphaR1 >> 4) | (block->m_alphaR2 << 4);
uint8_t r1 = (block->m_alphaR2 >> 4) | (block->m_alphaR0 << 4);
uint8_t r2 = (block->m_alphaR0 >> 4) | (block->m_alphaR1 << 4);
uint8_t r3 = (block->m_alphaR4 >> 4) | (block->m_alphaR5 << 4);
uint8_t r4 = (block->m_alphaR5 >> 4) | (block->m_alphaR3 << 4);
uint8_t r5 = (block->m_alphaR3 >> 4) | (block->m_alphaR4 << 4);
block->m_alphaR0 = r0;
block->m_alphaR1 = r1;
block->m_alphaR2 = r2;
block->m_alphaR3 = r3;
block->m_alphaR4 = r4;
block->m_alphaR5 = r5;
std::swap(block->m_row0, block->m_row1);
std::swap(block->m_row2, block->m_row3);
}
} else {
for (uint32_t y = 0; y < (numYBlocks + 1) / 2; y++) {
auto blocks0 = (BC3Block*)((uint8_t*)imageData.m_mem +
imageData.m_memPitch * y);
auto blocks1 =
(BC3Block*)((uint8_t*)imageData.m_mem +
imageData.m_memPitch * (numYBlocks - y - 1));
for (uint32_t x = 0; x < numXBlocks; x++) {
auto block0 = blocks0 + x;
auto block1 = blocks1 + x;
if (block0 != block1) {
std::swap(block0->m_alpha0, block1->m_alpha0);
std::swap(block0->m_alpha1, block1->m_alpha1);
uint8_t r0[6];
r0[0] = (block0->m_alphaR4 >> 4) | (block0->m_alphaR5 << 4);
r0[1] = (block0->m_alphaR5 >> 4) | (block0->m_alphaR3 << 4);
r0[2] = (block0->m_alphaR3 >> 4) | (block0->m_alphaR4 << 4);
r0[3] = (block0->m_alphaR1 >> 4) | (block0->m_alphaR2 << 4);
r0[4] = (block0->m_alphaR2 >> 4) | (block0->m_alphaR0 << 4);
r0[5] = (block0->m_alphaR0 >> 4) | (block0->m_alphaR1 << 4);
uint8_t r1[6];
r1[0] = (block1->m_alphaR4 >> 4) | (block1->m_alphaR5 << 4);
r1[1] = (block1->m_alphaR5 >> 4) | (block1->m_alphaR3 << 4);
r1[2] = (block1->m_alphaR3 >> 4) | (block1->m_alphaR4 << 4);
r1[3] = (block1->m_alphaR1 >> 4) | (block1->m_alphaR2 << 4);
r1[4] = (block1->m_alphaR2 >> 4) | (block1->m_alphaR0 << 4);
r1[5] = (block1->m_alphaR0 >> 4) | (block1->m_alphaR1 << 4);
block0->m_alphaR0 = r1[0];
block0->m_alphaR1 = r1[1];
block0->m_alphaR2 = r1[2];
block0->m_alphaR3 = r1[3];
block0->m_alphaR4 = r1[4];
block0->m_alphaR5 = r1[5];
block1->m_alphaR0 = r0[0];
block1->m_alphaR1 = r0[1];
block1->m_alphaR2 = r0[2];
block1->m_alphaR3 = r0[3];
block1->m_alphaR4 = r0[4];
block1->m_alphaR5 = r0[5];
std::swap(block0->m_color0, block1->m_color0);
std::swap(block0->m_color1, block1->m_color1);
std::swap(block0->m_row0, block1->m_row3);
std::swap(block0->m_row1, block1->m_row2);
std::swap(block0->m_row2, block1->m_row1);
std::swap(block0->m_row3, block1->m_row0);
} else {
uint8_t r0[6];
r0[0] = (block0->m_alphaR4 >> 4) | (block0->m_alphaR5 << 4);
r0[1] = (block0->m_alphaR5 >> 4) | (block0->m_alphaR3 << 4);
r0[2] = (block0->m_alphaR3 >> 4) | (block0->m_alphaR4 << 4);
r0[3] = (block0->m_alphaR1 >> 4) | (block0->m_alphaR2 << 4);
r0[4] = (block0->m_alphaR2 >> 4) | (block0->m_alphaR0 << 4);
r0[5] = (block0->m_alphaR0 >> 4) | (block0->m_alphaR1 << 4);
block0->m_alphaR0 = r0[0];
block0->m_alphaR1 = r0[1];
block0->m_alphaR2 = r0[2];
block0->m_alphaR3 = r0[3];
block0->m_alphaR4 = r0[4];
block0->m_alphaR5 = r0[5];
std::swap(block0->m_row0, block0->m_row3);
std::swap(block0->m_row1, block0->m_row2);
}
}
}
}
}
void DDSFile::FlipCompressedImageBC4(ImageData& imageData) {
uint32_t numXBlocks = (imageData.m_width + 3) / 4;
uint32_t numYBlocks = (imageData.m_height + 3) / 4;
if (imageData.m_height == 1) {
} else if (imageData.m_height == 2) {
auto blocks = (BC4Block*)imageData.m_mem;
for (uint32_t x = 0; x < numXBlocks; x++) {
auto block = blocks + x;
uint8_t r0 = (block->m_redR1 >> 4) | (block->m_redR2 << 4);
uint8_t r1 = (block->m_redR2 >> 4) | (block->m_redR0 << 4);
uint8_t r2 = (block->m_redR0 >> 4) | (block->m_redR1 << 4);
uint8_t r3 = (block->m_redR4 >> 4) | (block->m_redR5 << 4);
uint8_t r4 = (block->m_redR5 >> 4) | (block->m_redR3 << 4);
uint8_t r5 = (block->m_redR3 >> 4) | (block->m_redR4 << 4);
block->m_redR0 = r0;
block->m_redR1 = r1;
block->m_redR2 = r2;
block->m_redR3 = r3;
block->m_redR4 = r4;
block->m_redR5 = r5;
}
} else {
for (uint32_t y = 0; y < (numYBlocks + 1) / 2; y++) {
auto blocks0 = (BC4Block*)((uint8_t*)imageData.m_mem +
imageData.m_memPitch * y);
auto blocks1 =
(BC4Block*)((uint8_t*)imageData.m_mem +
imageData.m_memPitch * (numYBlocks - y - 1));
for (uint32_t x = 0; x < numXBlocks; x++) {
auto block0 = blocks0 + x;
auto block1 = blocks1 + x;
if (block0 != block1) {
std::swap(block0->m_red0, block1->m_red0);
std::swap(block0->m_red1, block1->m_red1);
uint8_t r0[6];
r0[0] = (block0->m_redR4 >> 4) | (block0->m_redR5 << 4);
r0[1] = (block0->m_redR5 >> 4) | (block0->m_redR3 << 4);
r0[2] = (block0->m_redR3 >> 4) | (block0->m_redR4 << 4);
r0[3] = (block0->m_redR1 >> 4) | (block0->m_redR2 << 4);
r0[4] = (block0->m_redR2 >> 4) | (block0->m_redR0 << 4);
r0[5] = (block0->m_redR0 >> 4) | (block0->m_redR1 << 4);
uint8_t r1[6];
r1[0] = (block1->m_redR4 >> 4) | (block1->m_redR5 << 4);
r1[1] = (block1->m_redR5 >> 4) | (block1->m_redR3 << 4);
r1[2] = (block1->m_redR3 >> 4) | (block1->m_redR4 << 4);
r1[3] = (block1->m_redR1 >> 4) | (block1->m_redR2 << 4);
r1[4] = (block1->m_redR2 >> 4) | (block1->m_redR0 << 4);
r1[5] = (block1->m_redR0 >> 4) | (block1->m_redR1 << 4);
block0->m_redR0 = r1[0];
block0->m_redR1 = r1[1];
block0->m_redR2 = r1[2];
block0->m_redR3 = r1[3];
block0->m_redR4 = r1[4];
block0->m_redR5 = r1[5];
block1->m_redR0 = r0[0];
block1->m_redR1 = r0[1];
block1->m_redR2 = r0[2];
block1->m_redR3 = r0[3];
block1->m_redR4 = r0[4];
block1->m_redR5 = r0[5];
} else {
uint8_t r0[6];
r0[0] = (block0->m_redR4 >> 4) | (block0->m_redR5 << 4);
r0[1] = (block0->m_redR5 >> 4) | (block0->m_redR3 << 4);
r0[2] = (block0->m_redR3 >> 4) | (block0->m_redR4 << 4);
r0[3] = (block0->m_redR1 >> 4) | (block0->m_redR2 << 4);
r0[4] = (block0->m_redR2 >> 4) | (block0->m_redR0 << 4);
r0[5] = (block0->m_redR0 >> 4) | (block0->m_redR1 << 4);
block0->m_redR0 = r0[0];
block0->m_redR1 = r0[1];
block0->m_redR2 = r0[2];
block0->m_redR3 = r0[3];
block0->m_redR4 = r0[4];
block0->m_redR5 = r0[5];
}
}
}
}
}
void DDSFile::FlipCompressedImageBC5(ImageData& imageData) {
uint32_t numXBlocks = (imageData.m_width + 3) / 4;
uint32_t numYBlocks = (imageData.m_height + 3) / 4;
if (imageData.m_height == 1) {
} else if (imageData.m_height == 2) {
auto blocks = (BC5Block*)imageData.m_mem;
for (uint32_t x = 0; x < numXBlocks; x++) {
auto block = blocks + x;
uint8_t r0 = (block->m_redR1 >> 4) | (block->m_redR2 << 4);
uint8_t r1 = (block->m_redR2 >> 4) | (block->m_redR0 << 4);
uint8_t r2 = (block->m_redR0 >> 4) | (block->m_redR1 << 4);
uint8_t r3 = (block->m_redR4 >> 4) | (block->m_redR5 << 4);
uint8_t r4 = (block->m_redR5 >> 4) | (block->m_redR3 << 4);
uint8_t r5 = (block->m_redR3 >> 4) | (block->m_redR4 << 4);
block->m_redR0 = r0;
block->m_redR1 = r1;
block->m_redR2 = r2;
block->m_redR3 = r3;
block->m_redR4 = r4;
block->m_redR5 = r5;
uint8_t g0 = (block->m_greenR1 >> 4) | (block->m_greenR2 << 4);
uint8_t g1 = (block->m_greenR2 >> 4) | (block->m_greenR0 << 4);
uint8_t g2 = (block->m_greenR0 >> 4) | (block->m_greenR1 << 4);
uint8_t g3 = (block->m_greenR4 >> 4) | (block->m_greenR5 << 4);
uint8_t g4 = (block->m_greenR5 >> 4) | (block->m_greenR3 << 4);
uint8_t g5 = (block->m_greenR3 >> 4) | (block->m_greenR4 << 4);
block->m_greenR0 = g0;
block->m_greenR1 = g1;
block->m_greenR2 = g2;
block->m_greenR3 = g3;
block->m_greenR4 = g4;
block->m_greenR5 = g5;
}
} else {
for (uint32_t y = 0; y < (numYBlocks + 1) / 2; y++) {
auto blocks0 = (BC5Block*)((uint8_t*)imageData.m_mem +
imageData.m_memPitch * y);
auto blocks1 =
(BC5Block*)((uint8_t*)imageData.m_mem +
imageData.m_memPitch * (numYBlocks - y - 1));
for (uint32_t x = 0; x < numXBlocks; x++) {
auto block0 = blocks0 + x;
auto block1 = blocks1 + x;
if (block0 != block1) {
std::swap(block0->m_red0, block1->m_red0);
std::swap(block0->m_red1, block1->m_red1);
uint8_t r0[6];
r0[0] = (block0->m_redR4 >> 4) | (block0->m_redR5 << 4);
r0[1] = (block0->m_redR5 >> 4) | (block0->m_redR3 << 4);
r0[2] = (block0->m_redR3 >> 4) | (block0->m_redR4 << 4);
r0[3] = (block0->m_redR1 >> 4) | (block0->m_redR2 << 4);
r0[4] = (block0->m_redR2 >> 4) | (block0->m_redR0 << 4);
r0[5] = (block0->m_redR0 >> 4) | (block0->m_redR1 << 4);
uint8_t r1[6];
r1[0] = (block1->m_redR4 >> 4) | (block1->m_redR5 << 4);
r1[1] = (block1->m_redR5 >> 4) | (block1->m_redR3 << 4);
r1[2] = (block1->m_redR3 >> 4) | (block1->m_redR4 << 4);
r1[3] = (block1->m_redR1 >> 4) | (block1->m_redR2 << 4);
r1[4] = (block1->m_redR2 >> 4) | (block1->m_redR0 << 4);
r1[5] = (block1->m_redR0 >> 4) | (block1->m_redR1 << 4);
block0->m_redR0 = r1[0];
block0->m_redR1 = r1[1];
block0->m_redR2 = r1[2];
block0->m_redR3 = r1[3];
block0->m_redR4 = r1[4];
block0->m_redR5 = r1[5];
block1->m_redR0 = r0[0];
block1->m_redR1 = r0[1];
block1->m_redR2 = r0[2];
block1->m_redR3 = r0[3];
block1->m_redR4 = r0[4];
block1->m_redR5 = r0[5];
std::swap(block0->m_green0, block1->m_green0);
std::swap(block0->m_green1, block1->m_green1);
uint8_t g0[6];
g0[0] = (block0->m_greenR4 >> 4) | (block0->m_greenR5 << 4);
g0[1] = (block0->m_greenR5 >> 4) | (block0->m_greenR3 << 4);
g0[2] = (block0->m_greenR3 >> 4) | (block0->m_greenR4 << 4);
g0[3] = (block0->m_greenR1 >> 4) | (block0->m_greenR2 << 4);
g0[4] = (block0->m_greenR2 >> 4) | (block0->m_greenR0 << 4);
g0[5] = (block0->m_greenR0 >> 4) | (block0->m_greenR1 << 4);
uint8_t g1[6];
g1[0] = (block1->m_greenR4 >> 4) | (block1->m_greenR5 << 4);
g1[1] = (block1->m_greenR5 >> 4) | (block1->m_greenR3 << 4);
g1[2] = (block1->m_greenR3 >> 4) | (block1->m_greenR4 << 4);
g1[3] = (block1->m_greenR1 >> 4) | (block1->m_greenR2 << 4);
g1[4] = (block1->m_greenR2 >> 4) | (block1->m_greenR0 << 4);
g1[5] = (block1->m_greenR0 >> 4) | (block1->m_greenR1 << 4);
block0->m_greenR0 = g1[0];
block0->m_greenR1 = g1[1];
block0->m_greenR2 = g1[2];
block0->m_greenR3 = g1[3];
block0->m_greenR4 = g1[4];
block0->m_greenR5 = g1[5];
block1->m_greenR0 = g0[0];
block1->m_greenR1 = g0[1];
block1->m_greenR2 = g0[2];
block1->m_greenR3 = g0[3];
block1->m_greenR4 = g0[4];
block1->m_greenR5 = g0[5];
} else {
uint8_t r0[6];
r0[0] = (block0->m_redR4 >> 4) | (block0->m_redR5 << 4);
r0[1] = (block0->m_redR5 >> 4) | (block0->m_redR3 << 4);
r0[2] = (block0->m_redR3 >> 4) | (block0->m_redR4 << 4);
r0[3] = (block0->m_redR1 >> 4) | (block0->m_redR2 << 4);
r0[4] = (block0->m_redR2 >> 4) | (block0->m_redR0 << 4);
r0[5] = (block0->m_redR0 >> 4) | (block0->m_redR1 << 4);
block0->m_redR0 = r0[0];
block0->m_redR1 = r0[1];
block0->m_redR2 = r0[2];
block0->m_redR3 = r0[3];
block0->m_redR4 = r0[4];
block0->m_redR5 = r0[5];
uint8_t g0[6];
g0[0] = (block0->m_greenR4 >> 4) | (block0->m_greenR5 << 4);
g0[1] = (block0->m_greenR5 >> 4) | (block0->m_greenR3 << 4);
g0[2] = (block0->m_greenR3 >> 4) | (block0->m_greenR4 << 4);
g0[3] = (block0->m_greenR1 >> 4) | (block0->m_greenR2 << 4);
g0[4] = (block0->m_greenR2 >> 4) | (block0->m_greenR0 << 4);
g0[5] = (block0->m_greenR0 >> 4) | (block0->m_greenR1 << 4);
block0->m_greenR0 = g0[0];
block0->m_greenR1 = g0[1];
block0->m_greenR2 = g0[2];
block0->m_greenR3 = g0[3];
block0->m_greenR4 = g0[4];
block0->m_greenR5 = g0[5];
}
}
}
}
}
} // namespace tinyddsloader
#endif // !TINYDDSLOADER_IMPLEMENTATION
#endif // !TINYDDSLOADER_H_