// // Copyright(c) 2019 benikabocha. // Distributed under the MIT License (http://opensource.org/licenses/MIT) // #ifndef TINYDDSLOADER_H_ #define TINYDDSLOADER_H_ #include #include 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&& 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 m_dds; std::vector 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 #include 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 dds(fileSize); input.read(reinterpret_cast(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 dds(data, data + size); return Load(std::move(dds)); } Result DDSFile::Load(std::vector&& 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(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( reinterpret_cast(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 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(1, w / 2); h = std::max(1, h / 2); d = std::max(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(1, (w + 3) / 4); } uint32_t numBlocksHigh = 0; if (h > 0) { numBlocksHigh = std::max(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_