From 75ea2703c61173b87936c72521d7a39ed5271503 Mon Sep 17 00:00:00 2001 From: shenlei Date: Wed, 30 Sep 2026 13:31:21 +0900 Subject: [PATCH] port: EterImageLib Image/TGAImage/DXTCImage verbatim (+ d3d.h ddraw shim) -fno-strict-aliasing on the port targets: 40250 relies on MSVC's lack of type-based alias analysis (DXTCImage reads Color8888 through DWORD*). Co-Authored-By: Claude Opus 5.5 --- audit/history.jsonl | 3 + .../port-map/EterImageLib/DXTCImage.cpp.json | 192 ++ audit/port-map/EterImageLib/Image.cpp.json | 143 ++ audit/port-map/EterImageLib/TGAImage.cpp.json | 108 ++ src/platform/EterImageLib/DXTCImage.cpp | 1632 ++++++++++++++++- src/platform/EterImageLib/Image.cpp | 214 ++- src/platform/EterImageLib/TGAImage.cpp | 365 +++- src/port/CMakeLists.txt | 6 + src/port/common/shim/sdk/d3d.h | 122 ++ tests/port/port_eter_image_test.cpp | 125 ++ 10 files changed, 2758 insertions(+), 152 deletions(-) create mode 100644 audit/port-map/EterImageLib/DXTCImage.cpp.json create mode 100644 audit/port-map/EterImageLib/Image.cpp.json create mode 100644 audit/port-map/EterImageLib/TGAImage.cpp.json create mode 100644 src/port/common/shim/sdk/d3d.h create mode 100644 tests/port/port_eter_image_test.cpp diff --git a/audit/history.jsonl b/audit/history.jsonl index a76bd8cd..a92ee584 100644 --- a/audit/history.jsonl +++ b/audit/history.jsonl @@ -622,3 +622,6 @@ {"date": "2026-09-30", "unit": "EterLib/MSWindow.cpp", "action": "port", "evidence": "verbatim over new winuser.h shim (platform/Win32User.cpp); 64-bit pointer casts ADAPTED; port.ms_window"} {"date": "2026-09-30", "unit": "EterLib/MSApplication.cpp", "action": "port", "evidence": "verbatim over new winuser.h shim (platform/Win32User.cpp); 64-bit pointer casts ADAPTED; port.ms_window"} {"date": "2026-09-30", "unit": "EterLib/Input.cpp", "action": "port", "evidence": "host key events instead of DirectInput polling; IsPressed/Reset verbatim; port.input"} +{"date": "2026-09-30", "unit": "EterImageLib/Image.cpp", "action": "port", "evidence": "verbatim; port.eter_image"} +{"date": "2026-09-30", "unit": "EterImageLib/TGAImage.cpp", "action": "port", "evidence": "verbatim; raw save/load flips (desc 0x08), RLE load; port.eter_image"} +{"date": "2026-09-30", "unit": "EterImageLib/DXTCImage.cpp", "action": "port", "evidence": "verbatim + DWORD_PTR casts + ddraw shim; -fno-strict-aliasing for MSVC type punning; port.eter_image"} diff --git a/audit/port-map/EterImageLib/DXTCImage.cpp.json b/audit/port-map/EterImageLib/DXTCImage.cpp.json new file mode 100644 index 00000000..ecbcb6ab --- /dev/null +++ b/audit/port-map/EterImageLib/DXTCImage.cpp.json @@ -0,0 +1,192 @@ +{ + "reference": "EterImageLib/DXTCImage.cpp", + "reference_sha256": "7e1c9d4a562441aea08b89892ab74907a582c6e0c2c8fd1ccce7fe7a300eead2", + "priority": "P4", + "contracts": [], + "functions": { + "CDXTCImage::CDXTCImage": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/DXTCImage.cpp:CDXTCImage::CDXTCImage" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CDXTCImage::~CDXTCImage": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/DXTCImage.cpp:CDXTCImage::~CDXTCImage" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CDXTCImage::Initialize": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/DXTCImage.cpp:CDXTCImage::Initialize" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CDXTCImage::Clear": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/DXTCImage.cpp:CDXTCImage::Clear" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CDXTCImage::LoadFromFile": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/DXTCImage.cpp:CDXTCImage::LoadFromFile" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CDXTCImage::LoadHeaderFromMemory": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/DXTCImage.cpp:CDXTCImage::LoadHeaderFromMemory" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CDXTCImage::LoadFromMemory": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/DXTCImage.cpp:CDXTCImage::LoadFromMemory" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CDXTCImage::Copy": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/DXTCImage.cpp:CDXTCImage::Copy" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CDXTCImage::Unextract": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/DXTCImage.cpp:CDXTCImage::Unextract" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CDXTCImage::Decompress": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/DXTCImage.cpp:CDXTCImage::Decompress" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "GetColorBlockColors": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/DXTCImage.cpp:GetColorBlockColors" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "DecodeColorBlock": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/DXTCImage.cpp:DecodeColorBlock" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ], + "note": "verbatim; reads Color8888 through DWORD* (MSVC aliasing) — port targets build with -fno-strict-aliasing" + }, + "DecodeAlphaExplicit": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/DXTCImage.cpp:DecodeAlphaExplicit" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "DecodeAlpha3BitLinear": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/DXTCImage.cpp:DecodeAlpha3BitLinear" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CDXTCImage::DecompressDXT1": { + "status": "ADAPTED", + "impl": [ + "src/platform/EterImageLib/DXTCImage.cpp:CDXTCImage::DecompressDXT1" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ], + "note": "40250: block/pixel addresses via (DWORD) pointer casts. Port: (DWORD_PTR). Invariant: the same byte offsets; a DWORD would truncate a 64-bit pointer." + }, + "CDXTCImage::DecompressDXT3": { + "status": "ADAPTED", + "impl": [ + "src/platform/EterImageLib/DXTCImage.cpp:CDXTCImage::DecompressDXT3" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ], + "note": "40250: block/pixel addresses via (DWORD) pointer casts. Port: (DWORD_PTR). Invariant: the same byte offsets; a DWORD would truncate a 64-bit pointer." + }, + "CDXTCImage::DecompressDXT5": { + "status": "ADAPTED", + "impl": [ + "src/platform/EterImageLib/DXTCImage.cpp:CDXTCImage::DecompressDXT5" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ], + "note": "40250: block/pixel addresses via (DWORD) pointer casts. Port: (DWORD_PTR). Invariant: the same byte offsets; a DWORD would truncate a 64-bit pointer." + }, + "CDXTCImage::DecompressARGB": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/DXTCImage.cpp:CDXTCImage::DecompressARGB" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "GetNumberOfBits": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/DXTCImage.cpp:GetNumberOfBits" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CDXTCImage::DecodePixelFormat": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/DXTCImage.cpp:CDXTCImage::DecodePixelFormat" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + } + } +} diff --git a/audit/port-map/EterImageLib/Image.cpp.json b/audit/port-map/EterImageLib/Image.cpp.json new file mode 100644 index 00000000..876bd6a4 --- /dev/null +++ b/audit/port-map/EterImageLib/Image.cpp.json @@ -0,0 +1,143 @@ +{ + "reference": "EterImageLib/Image.cpp", + "reference_sha256": "4117eebaa0f98aa4d5b75da728c824d148155fb27084ca0b95ea479b113ef204", + "priority": "P4", + "contracts": [], + "functions": { + "CImage::CImage": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/Image.cpp:CImage::CImage" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CImage::SetFileName": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/Image.cpp:CImage::SetFileName" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CImage::GetFileNameString": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/Image.cpp:CImage::GetFileNameString" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CImage::PutImage": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/Image.cpp:CImage::PutImage" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CImage::GetBasePointer": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/Image.cpp:CImage::GetBasePointer" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CImage::GetLinePointer": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/Image.cpp:CImage::GetLinePointer" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CImage::GetWidth": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/Image.cpp:CImage::GetWidth" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CImage::GetHeight": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/Image.cpp:CImage::GetHeight" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CImage::Clear": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/Image.cpp:CImage::Clear" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CImage::Create": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/Image.cpp:CImage::Create" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CImage::Destroy": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/Image.cpp:CImage::Destroy" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CImage::Initialize": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/Image.cpp:CImage::Initialize" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CImage::IsEmpty": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/Image.cpp:CImage::IsEmpty" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CImage::FlipTopToBottom": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/Image.cpp:CImage::FlipTopToBottom" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CImage::~CImage": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/Image.cpp:CImage::~CImage" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + } + } +} diff --git a/audit/port-map/EterImageLib/TGAImage.cpp.json b/audit/port-map/EterImageLib/TGAImage.cpp.json new file mode 100644 index 00000000..d495f397 --- /dev/null +++ b/audit/port-map/EterImageLib/TGAImage.cpp.json @@ -0,0 +1,108 @@ +{ + "reference": "EterImageLib/TGAImage.cpp", + "reference_sha256": "e60338a6416259f5abe9ab8541ba0bbe0790295aadd09794f31c8ccaba3e0c44", + "priority": "P4", + "contracts": [], + "functions": { + "CTGAImage::CTGAImage": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/TGAImage.cpp:CTGAImage::CTGAImage" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CTGAImage::~CTGAImage": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/TGAImage.cpp:CTGAImage::~CTGAImage" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CTGAImage::Create": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/TGAImage.cpp:CTGAImage::Create" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CTGAImage::LoadFromMemory": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/TGAImage.cpp:CTGAImage::LoadFromMemory" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CTGAImage::LoadFromDiskFile": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/TGAImage.cpp:CTGAImage::LoadFromDiskFile" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CTGAImage::GetRLEPixelCount": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/TGAImage.cpp:CTGAImage::GetRLEPixelCount" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CTGAImage::GetRawPixelCount": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/TGAImage.cpp:CTGAImage::GetRawPixelCount" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CTGAImage::SetCompressed": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/TGAImage.cpp:CTGAImage::SetCompressed" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CTGAImage::SetAlphaChannel": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/TGAImage.cpp:CTGAImage::SetAlphaChannel" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + }, + "CTGAImage::SaveToDiskFile": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/TGAImage.cpp:CTGAImage::SaveToDiskFile" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ], + "note": "verbatim; the RLE branch reads m_pdwEndPtr, which 40250 never sets (no caller saves compressed; TargaResource only loads)" + }, + "CTGAImage::GetHeader": { + "status": "PORTED", + "impl": [ + "src/platform/EterImageLib/TGAImage.cpp:CTGAImage::GetHeader" + ], + "test": [ + "tests/port/port_eter_image_test.cpp" + ] + } + } +} diff --git a/src/platform/EterImageLib/DXTCImage.cpp b/src/platform/EterImageLib/DXTCImage.cpp index 9f46188a..8ac32d43 100644 --- a/src/platform/EterImageLib/DXTCImage.cpp +++ b/src/platform/EterImageLib/DXTCImage.cpp @@ -1,85 +1,1647 @@ -// Platform skeleton for EterImageLib/DXTCImage.h (40250 EterImageLib/DXTCImage.cpp), generated by platform_stub.py. -// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation. +// 40250 EterImageLib/DXTCImage.cpp, verbatim except the pointer arithmetic through (DWORD) casts, which is +// (DWORD_PTR) here (marked PORT): a DWORD truncates a 64-bit pointer. +// need the d3d.h for things in format of .dds file #include "EterImageLib/StdAfx.h" + +#include +#include +#include +#include + +#include "EterBase/MappedFile.h" +#include "EterBase/Debug.h" + #include "EterImageLib/DXTCImage.h" -#include "../PlatformStub.h" +struct DXTColBlock +{ + WORD col0; + WORD col1; + + // no bit fields - use bytes + BYTE row[4]; +}; + +struct DXTAlphaBlockExplicit +{ + WORD row[4]; +}; + +struct DXTAlphaBlock3BitLinear +{ + BYTE alpha0; + BYTE alpha1; + + BYTE stuff[6]; +}; + +// use cast to struct instead of RGBA_MAKE as struct is much +struct Color8888 +{ + BYTE b; // Last one is MSB, 1st is LSB. + BYTE g; // order of the output ARGB or BGRA, etc... + BYTE r; // change the order of names to change the + BYTE a; +}; + +struct Color565 +{ + unsigned nBlue : 5; // order of names changes + unsigned nGreen : 6; // byte order of output to 32 bit + unsigned nRed : 5; +}; + +///////////////////////////////////// +// should be in ddraw.h +#ifndef MAKEFOURCC +#define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + ((DWORD)(BYTE) (ch0 ) | ((DWORD)(BYTE) (ch1) << 8) | \ + ((DWORD)(BYTE) (ch2) << 16) | ((DWORD)(BYTE) (ch3) << 24)) +#endif // defined(MAKEFOURCC) CDXTCImage::CDXTCImage() { - MT_PLATFORM_STUB(); + Initialize(); } CDXTCImage::~CDXTCImage() { - MT_PLATFORM_STUB(); } -auto CDXTCImage::Initialize() -> void +void CDXTCImage::Initialize() { - MT_PLATFORM_STUB(); + m_nWidth = 0; + m_nHeight = 0; + + for (int i = 0; i < MAX_MIPLEVELS; ++i) + m_pbCompBufferByLevels[i] = NULL; } -auto CDXTCImage::Clear() -> void +void CDXTCImage::Clear() { - MT_PLATFORM_STUB(); + for (int i = 0; i < MAX_MIPLEVELS; ++i) + m_bCompVector[i].clear(); + + Initialize(); } -auto CDXTCImage::LoadFromFile(const char *) -> bool +bool CDXTCImage::LoadFromFile(const char * filename) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + // only understands .dds files for now + // return true if success + int next = 1; + + static char fileupper[MAX_PATH+1]; + + strncpy(fileupper, filename, MAX_PATH); + _strupr(fileupper); + + int i; + bool knownformat = false; + + for (i = 0; i < next; ++i) + { + char * found = strstr(fileupper, ".DDS"); + + if (found != NULL) + { + knownformat = true; + break; + } + } + + if (knownformat == false) + { + Tracef("Unknown file format encountered! [%s]\n", filename); + return(false); + } + + CMappedFile mappedFile; + LPCVOID pvMap; + + if (!mappedFile.Create(filename, &pvMap, 0, 0)) + { + Tracef("Can't open file for reading! [%s]\n", filename); + return false; + } + + return LoadFromMemory((const BYTE*) pvMap); } -auto CDXTCImage::LoadFromMemory(const BYTE *) -> bool +bool CDXTCImage::LoadHeaderFromMemory(const BYTE * c_pbMap) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + ////////////////////////////////////// + // start reading the file + // from Microsoft's mssdk D3DIM example "Compress" + DWORD dwMagic; + + // Read magic number + dwMagic = *(DWORD *) c_pbMap; + c_pbMap += sizeof(DWORD); + +//!@# +// if (dwMagic != MAKEFOURCC('D','D','S',' ')) +// return false; + + DDSURFACEDESC2 ddsd; // read from dds file + + // Read the surface description + memcpy(&ddsd, c_pbMap, sizeof(DDSURFACEDESC2)); + c_pbMap += sizeof(DDSURFACEDESC2); + + // Does texture have mipmaps? + m_bMipTexture = (ddsd.dwMipMapCount > 0) ? TRUE : FALSE; + + // Clear unwanted flags + // Can't do this!!! surface not re-created here + // ddsd.dwFlags &= (~DDSD_PITCH); + // ddsd.dwFlags &= (~DDSD_LINEARSIZE); + + // Is it DXTC ? + // I sure hope pixelformat is valid! + m_xddPixelFormat.dwFlags = ddsd.ddpfPixelFormat.dwFlags; + m_xddPixelFormat.dwFourCC = ddsd.ddpfPixelFormat.dwFourCC; + m_xddPixelFormat.dwSize = ddsd.ddpfPixelFormat.dwSize; + m_xddPixelFormat.dwRGBBitCount = ddsd.ddpfPixelFormat.dwRGBBitCount; + m_xddPixelFormat.dwRGBAlphaBitMask = ddsd.ddpfPixelFormat.dwRGBAlphaBitMask; + m_xddPixelFormat.dwRBitMask = ddsd.ddpfPixelFormat.dwRBitMask; + m_xddPixelFormat.dwGBitMask = ddsd.ddpfPixelFormat.dwGBitMask; + m_xddPixelFormat.dwBBitMask = ddsd.ddpfPixelFormat.dwBBitMask; + + DecodePixelFormat(m_strFormat, &m_xddPixelFormat); + + if (m_CompFormat != PF_DXT1 && + m_CompFormat != PF_DXT3 && + m_CompFormat != PF_DXT5) + { + return false; + } + + if (ddsd.dwMipMapCount > MAX_MIPLEVELS) + ddsd.dwMipMapCount = MAX_MIPLEVELS; + + m_nWidth = ddsd.dwWidth; + m_nHeight = ddsd.dwHeight; + //!@# + m_dwMipMapCount = max(1, ddsd.dwMipMapCount); + m_dwFlags = ddsd.dwFlags; + + if (ddsd.dwFlags & DDSD_PITCH) + { + m_lPitch = ddsd.lPitch; + m_pbCompBufferByLevels[0] = c_pbMap; + } + else + { + m_lPitch = ddsd.dwLinearSize; + + if (ddsd.dwFlags & DDSD_MIPMAPCOUNT) + { + for (DWORD dwLinearSize = ddsd.dwLinearSize, i = 0; i < m_dwMipMapCount; ++i, dwLinearSize >>= 2) + { + m_pbCompBufferByLevels[i] = c_pbMap; + c_pbMap += dwLinearSize; + } + } + else + { + m_pbCompBufferByLevels[0] = c_pbMap; + } + } + + return true; } -auto CDXTCImage::LoadHeaderFromMemory(const BYTE *) -> bool +////////////////////////////////////////////////////////////////////// +bool CDXTCImage::LoadFromMemory(const BYTE * c_pbMap) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + if (!LoadHeaderFromMemory(c_pbMap)) + return false; + + if (m_dwFlags & DDSD_PITCH) + { + DWORD dwBytesPerRow = m_nWidth * m_xddPixelFormat.dwRGBBitCount / 8; + + m_nCompSize = m_lPitch * m_nHeight; + m_nCompLineSz = dwBytesPerRow; + + m_bCompVector[0].resize(m_nCompSize); + BYTE * pDest = &m_bCompVector[0][0]; + + c_pbMap = m_pbCompBufferByLevels[0]; + + for (int yp = 0; yp < m_nHeight; ++yp) + { + memcpy(pDest, c_pbMap, dwBytesPerRow); + pDest += m_lPitch; + c_pbMap += m_lPitch; + } + } + else + { + if (m_dwFlags & DDSD_MIPMAPCOUNT) + { + for (DWORD dwLinearSize = m_lPitch, i = 0; i < m_dwMipMapCount; ++i, dwLinearSize >>= 2) + { + m_bCompVector[i].resize(dwLinearSize); + Copy(i, &m_bCompVector[i][0], dwLinearSize); + } + } + else + { + m_bCompVector[0].resize(m_lPitch); + Copy(0, &m_bCompVector[0][0], m_lPitch); + } + } + + // done reading file + return true; } -auto CDXTCImage::Copy(int, BYTE *, long) -> bool +bool CDXTCImage::Copy(int miplevel, BYTE * pbDest, long lDestPitch) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + if (!(m_dwFlags & DDSD_MIPMAPCOUNT)) + if (miplevel) + return false; + + /* + DXTColBlock * pBlock; + WORD * pPos = (WORD *) &m_pbCompBufferByLevels[miplevel][0]; + int xblocks = (m_nWidth >> miplevel) / 4; + int yblocks = (m_nHeight >> miplevel) / 4; + + for (int y = 0; y < yblocks; ++y) + { + // 8 bytes per block + pBlock = (DXTColBlock*) ((DWORD) pPos + y * xblocks * 8); + + memcpy(pbDest, pBlock, xblocks * 8); + pbDest += lDestPitch; + } + */ + + memcpy(pbDest, m_pbCompBufferByLevels[miplevel], m_lPitch >> (miplevel * 2)); + pbDest += lDestPitch; + return true; } -auto CDXTCImage::Decompress(int, DWORD *) -> void +void CDXTCImage::Unextract(BYTE * pbDest, int /*iWidth*/, int /*iHeight*/, int iPitch) { - MT_PLATFORM_STUB(); + if (!m_pbCompBufferByLevels[0]) + return; + + DXTColBlock * pBlock; + BYTE * pPos = (BYTE *) &m_pbCompBufferByLevels[0][0]; + int xblocks = m_nWidth / 4; + int yblocks = (m_nHeight / 4) * ((iPitch / m_nWidth) / 2); + + for (int y = 0; y < yblocks; ++y) + { + pBlock = (DXTColBlock*) (pPos + y * xblocks * 8); + + memcpy(pbDest, pBlock, xblocks * 8); + pbDest += xblocks * 8; + } + + /* + for (int y = 0; y < iHeight; ++y) + { + memcpy(pbDest, &m_pbCompBufferByLevels[0][0] + y*iWidth, iWidth); + pbDest += iWidth; + } + */ } -auto CDXTCImage::DecompressDXT1(int, DWORD *) -> void +void CDXTCImage::Decompress(int miplevel, DWORD * pdwDest) { - MT_PLATFORM_STUB(); + switch (m_CompFormat) + { + case PF_DXT1: + DecompressDXT1(miplevel, pdwDest); + break; + + case PF_DXT3: + DecompressDXT3(miplevel, pdwDest); + break; + + case PF_DXT5: + DecompressDXT5(miplevel, pdwDest); + break; + + case PF_ARGB: + DecompressARGB(miplevel, pdwDest); + break; + + case PF_UNKNOWN: + break; + } } -auto CDXTCImage::DecompressDXT3(int, DWORD *) -> void +inline void GetColorBlockColors(DXTColBlock * pBlock, Color8888 * col_0, Color8888 * col_1, + Color8888 * col_2, Color8888 * col_3, + WORD & wrd) { - MT_PLATFORM_STUB(); + // There are 4 methods to use - see the Time_ functions. + // 1st = shift = does normal approach per byte for color comps + // 2nd = use freak variable bit field color565 for component extraction + // 3rd = use super-freak DWORD adds BEFORE shifting the color components + // This lets you do only 1 add per color instead of 3 BYTE adds and + // might be faster + // Call RunTimingSession() to run each of them & output result to txt file + + // freak variable bit structure method + // normal math + // This method is fastest + Color565 * pCol; + + pCol = (Color565*) & (pBlock->col0); + + col_0->a = 0xff; + col_0->r = pCol->nRed; + col_0->r <<= 3; // shift to full precision + col_0->g = pCol->nGreen; + col_0->g <<= 2; + col_0->b = pCol->nBlue; + col_0->b <<= 3; + + pCol = (Color565*) & (pBlock->col1); + col_1->a = 0xff; + col_1->r = pCol->nRed; + col_1->r <<= 3; // shift to full precision + col_1->g = pCol->nGreen; + col_1->g <<= 2; + col_1->b = pCol->nBlue; + col_1->b <<= 3; + + if (pBlock->col0 > pBlock->col1) + { + // Four-color block: derive the other two colors. + // 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3 + // These two bit codes correspond to the 2-bit fields + // stored in the 64-bit block. + wrd = (WORD) (((WORD) col_0->r * 2 + (WORD) col_1->r) / 3); + // no +1 for rounding + // as bits have been shifted to 888 + col_2->r = (BYTE)wrd; + + wrd = (WORD) (((WORD) col_0->g * 2 + (WORD) col_1->g) / 3); + col_2->g = (BYTE)wrd; + + wrd = (WORD) (((WORD) col_0->b * 2 + (WORD) col_1->b) / 3); + col_2->b = (BYTE)wrd; + col_2->a = 0xff; + + wrd = (WORD) (((WORD) col_0->r + (WORD) col_1->r * 2) / 3); + col_3->r = (BYTE)wrd; + + wrd = (WORD) (((WORD) col_0->g + (WORD) col_1->g * 2) / 3); + col_3->g = (BYTE)wrd; + + wrd = (WORD) (((WORD) col_0->b + (WORD) col_1->b * 2) / 3); + col_3->b = (BYTE)wrd; + col_3->a = 0xff; + } + else + { + // Three-color block: derive the other color. + // 00 = color_0, 01 = color_1, 10 = color_2, + // 11 = transparent. + // These two bit codes correspond to the 2-bit fields + // stored in the 64-bit block. + + // explicit for each component, unlike some refrasts... + + // Tracef("block has alpha\n"); + wrd = (WORD) (((WORD) col_0->r + (WORD) col_1->r) / 2); + col_2->r = (BYTE)wrd; + wrd = (WORD) (((WORD) col_0->g + (WORD) col_1->g) / 2); + col_2->g = (BYTE)wrd; + wrd = (WORD) (((WORD) col_0->b + (WORD) col_1->b) / 2); + col_2->b = (BYTE)wrd; + col_2->a = 0xff; + + col_3->r = 0x00; // random color to indicate alpha + col_3->g = 0x00; + col_3->b = 0x00; + col_3->a = 0x00; + } +} // Get color block colors (...) + + +inline void DecodeColorBlock(DWORD * pImPos, + DXTColBlock * pColorBlock, + int width, + DWORD * col_0, + DWORD * col_1, + DWORD * col_2, + DWORD * col_3) +{ + // width is width of image in pixels + DWORD bits; + int y, n; + + // bit masks = 00000011, 00001100, 00110000, 11000000 + const DWORD masks[] = { 3, 12, 3 << 4, 3 << 6 }; + const int shift[] = { 0, 2, 4, 6 }; + + // r steps through lines in y + for (y = 0; y < 4; ++y, pImPos += width - 4) // no width * 4 as DWORD ptr inc will * 4 + { + // width * 4 bytes per pixel per line + // each j dxtc row is 4 lines of pixels + + // pImPos = (DWORD*) ((DWORD) pBase + i * 16 + (y + j * 4) * m_nWidth * 4); + + // n steps through pixels + for (n = 0; n < 4; ++n) + { + bits = pColorBlock->row[y] & masks[n]; + bits >>= shift[n]; + + switch (bits) + { + case 0: + *pImPos = *col_0; + pImPos++; // increment to next DWORD + break; + + case 1: + *pImPos = *col_1; + pImPos++; + break; + + case 2: + *pImPos = *col_2; + pImPos++; + break; + + case 3: + *pImPos = *col_3; + pImPos++; + break; + + default: + Tracef("Your logic is jacked! bits == 0x%x\n", bits); + pImPos++; + break; + } + } + } } -auto CDXTCImage::DecompressDXT5(int, DWORD *) -> void +inline void DecodeAlphaExplicit(DWORD * pImPos, DXTAlphaBlockExplicit * pAlphaBlock, + int width, DWORD alphazero) { - MT_PLATFORM_STUB(); + // alphazero is a bit mask that when & with the image color + // will zero the alpha bits, so if the image DWORDs are + // ARGB then alphazero will be 0x00ffffff or if + // RGBA then alphazero will be 0xffffff00 + // alphazero constructed automaticaly from field order of Color8888 structure + + // decodes to 32 bit format only + int row, pix; + + WORD wrd; + + Color8888 col; + col.r = col.g = col.b = 0; + + //Tracef("\n"); + for (row = 0; row < 4; row++, pImPos += width - 4) + { + // pImPow += pImPos += width-4 moves to next row down + wrd = pAlphaBlock->row[row]; + + // Tracef("0x%.8x\t\t", wrd); + for (pix = 0; pix < 4; ++pix) + { + // zero the alpha bits of image pixel + *pImPos &= alphazero; + + col.a = (BYTE) (wrd & 0x000f); // get only low 4 bits + // col.a <<= 4; // shift to full byte precision + // NOTE: with just a << 4 you'll never have alpha + // of 0xff, 0xf0 is max so pure shift doesn't quite + // cover full alpha range. + // It's much cheaper than divide & scale though. + // To correct for this, and get 0xff for max alpha, + // or the low bits back in after left shifting + col.a = (BYTE) (col.a | (col.a << 4)); // This allows max 4 bit alpha to be 0xff alpha + // in final image, and is crude approach to full + // range scale + + *pImPos |= *((DWORD*)&col); // or the bits into the prev. nulled alpha + + wrd >>= 4; // move next bits to lowest 4 + + pImPos++; // move to next pixel in the row + } + } } -auto CDXTCImage::DecompressARGB(int, DWORD *) -> void +static BYTE gBits[4][4]; +static WORD gAlphas[8]; +static Color8888 gACol[4][4]; + +inline void DecodeAlpha3BitLinear(DWORD * pImPos, DXTAlphaBlock3BitLinear * pAlphaBlock, + int width, DWORD alphazero) { - MT_PLATFORM_STUB(); + gAlphas[0] = pAlphaBlock->alpha0; + gAlphas[1] = pAlphaBlock->alpha1; + + // 8-alpha or 6-alpha block? + if (gAlphas[0] > gAlphas[1]) + { + // 8-alpha block: derive the other 6 alphas. + // 000 = alpha_0, 001 = alpha_1, others are interpolated + gAlphas[2] = (WORD) ((6 * gAlphas[0] + gAlphas[1]) / 7); // Bit code 010 + gAlphas[3] = (WORD) ((5 * gAlphas[0] + 2 * gAlphas[1]) / 7); // Bit code 011 + gAlphas[4] = (WORD) ((4 * gAlphas[0] + 3 * gAlphas[1]) / 7); // Bit code 100 + gAlphas[5] = (WORD) ((3 * gAlphas[0] + 4 * gAlphas[1]) / 7); // Bit code 101 + gAlphas[6] = (WORD) ((2 * gAlphas[0] + 5 * gAlphas[1]) / 7); // Bit code 110 + gAlphas[7] = (WORD) (( gAlphas[0] + 6 * gAlphas[1]) / 7); // Bit code 111 + } + else + { + // 6-alpha block: derive the other alphas. + // 000 = alpha_0, 001 = alpha_1, others are interpolated + gAlphas[2] = (WORD) ((4 * gAlphas[0] + gAlphas[1]) / 5); // Bit code 010 + gAlphas[3] = (WORD) ((3 * gAlphas[0] + 2 * gAlphas[1]) / 5); // Bit code 011 + gAlphas[4] = (WORD) ((2 * gAlphas[0] + 3 * gAlphas[1]) / 5); // Bit code 100 + gAlphas[5] = (WORD) (( gAlphas[0] + 4 * gAlphas[1]) / 5); // Bit code 101 + gAlphas[6] = 0; // Bit code 110 + gAlphas[7] = 255; // Bit code 111 + } + + // Decode 3-bit fields into array of 16 BYTES with same value + + // first two rows of 4 pixels each: + // pRows = (Alpha3BitRows*) & (pAlphaBlock->stuff[0]); + const DWORD mask = 0x00000007; // bits = 00 00 01 11 + DWORD bits = *((DWORD*) & (pAlphaBlock->stuff[0])); + + gBits[0][0] = (BYTE) (bits & mask); + bits >>= 3; + gBits[0][1] = (BYTE) (bits & mask); + bits >>= 3; + gBits[0][2] = (BYTE) (bits & mask); + bits >>= 3; + gBits[0][3] = (BYTE) (bits & mask); + bits >>= 3; + gBits[1][0] = (BYTE) (bits & mask); + bits >>= 3; + gBits[1][1] = (BYTE) (bits & mask); + bits >>= 3; + gBits[1][2] = (BYTE) (bits & mask); + bits >>= 3; + gBits[1][3] = (BYTE) (bits & mask); + + // now for last two rows: + bits = *((DWORD*) & (pAlphaBlock->stuff[3])); // last 3 bytes + + gBits[2][0] = (BYTE) (bits & mask); + bits >>= 3; + gBits[2][1] = (BYTE) (bits & mask); + bits >>= 3; + gBits[2][2] = (BYTE) (bits & mask); + bits >>= 3; + gBits[2][3] = (BYTE) (bits & mask); + bits >>= 3; + gBits[3][0] = (BYTE) (bits & mask); + bits >>= 3; + gBits[3][1] = (BYTE) (bits & mask); + bits >>= 3; + gBits[3][2] = (BYTE) (bits & mask); + bits >>= 3; + gBits[3][3] = (BYTE) (bits & mask); + + // decode the codes into alpha values + int row, pix; + + for (row = 0; row < 4; ++row) + { + for (pix = 0; pix < 4; ++pix) + { + gACol[row][pix].a = (BYTE) gAlphas[gBits[row][pix]]; + + assert(gACol[row][pix].r == 0); + assert(gACol[row][pix].g == 0); + assert(gACol[row][pix].b == 0); + } + } + + // Write out alpha values to the image bits + for (row = 0; row < 4; ++row, pImPos += width - 4) + { + // pImPow += pImPos += width - 4 moves to next row down + for (pix = 0; pix < 4; ++pix) + { + // zero the alpha bits of image pixel + *pImPos &= alphazero; + *pImPos |= *((DWORD*) &(gACol[row][pix])); // or the bits into the prev. nulled alpha + pImPos++; + } + } } -auto CDXTCImage::DecodePixelFormat(CHAR *, XDDPIXELFORMAT *) -> void +void CDXTCImage::DecompressDXT1(int miplevel, DWORD * pdwDest) { - MT_PLATFORM_STUB(); + // This was hacked up pretty quick & slopily + // decompresses to 32 bit format 0xARGB + int xblocks, yblocks; +#ifdef DEBUG + if ((ddsd.dwWidth % 4) != 0) + { + Tracef("****** warning width not div by 4! %d\n", ddsd.dwWidth); + } + + if ((ddsd.dwHeight % 4) != 0) + { + Tracef("****** warning Height not div by 4! %d\n", ddsd.dwHeight); + } + + Tracef("end check\n"); +#endif + UINT nWidth = m_nWidth >> miplevel; + UINT nHeight = m_nHeight >> miplevel; + + xblocks = nWidth / 4; + yblocks = nHeight / 4; + + int x, y; + DWORD * pBase = (DWORD *) pdwDest; + WORD * pPos = (WORD *) &m_bCompVector[miplevel][0];; // pos in compressed data + DWORD * pImPos; + + DXTColBlock * pBlock; + + Color8888 col_0, col_1, col_2, col_3; + WORD wrd; + + for (y = 0; y < yblocks; ++y) + { + // 8 bytes per block + pBlock = (DXTColBlock *) ((DWORD_PTR) pPos + y * xblocks * 8); // PORT: (DWORD) in 40250 + + for (x = 0; x < xblocks; ++x, ++pBlock) + { + // inline func: + GetColorBlockColors(pBlock, &col_0, &col_1, &col_2, &col_3, wrd); + + pImPos = (DWORD *) ((DWORD_PTR) pBase + x*16 + (y*4) * nWidth * 4); // PORT: (DWORD) in 40250 + DecodeColorBlock(pImPos, pBlock, nWidth, (DWORD *)&col_0, (DWORD *)&col_1, (DWORD *)&col_2, (DWORD *)&col_3); + // Set to RGB test pattern + // pImPos = (DWORD*) ((DWORD) pBase + i * 4 + j * m_nWidth * 4); + // *pImPos = ((i * 4) << 16) | ((j * 4) << 8) | ((63 - i) * 4); + + // checkerboard of only col_0 and col_1 basis colors: + // pImPos = (DWORD *) ((DWORD) pBase + i * 8 + j * m_nWidth * 8); + // *pImPos = *((DWORD *) &col_0); + // pImPos += 1 + m_nWidth; + // *pImPos = *((DWORD *) &col_1); + } + } } -auto CDXTCImage::Unextract(BYTE *, int, int, int) -> void +void CDXTCImage::DecompressDXT3(int miplevel, DWORD* pdwDest) { - MT_PLATFORM_STUB(); + int xblocks, yblocks; +#ifdef DEBUG + if ((ddsd.dwWidth % 4) != 0) + { + Tracef("****** warning width not div by 4! %d\n", ddsd.dwWidth); + } + + if ((ddsd.dwHeight % 4) != 0) + { + Tracef("****** warning Height not div by 4! %d\n", ddsd.dwHeight); + } + + Tracef("end check\n"); +#endif + UINT nWidth = m_nWidth >> miplevel; + UINT nHeight = m_nHeight >> miplevel; + + xblocks = nWidth / 4; + yblocks = nHeight / 4; + + int x, y; + DWORD * pBase = (DWORD *) pdwDest; + WORD * pPos = (WORD *) &m_bCompVector[miplevel][0]; // pos in compressed data + DWORD * pImPos; // pos in decompressed data + + DXTColBlock * pBlock; + DXTAlphaBlockExplicit * pAlphaBlock; + + Color8888 col_0, col_1, col_2, col_3; + WORD wrd; + + // fill alphazero with appropriate value to zero out alpha when + // alphazero is ANDed with the image color 32 bit DWORD: + col_0.a = 0; + col_0.r = col_0.g = col_0.b = 0xff; + + DWORD alphazero = *((DWORD *) &col_0); + + for (y = 0; y < yblocks; ++y) + { + // 8 bytes per block + // 1 block for alpha, 1 block for color + pBlock = (DXTColBlock *) ((DWORD_PTR) (pPos + y * xblocks * 16)); // PORT: (DWORD) in 40250 + + for (x = 0; x < xblocks; ++x, ++pBlock) + { + // inline + // Get alpha block + pAlphaBlock = (DXTAlphaBlockExplicit *) pBlock; + + // inline func: + // Get color block & colors + pBlock++; + GetColorBlockColors(pBlock, &col_0, &col_1, &col_2, &col_3, wrd); + + // Decode the color block into the bitmap bits + // inline func: + pImPos = (DWORD *) ((DWORD_PTR) (pBase + x * 16 + (y * 4) * nWidth * 4)); // PORT: (DWORD) in 40250 + + DecodeColorBlock(pImPos, + pBlock, + nWidth, + (DWORD *) &col_0, (DWORD *) &col_1, (DWORD *) &col_2, (DWORD *) &col_3); + + // Overwrite the previous alpha bits with the alpha block + // info + // inline func: + DecodeAlphaExplicit(pImPos, pAlphaBlock, nWidth, alphazero); + } + } } + +void CDXTCImage::DecompressDXT5(int level, DWORD * pdwDest) +{ + int xblocks, yblocks; +#ifdef DEBUG + if ((ddsd.dwWidth % 4) != 0) + { + Tracef("****** warning width not div by 4! %d\n", ddsd.dwWidth); + } + + if ((ddsd.dwHeight % 4) != 0) + { + Tracef("****** warning Height not div by 4! %d\n", ddsd.dwHeight); + } + + Tracef("end check\n"); +#endif + UINT nWidth = m_nWidth >> level; + UINT nHeight = m_nHeight >> level; + + xblocks = nWidth / 4; + yblocks = nHeight / 4; + + int x, y; + + DWORD * pBase = (DWORD *) pdwDest; + WORD * pPos = pPos = (WORD *) &m_bCompVector[level][0]; // pos in compressed data + DWORD * pImPos; // pos in decompressed data + + DXTColBlock * pBlock; + DXTAlphaBlock3BitLinear * pAlphaBlock; + + Color8888 col_0, col_1, col_2, col_3; + WORD wrd; + + // fill alphazero with appropriate value to zero out alpha when + // alphazero is ANDed with the image color 32 bit DWORD: + col_0.a = 0; + col_0.r = col_0.g = col_0.b = 0xff; + DWORD alphazero = *((DWORD *) &col_0); + + //////////////////////////////// + // Tracef("blocks: x: %d y: %d\n", xblocks, yblocks); + for (y = 0; y < yblocks; ++y) + { + // 8 bytes per block + // 1 block for alpha, 1 block for color + pBlock = (DXTColBlock*) ((DWORD_PTR) (pPos + y * xblocks * 16)); // PORT: (DWORD) in 40250 + + for (x = 0; x < xblocks; ++x, ++pBlock) + { + // inline + // Get alpha block + pAlphaBlock = (DXTAlphaBlock3BitLinear*) pBlock; + + // inline func: + // Get color block & colors + pBlock++; + + // Tracef("pBlock: 0x%.8x\n", pBlock); + GetColorBlockColors(pBlock, &col_0, &col_1, &col_2, &col_3, wrd); + + // Decode the color block into the bitmap bits + // inline func: + pImPos = (DWORD *) ((DWORD_PTR) (pBase + x * 16 + (y * 4) * nWidth * 4)); // PORT: (DWORD) in 40250 + + //DecodeColorBlock(pImPos, pBlock, nWidth, (DWORD *)&col_0, (DWORD *)&col_1, (DWORD *)&col_2, (DWORD *)&col_3); + DecodeColorBlock(pImPos, pBlock, nWidth, (DWORD *)&col_0, (DWORD *)&col_1, (DWORD *)&col_2, (DWORD *)&col_3); + + // Overwrite the previous alpha bits with the alpha block + // info + DecodeAlpha3BitLinear(pImPos, pAlphaBlock, nWidth, alphazero); + } + } +} // dxt5 + +void CDXTCImage::DecompressARGB(int level, DWORD * pdwDest) +{ + UINT lPitch = m_lPitch >> (level * 2); + memcpy(pdwDest, &m_bCompVector[level][0], lPitch); +} +/* +typedef struct _DDSURFACEDESC2 { +DWORD dwSize; +DWORD dwFlags; +DWORD dwHeight; +DWORD dwWidth; +union +{ +LONG lPitch; +DWORD dwLinearSize; +} DUMMYUNIONNAMEN(1); +DWORD dwBackBufferCount; +union +{ +DWORD dwMipMapCount; +DWORD dwRefreshRate; +} DUMMYUNIONNAMEN(2); +DWORD dwAlphaBitDepth; +DWORD dwReserved; +LPVOID lpSurface; +union +{ +DDCOLORKEY ddckCKDestOverlay; +DWORD dwEmptyFaceColor; +} DUMMYUNIONNAMEN(3); +DDCOLORKEY ddckCKDestBlt; +DDCOLORKEY ddckCKSrcOverlay; +DDCOLORKEY ddckCKSrcBlt; +DDPIXELFORMAT ddpfPixelFormat; +DDSCAPS2 ddsCaps; +DWORD dwTextureStage; +} DDSURFACEDESC2, FAR* LPDDSURFACEDESC2; +*/ + +//----------------------------------------------------------------------------- +// Name: GetNumberOfBits() +// Desc: Returns the number of bits set in a DWORD mask +// from microsoft mssdk d3dim sample "Compress" +//----------------------------------------------------------------------------- +static WORD GetNumberOfBits(DWORD dwMask) +{ + WORD wBits; + for (wBits = 0; dwMask; wBits++) + dwMask = (dwMask & (dwMask - 1)); + + return wBits; +} + +//----------------------------------------------------------------------------- +// Name: PixelFormatToString() +// Desc: Creates a string describing a pixel format. +// adapted from microsoft mssdk D3DIM Compress example +// PixelFormatToString() +//----------------------------------------------------------------------------- +VOID CDXTCImage::DecodePixelFormat(CHAR* strPixelFormat, XDDPIXELFORMAT* pxddpf) +{ + switch (pxddpf->dwFourCC) + { + case 0: + { + // This dds texture isn't compressed so write out ARGB format + WORD a = GetNumberOfBits(pxddpf->dwRGBAlphaBitMask); + WORD r = GetNumberOfBits(pxddpf->dwRBitMask); + WORD g = GetNumberOfBits(pxddpf->dwGBitMask); + WORD b = GetNumberOfBits(pxddpf->dwBBitMask); + + _snprintf(strPixelFormat, 31, "ARGB-%d%d%d%d%s", a, r, g, b, + pxddpf->dwBBitMask & DDPF_ALPHAPREMULT ? "-premul" : ""); + m_CompFormat = PF_ARGB; + } + break; + + case MAKEFOURCC('D','X','T','1'): + strncpy(strPixelFormat, "DXT1", 31); + m_CompFormat = PF_DXT1; + break; + + case MAKEFOURCC('D','X','T','2'): + strncpy(strPixelFormat, "DXT2", 31); + m_CompFormat = PF_DXT2; + break; + + case MAKEFOURCC('D','X','T','3'): + strncpy(strPixelFormat, "DXT3", 31); + m_CompFormat = PF_DXT3; + break; + + case MAKEFOURCC('D','X','T','4'): + strncpy(strPixelFormat, "DXT4", 31); + m_CompFormat = PF_DXT4; + break; + + case MAKEFOURCC('D','X','T','5'): + strncpy(strPixelFormat, "DXT5", 31); + m_CompFormat = PF_DXT5; + break; + + default: + strcpy(strPixelFormat, "Format Unknown"); + m_CompFormat = PF_UNKNOWN; + break; + } +} + +/* +// Struct to hold various timing values +struct TimingInfo +{ + LARGE_INTEGER m_start_clk; + LARGE_INTEGER m_end_clk; + + int m_nSamples; + LARGE_INTEGER m_interval_sum; // sum of all end-start, nSamples number added in + + CString m_csName; // text desc of what timed +}; + +void CDXTCImage::RunTimingSession() +{ + // Must have a dxt5 texture loaded + // No special reason - just lazy coding + // Functions called to time code are separate from non-timed + // code. It's alogorithm that counts. + ASSERT(m_pCompBytes != NULL); + ASSERT(m_pDecompBytes != NULL); // must already have allocated memory + + switch (m_CompFormat) + { + case PF_DXT1: + case PF_DXT2: + case PF_DXT3: + case PF_DXT4: + case PF_UNKNOWN: + Tracef("You must have a DXT5 texture loaded to RunTimingSession()!!\n"); + Tracef("Now I will be nasty and ASSERT(false)!\n"); + ASSERT(false); + break; + + case PF_DXT5: + Tracef("Running code timing session on DXT5 color decompress\n"); + break; + } + + LARGE_INTEGER start_clk, end_clk; + QueryPerformanceCounter(&start_clk); +#define NMETHOD 4 +#define NBATCHES 4 + int passes[NBATCHES]; + passes[0] = 1; + passes[1] = 10; + passes[2] = 30; + passes[3] = 50; + + TimingInfo method[NMETHOD][NBATCHES]; + + int i, n; + + FILE * pf = fopen("timing.txt", "wt"); + + if (pf == NULL) + { + return; + } + + fprintf(pf, "\n\n"); + + for (i = 0; i < NBATCHES; ++i) + { + Sleep(50); + fprintf(pf,"i: %d passes[i]: %d\n", i, passes[i]); + Time_Decomp5_01(passes[i], &(method[0][i])); + Time_Decomp5_02(passes[i], &(method[1][i])); + Time_Decomp5_03(passes[i], &(method[2][i])); + Time_Decomp5_04(passes[i], &(method[3][i])); + } + + QueryPerformanceCounter(&end_clk); + + // unsigned long total; + // total = (unsigned long) (end_clk - start_clk); + LARGE_INTEGER freq; + QueryPerformanceFrequency(& freq); + + fprintf(pf, "\nCounter freq = %u %d \n", freq.LowPart, freq.HighPart); + fprintf(pf, "start: %u %u end: %u %u\n", start_clk.LowPart, start_clk.HighPart, end_clk.LowPart, end_clk.HighPart); + + Tracef("\nCounter freq = %u %d \n", freq.LowPart, freq.HighPart); + Tracef("start: %u %u end: %u %u\n", start_clk.LowPart, start_clk.HighPart, end_clk.LowPart, end_clk.HighPart); + + double dur = ((double)end_clk.LowPart - (double)start_clk.LowPart) / (double)freq.LowPart; + + fprintf(pf, "Total timing session took: %u cycles = %f seconds\n", (end_clk.LowPart - start_clk.LowPart), dur); + fprintf(pf, "\n\n"); + + Tracef("Total timing session took: %u cycles = %f seconds\n", (end_clk.LowPart - start_clk.LowPart), dur); + Tracef("\n\n"); + + for (n = 0; n < NMETHOD; ++n) + { + for (i = 0; i < NBATCHES; ++i) + { + fprintf(pf, "method %d:\n", n); + fprintf(pf, " %s", method[n][i].m_csName); + fprintf(pf, " tot: %u %u\n", method[n][i].m_interval_sum.HighPart, method[n][i].m_interval_sum.LowPart); + + Tracef("method %d:\n", n); + Tracef(" %s", method[n][i].m_csName); + Tracef(" tot: %u %u\n", method[n][i].m_interval_sum.HighPart, method[n][i].m_interval_sum.LowPart); + + dur = ((double)method[n][i].m_interval_sum.LowPart) / ((double)method[n][i].m_nSamples * (double)freq.LowPart); + + fprintf(pf, " avg: %u\n", method[n][i].m_interval_sum.LowPart / method[n][i].m_nSamples); + fprintf(pf, " avg time: %f sec\n", dur); + + Tracef(" avg: %u\n", method[n][i].m_interval_sum.LowPart / method[n][i].m_nSamples); + Tracef(" avg time: %f sec\n", dur); + } + + fprintf(pf, "\n\n"); + Tracef("\n\n"); + } + + fclose(pf); + + MessageBeep(MB_OK); + //BOOL QueryPerformanceFrequency( + // LARGE_INTEGER *lpFrequency // address of current frequency + //); +} + +inline void GetColorBlockColors_m2(DXTColBlock * pBlock, Color8888 * col_0, Color8888 * col_1, + Color8888 * col_2, Color8888 * col_3, + WORD & wrd ) +{ + // method 2 + // freak variable bit structure method + // normal math + Color565 * pCol; + + pCol = (Color565*) & (pBlock->col0); + + col_0->a = 0xff; + col_0->r = pCol->nRed; + col_0->r <<= 3; // shift to full precision + col_0->g = pCol->nGreen; + col_0->g <<= 2; + col_0->b = pCol->nBlue; + col_0->b <<= 3; + + pCol = (Color565*) & (pBlock->col1); + col_1->a = 0xff; + col_1->r = pCol->nRed; + col_1->r <<= 3; // shift to full precision + col_1->g = pCol->nGreen; + col_1->g <<= 2; + col_1->b = pCol->nBlue; + col_1->b <<= 3; + + if (pBlock->col0 > pBlock->col1) + { + // Four-color block: derive the other two colors. + // 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3 + // These two bit codes correspond to the 2-bit fields + // stored in the 64-bit block. + wrd = ((WORD) col_0->r * 2 + (WORD) col_1->r) / 3; + // no +1 for rounding + // as bits have been shifted to 888 + col_2->r = (BYTE)wrd; + + wrd = ((WORD) col_0->g * 2 + (WORD) col_1->g) / 3; + col_2->g = (BYTE)wrd; + + wrd = ((WORD) col_0->b * 2 + (WORD) col_1->b) / 3; + col_2->b = (BYTE)wrd; + col_2->a = 0xff; + + wrd = ((WORD) col_0->r + (WORD) col_1->r * 2) / 3; + col_3->r = (BYTE)wrd; + + wrd = ((WORD) col_0->g + (WORD) col_1->g * 2) / 3; + col_3->g = (BYTE)wrd; + + wrd = ((WORD) col_0->b + (WORD) col_1->b * 2) / 3; + col_3->b = (BYTE)wrd; + col_3->a = 0xff; + + } + else + { + // Three-color block: derive the other color. + // 00 = color_0, 01 = color_1, 10 = color_2, + // 11 = transparent. + // These two bit codes correspond to the 2-bit fields + // stored in the 64-bit block. + + // explicit for each component, unlike some refrasts... + + // Tracef("block has alpha\n"); + wrd = ((WORD) col_0->r + (WORD) col_1->r) / 2; + col_2->r = (BYTE)wrd; + wrd = ((WORD) col_0->g + (WORD) col_1->g) / 2; + col_2->g = (BYTE)wrd; + wrd = ((WORD) col_0->b + (WORD) col_1->b) / 2; + col_2->b = (BYTE)wrd; + col_2->a = 0xff; + + col_3->r = 0x00; // random color to indicate alpha + col_3->g = 0xff; + col_3->b = 0xff; + col_3->a = 0x00; + } +} + + + +inline void GetColorBlockColors_m3(DXTColBlock * pBlock, Color8888 * col_0, Color8888 * col_1, + Color8888 * col_2, Color8888 * col_3, + WORD & wrd ) +{ + // method 3 + ////////////////////////////////////////////////////// + // super-freak variable bit structure with + // Cool Math Trick (tm) + + // Do 2/3 1/3 math BEFORE bit shift on the whole DWORD + // as the fields will NEVER carry into the next + // or overflow!! =) + + Color565 * pCol; + + pCol = (Color565*) & (pBlock->col0); + + col_0->a = 0x00; // must set to 0 to avoid overflow in DWORD add + col_0->r = pCol->nRed; + col_0->g = pCol->nGreen; + col_0->b = pCol->nBlue; + + pCol = (Color565*) & (pBlock->col1); + col_1->a = 0x00; + col_1->r = pCol->nRed; + col_1->g = pCol->nGreen; + col_1->b = pCol->nBlue; + + if (pBlock->col0 > pBlock->col1) + { + *((DWORD*)col_2) = ((*((DWORD*)col_0)) * 2 + (*((DWORD*)col_1))); + + *((DWORD*)col_3) = ((*((DWORD*)col_0)) + (*((DWORD*)col_1)) * 2); + + // now shift to appropriate precision & divide by 3. + col_2->r = ((WORD) col_2->r << 3) / (WORD)3; + col_2->g = ((WORD) col_2->g << 2) / (WORD)3; + col_2->b = ((WORD) col_2->b << 3) / (WORD)3; + + col_3->r = ((WORD) col_3->r << 3) / (WORD)3; + col_3->g = ((WORD) col_3->g << 2) / (WORD)3; + col_3->b = ((WORD) col_3->b << 3) / (WORD)3; + + col_0->a = 0xff; // now set appropriate alpha + col_1->a = 0xff; + col_2->a = 0xff; + col_3->a = 0xff; + } + else + { + *((DWORD*)col_2) = ((*((DWORD*)col_0)) + (*((DWORD*)col_1))); + + // now shift to appropriate precision & divide by 2. + // << 3) / 2 == << 2 + // << 2) / 2 == << 1 + col_2->r = ((WORD) col_2->r << 2); + col_2->g = ((WORD) col_2->g << 1); + col_2->b = ((WORD) col_2->b << 2); + + col_2->a = 0xff; + + col_3->a = 0x00; // + col_3->r = 0x00; // random color to indicate alpha + col_3->g = 0xff; + col_3->b = 0xff; + } + + // now shift orig color components + col_0->r <<= 3; + col_0->g <<= 2; + col_0->b <<= 3; + + col_1->r <<= 3; + col_1->g <<= 2; + col_1->b <<= 3; +} + + +inline void GetColorBlockColors_m4(DXTColBlock * pBlock, Color8888 * col_0, Color8888 * col_1, + Color8888 * col_2, Color8888 * col_3, + WORD & wrd ) +{ + + // m1 color extraction from 5-6-5 + // m3 color math on DWORD before bit shift to full precision + wrd = pBlock->col0; + col_0->a = 0x00; // must set to 0 to avoid possible overflow & carry to next field in DWORD add + + // extract r,g,b bits + col_0->b = (unsigned char) wrd & 0x1f; // 0x1f = 0001 1111 to mask out upper 3 bits + wrd >>= 5; + col_0->g = (unsigned char) wrd & 0x3f; // 0x3f = 0011 1111 to mask out upper 2 bits + wrd >>= 6; + col_0->r = (unsigned char) wrd & 0x1f; + + + // same for col # 2: + wrd = pBlock->col1; + col_1->a = 0x00; // must set to 0 to avoid possible overflow in DWORD add + + // extract r,g,b bits + col_1->b = (unsigned char) wrd & 0x1f; + wrd >>= 5; + col_1->g = (unsigned char) wrd & 0x3f; + wrd >>= 6; + col_1->r = (unsigned char) wrd & 0x1f; + + if (pBlock->col0 > pBlock->col1) + { + *((DWORD*)col_2) = ((*((DWORD*)col_0)) * 2 + (*((DWORD*)col_1))); + *((DWORD*)col_3) = ((*((DWORD*)col_0)) + (*((DWORD*)col_1)) * 2); + + // shift to appropriate precision & divide by 3. + col_2->r = ((WORD) col_2->r << 3) / (WORD)3; + col_2->g = ((WORD) col_2->g << 2) / (WORD)3; + col_2->b = ((WORD) col_2->b << 3) / (WORD)3; + + col_3->r = ((WORD) col_3->r << 3) / (WORD)3; + col_3->g = ((WORD) col_3->g << 2) / (WORD)3; + col_3->b = ((WORD) col_3->b << 3) / (WORD)3; + + col_0->a = 0xff; // set appropriate alpha + col_1->a = 0xff; + col_2->a = 0xff; + col_3->a = 0xff; + } + else + { + *((DWORD*)col_2) = ((*((DWORD*)col_0)) + (*((DWORD*)col_1))); + + // shift to appropriate precision & divide by 2. + // << 3) / 2 == << 2 + // << 2) / 2 == << 1 + col_2->r = ((WORD) col_2->r << 2); + col_2->g = ((WORD) col_2->g << 1); + col_2->b = ((WORD) col_2->b << 2); + + col_2->a = 0xff; + + col_3->a = 0x00; // + col_3->r = 0x00; // random color to indicate alpha + col_3->g = 0xff; + col_3->b = 0xff; + } + + // shift orig color components to full precision + col_0->r <<= 3; + col_0->g <<= 2; + col_0->b <<= 3; + + col_1->r <<= 3; + col_1->g <<= 2; + col_1->b <<= 3; +} + + +inline void GetColorBlockColors_m1(DXTColBlock * pBlock, Color8888 * col_0, Color8888 * col_1, + Color8888 * col_2, Color8888 * col_3, + WORD & wrd ) +{ + + // Method 1: + // Shifty method + wrd = pBlock->col0; + col_0->a = 0xff; + + // extract r,g,b bits + col_0->b = (unsigned char) wrd; + col_0->b <<= 3; // shift to full precision + wrd >>= 5; + col_0->g = (unsigned char) wrd; + col_0->g <<= 2; // shift to full precision + wrd >>= 6; + col_0->r = (unsigned char) wrd; + col_0->r <<= 3; // shift to full precision + + // same for col # 2: + wrd = pBlock->col1; + col_1->a = 0xff; + + // extract r,g,b bits + col_1->b = (unsigned char) wrd; + col_1->b <<= 3; // shift to full precision + wrd >>= 5; + col_1->g = (unsigned char) wrd; + col_1->g <<= 2; // shift to full precision + wrd >>= 6; + col_1->r = (unsigned char) wrd; + col_1->r <<= 3; // shift to full precision + + // use this for all but the super-freak math method + if (pBlock->col0 > pBlock->col1) + { + // Four-color block: derive the other two colors. + // 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3 + // These two bit codes correspond to the 2-bit fields + // stored in the 64-bit block. + + wrd = ((WORD) col_0->r * 2 + (WORD) col_1->r) / 3; + // no +1 for rounding + // as bits have been shifted to 888 + col_2->r = (BYTE)wrd; + + wrd = ((WORD) col_0->g * 2 + (WORD) col_1->g) / 3; + col_2->g = (BYTE)wrd; + + wrd = ((WORD) col_0->b * 2 + (WORD) col_1->b) / 3; + col_2->b = (BYTE)wrd; + col_2->a = 0xff; + + wrd = ((WORD) col_0->r + (WORD) col_1->r * 2) / 3; + col_3->r = (BYTE)wrd; + + wrd = ((WORD) col_0->g + (WORD) col_1->g * 2) / 3; + col_3->g = (BYTE)wrd; + + wrd = ((WORD) col_0->b + (WORD) col_1->b * 2) / 3; + col_3->b = (BYTE)wrd; + col_3->a = 0xff; + } + else + { + // Three-color block: derive the other color. + // 00 = color_0, 01 = color_1, 10 = color_2, + // 11 = transparent. + // These two bit codes correspond to the 2-bit fields + // stored in the 64-bit block. + + // explicit for each component, unlike some refrasts... + + // Tracef("block has alpha\n"); + + wrd = ((WORD) col_0->r + (WORD) col_1->r) / 2; + col_2->r = (BYTE)wrd; + wrd = ((WORD) col_0->g + (WORD) col_1->g) / 2; + col_2->g = (BYTE)wrd; + wrd = ((WORD) col_0->b + (WORD) col_1->b) / 2; + col_2->b = (BYTE)wrd; + col_2->a = 0xff; + + col_3->r = 0x00; // random color to indicate alpha + col_3->g = 0xff; + col_3->b = 0xff; + col_3->a = 0x00; + } +} // Get color block colors (...) + +void CDXTCImage::Time_Decomp5_01(int ntimes, TimingInfo * info) +{ + int n; + + info->m_nSamples = 0; + info->m_interval_sum.QuadPart = 0; + info->m_csName.Format("Timing decomp method 1: bit shift, for %d times\n", ntimes); + + for (n = 0; n < ntimes; n++) + { + QueryPerformanceCounter(& info->m_start_clk); + + int xblocks, yblocks; + + xblocks = m_DDSD.dwWidth / 4; + yblocks = m_DDSD.dwHeight / 4; + + int i,j; + + DWORD * pBase = (DWORD*) m_pDecompBytes; + DWORD * pImPos = (DWORD*) pBase; // pos in decompressed data + WORD * pPos = (WORD*) m_pCompBytes; // pos in compressed data + + DXTColBlock * pBlock; + DXTAlphaBlock3BitLinear * pAlphaBlock; + + Color8888 col_0, col_1, col_2, col_3; + WORD wrd; + + // fill alphazero with appropriate value to zero out alpha when + // alphazero is ANDed with the image color 32 bit DWORD: + col_0.a = 0; + col_0.r = col_0.g = col_0.b = 0xff; + DWORD alphazero = *((DWORD*) &col_0); + + // ** See DecompressDXT5 code for comments!! + for (j = 0; j < yblocks; ++j) + { + pBlock = (DXTColBlock*) ((DWORD)m_pCompBytes + j * xblocks * 16); + for (i = 0; i < xblocks; ++i, ++pBlock) + { + pAlphaBlock = (DXTAlphaBlock3BitLinear*) pBlock; + pBlock++; + + GetColorBlockColors_m1(pBlock, &col_0, &col_1, &col_2, &col_3, wrd); + + pImPos = (DWORD*)((DWORD)pBase + i*16 + (j*4) * m_nWidth * 4); + DecodeColorBlock(pImPos, pBlock, m_nWidth, (DWORD*)&col_0, (DWORD*)&col_1, + (DWORD*)&col_2, (DWORD*)&col_3); + DecodeAlpha3BitLinear(pImPos, pAlphaBlock, m_nWidth, alphazero); + } + } + + QueryPerformanceCounter(& info->m_end_clk); + + info->m_nSamples ++; + info->m_interval_sum.QuadPart += info->m_end_clk.QuadPart - info->m_start_clk.QuadPart; + } +} + + +void CDXTCImage::Time_Decomp5_02(int ntimes, TimingInfo * info) +{ + int n; + + info->m_nSamples = 0; + info->m_interval_sum.QuadPart = 0; + info->m_csName.Format("Timing decomp method 2: bit field struct, for %d times\n", ntimes); + + for (n = 0; n < ntimes; n++) + { + QueryPerformanceCounter(& info->m_start_clk); + + int xblocks, yblocks; + xblocks = m_DDSD.dwWidth / 4; + yblocks = m_DDSD.dwHeight / 4; + int i,j; + DWORD * pBase = (DWORD*) m_pDecompBytes; + DWORD * pImPos = (DWORD*) pBase; // pos in decompressed data + WORD * pPos = (WORD*) m_pCompBytes; // pos in compressed data + DXTColBlock * pBlock; + DXTAlphaBlock3BitLinear * pAlphaBlock; + Color8888 col_0, col_1, col_2, col_3; + WORD wrd; + // fill alphazero with appropriate value to zero out alpha when + // alphazero is ANDed with the image color 32 bit DWORD: + col_0.a = 0; + col_0.r = col_0.g = col_0.b = 0xff; + DWORD alphazero = *((DWORD*) &col_0); + + // ** See DecompressDXT5 code for comments!! + for (j = 0; j < yblocks; ++j) + { + pBlock = (DXTColBlock*) ((DWORD)m_pCompBytes + j * xblocks * 16); + for (i = 0; i < xblocks; ++i, ++pBlock) + { + pAlphaBlock = (DXTAlphaBlock3BitLinear*) pBlock; + pBlock++; + + GetColorBlockColors_m2(pBlock, &col_0, &col_1, &col_2, &col_3, wrd); + + pImPos = (DWORD*)((DWORD)pBase + i*16 + (j*4) * m_nWidth * 4); + DecodeColorBlock(pImPos, pBlock, m_nWidth, (DWORD*)&col_0, (DWORD*)&col_1, + (DWORD*)&col_2, (DWORD*)&col_3); + DecodeAlpha3BitLinear(pImPos, pAlphaBlock, m_nWidth, alphazero); + } + } + + QueryPerformanceCounter(& info->m_end_clk); + + info->m_nSamples ++; + info->m_interval_sum.QuadPart += info->m_end_clk.QuadPart - info->m_start_clk.QuadPart; + } +} + +void CDXTCImage::Time_Decomp5_03(int ntimes, TimingInfo * info) +{ + int n; + + info->m_nSamples = 0; + info->m_interval_sum.QuadPart = 0; + info->m_csName.Format("Timing decomp method 3: bit field struct w/ pre-shift math, for %d times\n", ntimes); + + for (n = 0; n < ntimes; n++) + { + QueryPerformanceCounter(& info->m_start_clk); + + int xblocks, yblocks; + xblocks = m_DDSD.dwWidth / 4; + yblocks = m_DDSD.dwHeight / 4; + int i,j; + DWORD * pBase = (DWORD*) m_pDecompBytes; + DWORD * pImPos = (DWORD*) pBase; // pos in decompressed data + WORD * pPos = (WORD*) m_pCompBytes; // pos in compressed data + DXTColBlock * pBlock; + DXTAlphaBlock3BitLinear * pAlphaBlock; + Color8888 col_0, col_1, col_2, col_3; + WORD wrd; + + // fill alphazero with appropriate value to zero out alpha when + // alphazero is ANDed with the image color 32 bit DWORD: + col_0.a = 0; + col_0.r = col_0.g = col_0.b = 0xff; + DWORD alphazero = *((DWORD*) &col_0); + + // ** See DecompressDXT5 code for comments!! + for (j = 0; j < yblocks; ++j) + { + pBlock = (DXTColBlock*) ((DWORD)m_pCompBytes + j * xblocks * 16); + for (i = 0; i < xblocks; ++i, ++pBlock) + { + pAlphaBlock = (DXTAlphaBlock3BitLinear*) pBlock; + pBlock++; + + GetColorBlockColors_m3(pBlock, &col_0, &col_1, &col_2, &col_3, wrd); + + pImPos = (DWORD*)((DWORD)pBase + i*16 + (j*4) * m_nWidth * 4); + DecodeColorBlock(pImPos, pBlock, m_nWidth, (DWORD*)&col_0, (DWORD*)&col_1, + (DWORD*)&col_2, (DWORD*)&col_3); + DecodeAlpha3BitLinear(pImPos, pAlphaBlock, m_nWidth, alphazero); + } + } + + QueryPerformanceCounter(& info->m_end_clk); + + info->m_nSamples ++; + info->m_interval_sum.QuadPart += info->m_end_clk.QuadPart - info->m_start_clk.QuadPart; + } +} + + +void CDXTCImage::Time_Decomp5_04(int ntimes, TimingInfo * info) +{ + int n; + + info->m_nSamples = 0; + info->m_interval_sum.QuadPart = 0; + info->m_csName.Format("Timing decomp method 4: shift extract w/ pre-shift math, for %d times\n", ntimes); + + QueryPerformanceCounter(& info->m_start_clk); + + for (n = 0; n < ntimes; n++) + { + int xblocks, yblocks; + xblocks = m_DDSD.dwWidth / 4; + yblocks = m_DDSD.dwHeight / 4; + int i,j; + DWORD * pBase = (DWORD*) m_pDecompBytes; + DWORD * pImPos = (DWORD*) pBase; // pos in decompressed data + WORD * pPos = (WORD*) m_pCompBytes; // pos in compressed data + DXTColBlock * pBlock; + DXTAlphaBlock3BitLinear * pAlphaBlock; + Color8888 col_0, col_1, col_2, col_3; + WORD wrd; + // fill alphazero with appropriate value to zero out alpha when + // alphazero is ANDed with the image color 32 bit DWORD: + col_0.a = 0; + col_0.r = col_0.g = col_0.b = 0xff; + DWORD alphazero = *((DWORD*) &col_0); + + // ** See DecompressDXT5 code for comments!! + for (j = 0; j < yblocks; ++j) + { + pBlock = (DXTColBlock*) ((DWORD)m_pCompBytes + j * xblocks * 16); + for (i = 0; i < xblocks; ++i, ++pBlock) + { + pAlphaBlock = (DXTAlphaBlock3BitLinear*) pBlock; + pBlock++; + + GetColorBlockColors_m4(pBlock, &col_0, &col_1, &col_2, &col_3, wrd); + + pImPos = (DWORD*)((DWORD)pBase + i*16 + (j*4) * m_nWidth * 4); + DecodeColorBlock(pImPos, pBlock, m_nWidth, (DWORD*)&col_0, (DWORD*)&col_1, + (DWORD*)&col_2, (DWORD*)&col_3); + DecodeAlpha3BitLinear(pImPos, pAlphaBlock, m_nWidth, alphazero); + } + } + } + + QueryPerformanceCounter(& info->m_end_clk); + + info->m_nSamples = ntimes; + info->m_interval_sum.QuadPart += info->m_end_clk.QuadPart - info->m_start_clk.QuadPart; + +} +*/ diff --git a/src/platform/EterImageLib/Image.cpp b/src/platform/EterImageLib/Image.cpp index e4a4fdfd..9056a534 100644 --- a/src/platform/EterImageLib/Image.cpp +++ b/src/platform/EterImageLib/Image.cpp @@ -1,92 +1,144 @@ -// Platform skeleton for EterImageLib/Image.h (40250 EterImageLib/Image.cpp), generated by platform_stub.py. -// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation. +// 40250 EterImageLib/Image.cpp, verbatim. #include "EterImageLib/StdAfx.h" + +#include #include "EterImageLib/Image.h" -#include "../PlatformStub.h" +CImage::CImage(CImage & image) +{ + Initialize(); + + int w = image.GetWidth(); + int h = image.GetHeight(); + + Create(w, h); + + DWORD * pdwDest = GetBasePointer(); + DWORD * pdwSrc = image.GetBasePointer(); + + memcpy(pdwDest, pdwSrc, w * h * sizeof(DWORD)); +} + +void CImage::SetFileName(const char* c_szFileName) +{ + m_stFileName = c_szFileName; +} + +const std::string& CImage::GetFileNameString() +{ + return m_stFileName; +} + +void CImage::PutImage(int x, int y, CImage* pImage) +{ + assert(x >= 0 && x + pImage->GetWidth() <= GetWidth()); + assert(y >= 0 && y + pImage->GetHeight() <= GetHeight()); + + int len = pImage->GetWidth() * sizeof(DWORD); + + for (int j = 0; j < pImage->GetHeight(); ++j) + { + DWORD * pdwDest = GetLinePointer(y + j) + x; + memcpy(pdwDest, pImage->GetLinePointer(j), len); + } +} + +DWORD* CImage::GetBasePointer() +{ + assert(m_pdwColors != NULL); + return m_pdwColors; +} + +DWORD* CImage::GetLinePointer(int line) +{ + assert(m_pdwColors != NULL); + return m_pdwColors + line * m_width; +} + +int CImage::GetWidth() const +{ + assert(m_pdwColors != NULL); + return m_width; +} + +int CImage::GetHeight() const +{ + assert(m_pdwColors != NULL); + return m_height; +} + +void CImage::Clear(DWORD color) +{ + assert(m_pdwColors != NULL); + + for (int y = 0; y < m_height; ++y) + { + DWORD * colorLine = &m_pdwColors[y * m_width]; + + for (int x = 0; x < m_width; ++x) + colorLine[x] = color; + } +} + +void CImage::Create(int width, int height) +{ + Destroy(); + + m_width = width; + m_height = height; + m_pdwColors = new DWORD[m_width*m_height]; +} + +void CImage::Destroy() +{ + if (m_pdwColors) + { + delete [] m_pdwColors; + m_pdwColors = NULL; + } +} + +void CImage::Initialize() +{ + m_pdwColors = NULL; + m_width = 0; + m_height = 0; +} + +bool CImage::IsEmpty() const +{ + return (m_pdwColors == NULL) ? true : false; +} + +void CImage::FlipTopToBottom() +{ + DWORD * swap = new DWORD[m_width * m_height]; + + int row; + UINT width = GetWidth(); + UINT height = GetHeight(); + DWORD * end_row; + DWORD * start_row; + + for (row = 0; row < GetHeight() / 2; row++) + { + end_row = &(m_pdwColors[width * (height - row - 1)]); + start_row = &(m_pdwColors[width * row]); + + memcpy(swap, end_row, width * sizeof(DWORD)); + memcpy(end_row, start_row, width * sizeof(DWORD)); + memcpy(start_row, swap, width * sizeof(DWORD)); + } + + delete [] swap; +} CImage::CImage() { - MT_PLATFORM_STUB(); -} - -CImage::CImage(CImage &) -{ - MT_PLATFORM_STUB(); + Initialize(); } CImage::~CImage() { - MT_PLATFORM_STUB(); -} - -auto CImage::Destroy() -> void -{ - MT_PLATFORM_STUB(); -} - -auto CImage::Create(int, int) -> void -{ - MT_PLATFORM_STUB(); -} - -auto CImage::Clear(DWORD) -> void -{ - MT_PLATFORM_STUB(); -} - -auto CImage::GetWidth() const -> int -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CImage::GetHeight() const -> int -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CImage::GetBasePointer() -> DWORD * -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CImage::GetLinePointer(int) -> DWORD * -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CImage::PutImage(int, int, CImage *) -> void -{ - MT_PLATFORM_STUB(); -} - -auto CImage::FlipTopToBottom() -> void -{ - MT_PLATFORM_STUB(); -} - -auto CImage::SetFileName(const char *) -> void -{ - MT_PLATFORM_STUB(); -} - -auto CImage::GetFileNameString() -> const std::string & -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CImage::IsEmpty() const -> bool -{ - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); -} - -auto CImage::Initialize() -> void -{ - MT_PLATFORM_STUB(); + Destroy(); } diff --git a/src/platform/EterImageLib/TGAImage.cpp b/src/platform/EterImageLib/TGAImage.cpp index 790a7e73..70ff5d8d 100644 --- a/src/platform/EterImageLib/TGAImage.cpp +++ b/src/platform/EterImageLib/TGAImage.cpp @@ -1,72 +1,365 @@ -// Platform skeleton for EterImageLib/TGAImage.h (40250 EterImageLib/TGAImage.cpp), generated by platform_stub.py. -// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation. +// 40250 EterImageLib/TGAImage.cpp, verbatim. #include "EterImageLib/StdAfx.h" + +#include + +#include "EterBase/MappedFile.h" #include "EterImageLib/TGAImage.h" -#include "../PlatformStub.h" - -CTGAImage::CTGAImage() +CTGAImage::CTGAImage() : m_dwFlag(0) { - MT_PLATFORM_STUB(); -} - -CTGAImage::CTGAImage(CImage &) -{ - MT_PLATFORM_STUB(); } CTGAImage::~CTGAImage() { - MT_PLATFORM_STUB(); } -auto CTGAImage::Create(int, int) -> void +CTGAImage::CTGAImage(CImage &image) : m_dwFlag(0) { - MT_PLATFORM_STUB(); + int w = image.GetWidth(); + int h = image.GetHeight(); + + Create(w, h); + + DWORD * pdwDest = GetBasePointer(); + memcpy(pdwDest, image.GetBasePointer(), w * h * sizeof(DWORD)); + FlipTopToBottom(); } -auto CTGAImage::LoadFromMemory(int, const BYTE *) -> bool +void CTGAImage::Create(int width, int height) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + memset(&m_Header, 0, sizeof(m_Header)); + + m_Header.imgType = 2; + m_Header.width = (short) width; + m_Header.height = (short) height; + m_Header.colorBits = 32; + m_Header.desc = 0x08; // alpha channel 있음 + + CImage::Create(width, height); } -auto CTGAImage::LoadFromDiskFile(const char *) -> bool +bool CTGAImage::LoadFromMemory(int iSize, const BYTE * c_pbMem) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + memcpy(&m_Header, c_pbMem, 18); + c_pbMem += 18; + iSize -= 18; + + CImage::Create(m_Header.width, m_Header.height); + + UINT hxw = m_Header.width * m_Header.height; + BYTE r, g, b, a; + DWORD i; + + DWORD * pdwDest = GetBasePointer(); + + switch (m_Header.imgType) + { + case 3: // 알파만 있는 것 (1bytes per pixel, 거의 안쓰임) + { + for (i = 0; i < hxw; ++i) + { + a = (char) *(c_pbMem++); + pdwDest[i] = (a << 24) | (a << 16) | (a << 8) | a; + } + } + break; + + case 2: // 압축 안된 TGA + { + if (m_Header.colorBits == 16) // 16bit + { + for (i = 0; i < hxw; ++i) + { + WORD w; + + memcpy(&w, c_pbMem, sizeof(WORD)); + c_pbMem += sizeof(WORD); + iSize -= sizeof(WORD); + + b = (BYTE) (w & 0x1F); + g = (BYTE) ((w >> 5) & 0x1F); + r = (BYTE) ((w >> 10) & 0x1F); + + b <<= 3; + g <<= 3; + r <<= 3; + a = 0xff; + + pdwDest[i] = (a << 24) | (r << 16) | (g << 8) | b; + } + } + else if (m_Header.colorBits == 24) // 24bit + { + for (i = 0; i < hxw; ++i) + { + r = (BYTE) *(c_pbMem++); --iSize; + g = (BYTE) *(c_pbMem++); --iSize; + b = (BYTE) *(c_pbMem++); --iSize; + a = 0xff; + + pdwDest[i] = (a << 24) | (r << 16) | (g << 8) | b; + } + } + else if (m_Header.colorBits == 32) // 32bit + { + int size = GetWidth(); + size *= GetHeight() * 4; + + memcpy(pdwDest, c_pbMem, size); + c_pbMem += size; + iSize -= size; + } + } + break; + + case 10: // 압축 된 TGA (RLE) + { + BYTE rle; + + if (m_Header.colorBits == 24) + { + i = 0; + while (i < hxw) + { + rle = (BYTE) *(c_pbMem++); --iSize; + + if (rle < 0x80) // 압축 안된 곳 + { + rle++; + + while (rle) + { + b = (BYTE) *(c_pbMem++); --iSize; + g = (BYTE) *(c_pbMem++); --iSize; + r = (BYTE) *(c_pbMem++); --iSize; + a = 0xff; + pdwDest[i++] = (a << 24) | (r << 16) | (g << 8) | b; + + if (i > hxw) + { + assert(!"RLE overflow"); + printf("RLE overflow"); + return false; + } + --rle; + } + } + else + { + // 압축 된 곳 + rle -= 127; + + b = (BYTE) *(c_pbMem++); --iSize; + g = (BYTE) *(c_pbMem++); --iSize; + r = (BYTE) *(c_pbMem++); --iSize; + a = 0xff; + + while (rle) + { + pdwDest[i++] = (a << 24) | (r << 16) | (g << 8) | b; + + if (i > hxw) + { + assert(!"RLE overflow"); + printf("RLE overflow"); + return false; + } + --rle; + } + } + } + } + else if (m_Header.colorBits == 32) + { + i = 0; + while (i < hxw) + { + rle = (BYTE) *(c_pbMem++); --iSize; + + if (rle < 0x80) + { + rle++; + + while (rle) + { + b = (BYTE) *(c_pbMem++); --iSize; + g = (BYTE) *(c_pbMem++); --iSize; + r = (BYTE) *(c_pbMem++); --iSize; + a = (BYTE) *(c_pbMem++); --iSize; + pdwDest[i++] = (a << 24) | (r << 16) | (g << 8) | b; + + if (i > hxw) + { + assert(!"RLE overflow"); + printf("RLE overflow"); + return false; + } + --rle; + } + } + else + { + rle -= 127; + + b = (BYTE) *(c_pbMem++); --iSize; + g = (BYTE) *(c_pbMem++); --iSize; + r = (BYTE) *(c_pbMem++); --iSize; + a = (BYTE) *(c_pbMem++); --iSize; + + while (rle) + { + pdwDest[i++] = (a << 24) | (r << 16) | (g << 8) | b; + + if (i > hxw) + { + assert(!"RLE overflow"); + printf("RLE overflow"); + return false; + } + + --rle; + } + } + } + } + } + break; + } + + if (!(m_Header.desc & 0x20)) + { + FlipTopToBottom(); + } + + return true; } -auto CTGAImage::SaveToDiskFile(const char *) -> bool +bool CTGAImage::LoadFromDiskFile(const char * c_szFileName) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + CMappedFile file; + + const BYTE * c_pbMap; + + if (!file.Create(c_szFileName, (const void **) &c_pbMap, 0, 0)) + return false; + + return LoadFromMemory(file.Size(), c_pbMap); } -auto CTGAImage::SetCompressed(bool) -> void +int CTGAImage::GetRLEPixelCount(const DWORD * data) { - MT_PLATFORM_STUB(); + int r = 0; + DWORD pixel; + + r = 1; + + if (data >= m_pdwEndPtr) + return 0; + + pixel = *data; + + while ((r < 127) && (data < m_pdwEndPtr)) + { + if (pixel != *(++data)) + return r; + + r++; + } + + return r; } -auto CTGAImage::SetAlphaChannel(bool) -> void +int CTGAImage::GetRawPixelCount(const DWORD * data) { - MT_PLATFORM_STUB(); + int i = 0; + + if (data >= m_pdwEndPtr) + return 0; + + while ((data < m_pdwEndPtr) && (i < 127)) + { + int rle = GetRLEPixelCount(data); + + if (rle >= 4) + break; + + data++; + i++; + } + + return i; } -auto CTGAImage::GetHeader() -> TGA_HEADER & +void CTGAImage::SetCompressed(bool isCompress) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + if (isCompress) + m_Header.imgType = 10; + else + m_Header.imgType = 2; } -auto CTGAImage::GetRawPixelCount(const DWORD *) -> int +void CTGAImage::SetAlphaChannel(bool isExist) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + if (isExist) + m_Header.desc |= 0x08; + else + m_Header.desc &= ~0x08; } -auto CTGAImage::GetRLEPixelCount(const DWORD *) -> int +bool CTGAImage::SaveToDiskFile(const char* c_szFileName) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + FILE * fp = fopen(c_szFileName, "wb"); + + if (!fp) + return false; + + fwrite(&m_Header, 18, 1, fp); + + if (m_Header.imgType == 10) // RLE 압축으로 저장 + { + DWORD * data = GetBasePointer(); + + while (data < m_pdwEndPtr) + { + int rle = GetRLEPixelCount(data); + + if (rle < 4) + { + int raw = GetRawPixelCount(data); + + if (raw == 0) + break; + + fputc(raw - 1, fp); + + while (raw) + { + fwrite(data, sizeof(DWORD), 1, fp); + data++; + raw--; + } + } + else + { + fputc((rle - 1) | 0x80, fp); + fwrite(data, sizeof(DWORD), 1, fp); + data += rle; + } + } + } + else + { + int size = GetWidth(); + size *= GetHeight() * 4; + fwrite(GetBasePointer(), size, 1, fp); + } + + fclose(fp); + return true; +} + +TGA_HEADER & CTGAImage::GetHeader() +{ + return m_Header; } diff --git a/src/port/CMakeLists.txt b/src/port/CMakeLists.txt index 45a0e7fa..8b308b4e 100644 --- a/src/port/CMakeLists.txt +++ b/src/port/CMakeLists.txt @@ -43,6 +43,9 @@ endif() set(MT_PORT_WARNING_FLAGS) if(NOT MSVC) set(MT_PORT_WARNING_FLAGS -Wno-unknown-pragmas -Wno-format-security) + # MSVC does no type-based alias analysis and 40250 relies on it (DXTCImage reads a Color8888 through a + # DWORD*; clang -O2 then loads the colour before it is written). + list(APPEND MT_PORT_WARNING_FLAGS -fno-strict-aliasing) endif() target_compile_options(port_logic PUBLIC ${MT_PORT_WARNING_FLAGS}) # 40250 headers keep `#pragma comment(lib, "d3d8.lib")` etc. verbatim; clang turns them into ELF @@ -208,6 +211,9 @@ if(BUILD_TESTING AND CMAKE_SYSTEM_NAME STREQUAL CMAKE_HOST_SYSTEM_NAME) add_executable(port_input_test ${PROJECT_SOURCE_DIR}/tests/port/port_input_test.cpp) target_link_libraries(port_input_test PRIVATE port_platform) add_test(NAME port.input COMMAND $) + add_executable(port_eter_image_test ${PROJECT_SOURCE_DIR}/tests/port/port_eter_image_test.cpp) + target_link_libraries(port_eter_image_test PRIVATE port_platform) + add_test(NAME port.eter_image COMMAND $) # MilesLib: the 40250 sound managers over the Miles emulation (platform/MilesLib/MilesAil.cpp). add_executable(port_miles_test ${PROJECT_SOURCE_DIR}/tests/port/port_miles_test.cpp) diff --git a/src/port/common/shim/sdk/d3d.h b/src/port/common/shim/sdk/d3d.h new file mode 100644 index 00000000..4444b245 --- /dev/null +++ b/src/port/common/shim/sdk/d3d.h @@ -0,0 +1,122 @@ +#pragma once +// Shim for the DirectX 7 on non-Windows targets: the surface description 40250 +// EterImageLib/DXTCImage.cpp copies straight out of a .dds file (the 124 bytes after the "DDS " magic). +// +// PORT: lpSurface is a pointer in , 4 bytes in the 32-bit client; it is a DWORD here so the +// structure keeps the on-disk layout on 64-bit targets. +#include "../win32/windows.h" + +#define DDSD_CAPS 0x00000001l +#define DDSD_HEIGHT 0x00000002l +#define DDSD_WIDTH 0x00000004l +#define DDSD_PITCH 0x00000008l +#define DDSD_PIXELFORMAT 0x00001000l +#define DDSD_MIPMAPCOUNT 0x00020000l +#define DDSD_LINEARSIZE 0x00080000l + +#define DDPF_ALPHAPIXELS 0x00000001l +#define DDPF_FOURCC 0x00000004l +#define DDPF_RGB 0x00000040l +#define DDPF_ALPHAPREMULT 0x00008000l + +typedef struct _DDCOLORKEY +{ + DWORD dwColorSpaceLowValue; + DWORD dwColorSpaceHighValue; +} DDCOLORKEY; + +typedef struct _DDPIXELFORMAT +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwFourCC; + union + { + DWORD dwRGBBitCount; + DWORD dwYUVBitCount; + DWORD dwZBufferBitDepth; + DWORD dwAlphaBitDepth; + DWORD dwLuminanceBitCount; + DWORD dwBumpBitCount; + DWORD dwPrivateFormatBitCount; + }; + union + { + DWORD dwRBitMask; + DWORD dwYBitMask; + DWORD dwStencilBitDepth; + DWORD dwLuminanceBitMask; + DWORD dwBumpDuBitMask; + DWORD dwOperations; + }; + union + { + DWORD dwGBitMask; + DWORD dwUBitMask; + DWORD dwZBitMask; + DWORD dwBumpDvBitMask; + }; + union + { + DWORD dwBBitMask; + DWORD dwVBitMask; + DWORD dwStencilBitMask; + DWORD dwBumpLuminanceBitMask; + }; + union + { + DWORD dwRGBAlphaBitMask; + DWORD dwYUVAlphaBitMask; + DWORD dwLuminanceAlphaBitMask; + DWORD dwRGBZBitMask; + DWORD dwYUVZBitMask; + }; +} DDPIXELFORMAT; + +typedef struct _DDSCAPS2 +{ + DWORD dwCaps; + DWORD dwCaps2; + DWORD dwCaps3; + DWORD dwCaps4; +} DDSCAPS2; + +typedef struct _DDSURFACEDESC2 +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwHeight; + DWORD dwWidth; + union + { + LONG lPitch; + DWORD dwLinearSize; + }; + union + { + DWORD dwBackBufferCount; + DWORD dwDepth; + }; + union + { + DWORD dwMipMapCount; + DWORD dwRefreshRate; + DWORD dwSrcVBHandle; + }; + DWORD dwAlphaBitDepth; + DWORD dwReserved; + DWORD lpSurface; + union + { + DDCOLORKEY ddckCKDestOverlay; + DWORD dwEmptyFaceColor; + }; + DDCOLORKEY ddckCKDestBlt; + DDCOLORKEY ddckCKSrcOverlay; + DDCOLORKEY ddckCKSrcBlt; + DDPIXELFORMAT ddpfPixelFormat; + DDSCAPS2 ddsCaps; + DWORD dwTextureStage; +} DDSURFACEDESC2, *LPDDSURFACEDESC2; + +static_assert(sizeof(DDPIXELFORMAT) == 32 && sizeof(DDSURFACEDESC2) == 124, "the .dds header layout"); diff --git a/tests/port/port_eter_image_test.cpp b/tests/port/port_eter_image_test.cpp new file mode 100644 index 00000000..eace9536 --- /dev/null +++ b/tests/port/port_eter_image_test.cpp @@ -0,0 +1,125 @@ +// 40250 EterImageLib (CImage, CTGAImage, CDXTCImage), verbatim on the platform. +#include "EterImageLib/StdAfx.h" +#include "EterImageLib/TGAImage.h" +#include "EterImageLib/DXTCImage.h" + +#include +#include +#include +#include + +namespace { +// "DDS " + a 124-byte DDSURFACEDESC2 (linear size, FourCC) + the blocks of one 4x4 level. +std::vector MakeDDS(const char* fourcc, const std::vector& blocks) +{ + std::vector dds(4 + 124, 0); + memcpy(&dds[0], "DDS ", 4); + DWORD* h = reinterpret_cast(&dds[4]); + h[0] = 124; + h[1] = 0x1 | 0x2 | 0x4 | 0x1000 | 0x80000; // CAPS|HEIGHT|WIDTH|PIXELFORMAT|LINEARSIZE + h[2] = 4; + h[3] = 4; + h[4] = DWORD(blocks.size()); + h[18] = 32; // ddpfPixelFormat.dwSize (offset 72) + h[19] = 0x4; // DDPF_FOURCC + memcpy(&h[20], fourcc, 4); + dds.insert(dds.end(), blocks.begin(), blocks.end()); + return dds; +} +} + +int main() +{ + int failures = 0; + auto check = [&failures](bool ok, const char *name) { + if (!ok) { + std::fprintf(stderr, "FAIL %s\n", name); + ++failures; + } + }; + + // CImage: Clear, PutImage clips nothing, FlipTopToBottom swaps rows. + { + CImage a, b; + a.Create(4, 2); + a.Clear(0x11111111); + b.Create(2, 1); + b.Clear(0x22222222); + a.PutImage(1, 1, &b); + check(a.GetLinePointer(1)[1] == 0x22222222 && a.GetLinePointer(1)[3] == 0x11111111 && a.GetLinePointer(0)[1] == 0x11111111, "PutImage"); + a.FlipTopToBottom(); + check(a.GetLinePointer(0)[2] == 0x22222222 && a.GetLinePointer(1)[2] == 0x11111111, "FlipTopToBottom"); + check(!a.IsEmpty() && a.GetWidth() == 4 && a.GetHeight() == 2, "CImage size"); + } + + // CTGAImage: SaveToDiskFile writes the rows as stored with a bottom-left origin (desc 0x08), so loading + // the file back flips it (the CTGAImage(CImage&) constructor pre-flips for that). RLE saving is not + // exercised: 40250 never sets m_pdwEndPtr (no caller saves compressed; TargaResource only loads). + { + CTGAImage img; + img.Create(5, 3); + DWORD* p = img.GetBasePointer(); + for (int i = 0; i < 15; ++i) + p[i] = 0x80000000u + DWORD(i); + char path[] = "/tmp/mt_tga_XXXXXX"; + close(mkstemp(path)); + check(img.SaveToDiskFile(path), "TGA SaveToDiskFile"); + CTGAImage back; + check(back.LoadFromDiskFile(path), "TGA LoadFromDiskFile"); + bool flipped = back.GetWidth() == 5 && back.GetHeight() == 3; + for (int y = 0; flipped && y < 3; ++y) + for (int x = 0; x < 5; ++x) + flipped = flipped && back.GetLinePointer(y)[x] == p[(2 - y) * 5 + x]; + check(flipped, "TGA raw round trip is flipped"); + check(back.GetHeader().width == 5 && back.GetHeader().colorBits == 32, "TGA header"); + remove(path); + } + // RLE (type 10), 32-bit, top-left origin: a run packet then a raw packet, BGRA bytes. + { + std::vector tga(18, 0); + tga[2] = 10; + tga[12] = 5; + tga[14] = 1; + tga[16] = 32; + tga[17] = 0x28; + const BYTE body[] = {0x82, 1, 2, 3, 4, 0x01, 5, 6, 7, 8, 9, 10, 11, 12}; + tga.insert(tga.end(), body, body + sizeof(body)); + CTGAImage img; + check(img.LoadFromMemory(int(tga.size()), tga.data()), "TGA RLE load"); + DWORD* p = img.GetBasePointer(); + check(p[0] == 0x04030201 && p[2] == 0x04030201 && p[3] == 0x08070605 && p[4] == 0x0C0B0A09, "TGA RLE pixels"); + } + + // CDXTCImage DXT1: 565 endpoints shifted to 888 without replication, 4-colour mode thirds, rows LSB first. + { + std::vector block = {0x00, 0xF8, 0x1F, 0x00, 0xE4, 0x00, 0x00, 0x00}; + std::vector dds = MakeDDS("DXT1", block); + CDXTCImage image; + check(image.LoadFromMemory(dds.data()), "DXT1 LoadFromMemory"); + check(image.m_CompFormat == PF_DXT1 && image.m_nWidth == 4 && image.m_dwMipMapCount == 1, "DXT1 header"); + DWORD px[16] = {}; + image.Decompress(0, px); + check(px[0] == 0xFFF80000 && px[1] == 0xFF0000F8 && px[2] == 0xFFA50052 && px[3] == 0xFF5200A5, "DXT1 row 0"); + check(px[4] == 0xFFF80000 && px[15] == 0xFFF80000, "DXT1 index 0 rows"); + } + + // CDXTCImage DXT5: alpha0/alpha1 with 3-bit indices, then the colour block. + { + std::vector block = {0x80, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xF8, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00}; + std::vector dds = MakeDDS("DXT5", block); + CDXTCImage image; + check(image.LoadFromMemory(dds.data()) && image.m_CompFormat == PF_DXT5, "DXT5 load"); + DWORD px[16] = {}; + image.Decompress(0, px); + check(px[0] == 0x10F80000 && px[1] == 0x80F80000 && px[15] == 0x80F80000, "DXT5 alpha"); + } + + // A format other than DXT1/3/5 is refused. + { + std::vector dds = MakeDDS("ATI2", std::vector(16, 0)); + CDXTCImage image; + check(!image.LoadFromMemory(dds.data()), "unknown FourCC refused"); + } + return failures ? 1 : 0; +}