Files
mtgodot-poc/extension/tests/dxt_decode_test.cpp
T
shenleiandClaude Opus 5.5 d2356d59c3 render parity unit K: 40250 DDS mip levels
DXT DDS textures upload the file's own mip levels like 40250
CGraphicImageTexture::CreateDDSTexture (max(1, dwMipMapCount), level i read
at sum(dwLinearSize>>2k), Copy of dwLinearSize>>2i bytes) instead of a
regenerated chain. Non-DDS images keep the D3DX_DEFAULT full chain and mem:
textures get one level. The invented is_ui_tex no-mip rule (icon/, ui/,
locale/, <=128px) is deleted. dxt_decode_test is registered in ctest with
mip-level cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 14:29:30 +09:00

70 lines
4.1 KiB
C++

#include "dxt.h"
#include <cassert>
#include <cstdio>
#include <vector>
using Bytes = std::vector<uint8_t>;
void put(Bytes& b, size_t off, uint32_t v) {
for (int i = 0; i < 4; ++i) b[off + i] = uint8_t(v >> (8 * i));
}
Bytes header(uint32_t bits, uint32_t r, uint32_t g, uint32_t b, uint32_t a, size_t payload = 8) {
Bytes out(128 + payload, 0);
put(out, 0, 0x20534444); put(out, 4, 124); put(out, 12, 1); put(out, 16, 2);
put(out, 76, 32); put(out, 80, 0x40 | (a ? 1 : 0)); put(out, 88, bits);
put(out, 92, r); put(out, 96, g); put(out, 100, b); put(out, 104, a);
return out;
}
mtgodot::Image decode(const Bytes& b) { return mtgodot::load_dds(b.data(), b.size()); }
void pixel(const mtgodot::Image& im, size_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
assert(im.ok()); assert(im.rgba[n*4] == r && im.rgba[n*4+1] == g && im.rgba[n*4+2] == b && im.rgba[n*4+3] == a);
}
int main() {
auto a1555 = header(16, 0x7c00, 0x3e0, 0x1f, 0x8000);
put(a1555, 128, 0x001ffc00); // opaque red, transparent blue
pixel(decode(a1555), 0, 255, 0, 0, 255); pixel(decode(a1555), 1, 0, 0, 255, 0);
auto rgb565 = header(16, 0xf800, 0x7e0, 0x1f, 0);
put(rgb565, 128, 0x001f07e0);
pixel(decode(rgb565), 0, 0, 255, 0, 255); pixel(decode(rgb565), 1, 0, 0, 255, 255);
auto a4444 = header(16, 0xf00, 0xf0, 0xf, 0xf000);
put(a4444, 128, 0x12348f00);
pixel(decode(a4444), 0, 255, 0, 0, 136); pixel(decode(a4444), 1, 34, 51, 68, 17);
auto padded = header(24, 0xff0000, 0xff00, 0xff, 0, 8);
put(padded, 16, 1); put(padded, 12, 2); put(padded, 8, 8); put(padded, 20, 4);
put(padded, 128, 0xaaff0000); put(padded, 132, 0xbb0000ff);
pixel(decode(padded), 0, 255, 0, 0, 255); pixel(decode(padded), 1, 0, 0, 255, 255);
auto rgba = header(32, 0xff, 0xff00, 0xff0000, 0xff000000);
put(rgba, 128, 0x80402010); pixel(decode(rgba), 0, 16, 32, 64, 128);
put(rgba, 80, 0x40); pixel(decode(rgba), 0, 16, 32, 64, 255); // XRGB ignores unused alpha bits
auto invalid = a1555; invalid.resize(130); assert(!decode(invalid).ok());
invalid = padded; put(invalid, 20, 2); assert(!decode(invalid).ok());
invalid = a1555; put(invalid, 92, 0x3e0); assert(!decode(invalid).ok());
invalid = a1555; put(invalid, 92, 0x7400); assert(!decode(invalid).ok());
for (uint32_t fourcc : {0x31545844u, 0x33545844u, 0x35545844u}) {
auto dxt = header(0, 0, 0, 0, 0, 16);
put(dxt, 12, 4); put(dxt, 16, 4); put(dxt, 80, 4); put(dxt, 84, fourcc);
const bool dxt1 = fourcc == 0x31545844;
put(dxt, dxt1 ? 128 : 136, 0x0000f800);
if (fourcc == 0x33545844) { put(dxt, 128, 0xffffffff); put(dxt, 132, 0xffffffff); }
if (fourcc == 0x35545844) dxt[128] = 255;
pixel(decode(dxt), 0, 255, 0, 0, 255);
}
{ // 40250 CreateDDSTexture levels: DDSD_MIPMAPCOUNT, level i at sum(linear >> 2k),
// Copy() takes linear >> 2i bytes (the 2x2 level gets 2 of its 8 block bytes).
auto mip = header(0, 0, 0, 0, 0, 32 + 8 + 2);
put(mip, 8, 0x1007 | 0x20000 | 0x80000); put(mip, 12, 8); put(mip, 16, 8);
put(mip, 20, 32); put(mip, 28, 3); put(mip, 80, 4); put(mip, 84, 0x31545844u);
for (int blk = 0; blk < 4; ++blk) put(mip, 128 + blk * 8, 0x000007e0); // level 0 green
put(mip, 160, 0x0000001f); // level 1 blue
mip[168] = 0x00; mip[169] = 0xf8; // level 2 red
auto im = decode(mip);
assert(im.w == 8 && im.h == 8 && im.file_mip_levels == 3 && im.mips.size() == 2);
pixel(im, 63, 0, 255, 0, 255);
assert(im.mips[0].size() == 4 * 4 * 4 && im.mips[0][2] == 255 && im.mips[0][0] == 0);
assert(im.mips[1].size() == 2 * 2 * 4 && im.mips[1][0] == 255 && im.mips[1][1] == 0);
put(mip, 8, 0x1007 | 0x80000); // no DDSD_MIPMAPCOUNT -> only level 0 is filled
im = decode(mip);
assert(im.file_mip_levels == 1 && im.mips.empty());
assert(decode(a1555).file_mip_levels == 0); // non-DXT -> D3DX full-chain path
}
std::puts("PASS: dxt_decode_test (masked RGB, pitch, invalid headers, DXT1/3/5, mip levels)");
}