layout: restructure into src/ tests/ android/ scripts/ tools/

- extension/src/{port,platform,codepage} -> src/; native_render -> src/host
  (+ dxt, shaders/); libgr2 -> src/gr2; extension/third_party -> third_party
- extension/tests -> tests/port, libgr2/tests -> tests/gr2
- android-native -> android (build.sh, push-client.sh moved in)
- script -> scripts; tools/40250 -> tools/server; oracle -> tools/granny-oracle;
  perf tools -> tools/perf
- all build trees under build/ (native, release, android, port-gate)
- xrender:: CMake aliases -> mt::; port-map ledger impl paths rewritten

No code changes. ctest 15/15, port_gate macos+android PASS, port_map check 0 errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-29 19:08:19 +09:00
co-authored by Claude Opus 5.5
parent 70710477cf
commit a46093104c
2817 changed files with 13728 additions and 13744 deletions
+212
View File
@@ -0,0 +1,212 @@
// engine/dxt —— DDS(DXT1/3/5) + 未压缩 BGRA8 → RGBA8 软解。M1 T3。
#include "dxt.h"
#include <cstdio>
#include <algorithm>
#include <cstring>
namespace mtimage {
namespace {
inline uint32_t rd_le32(const uint8_t* p) {
return uint32_t(p[0]) | (uint32_t(p[1]) << 8) | (uint32_t(p[2]) << 16) | (uint32_t(p[3]) << 24);
}
// 5:6:5 → r,g,b (0..255)
inline void unpack565(uint16_t c, int& r, int& g, int& b) {
r = ((c >> 11) & 0x1f); r = (r << 3) | (r >> 2);
g = ((c >> 5) & 0x3f); g = (g << 2) | (g >> 4);
b = (c & 0x1f); b = (b << 3) | (b >> 2);
}
// 解一个 DXT1 颜色块(8B)→ 16 像素 RGB(不写 alpha;dxt1_alpha=true 时按 1-bit alpha 写)
void decode_color_block(const uint8_t* blk, uint8_t out[16][4], bool dxt1_alpha) {
uint16_t c0 = uint16_t(blk[0] | (blk[1] << 8));
uint16_t c1 = uint16_t(blk[2] | (blk[3] << 8));
int r[4], g[4], b[4], a[4] = {255, 255, 255, 255};
unpack565(c0, r[0], g[0], b[0]);
unpack565(c1, r[1], g[1], b[1]);
if (c0 > c1 || !dxt1_alpha) {
r[2] = (2 * r[0] + r[1]) / 3; g[2] = (2 * g[0] + g[1]) / 3; b[2] = (2 * b[0] + b[1]) / 3;
r[3] = (r[0] + 2 * r[1]) / 3; g[3] = (g[0] + 2 * g[1]) / 3; b[3] = (b[0] + 2 * b[1]) / 3;
} else {
r[2] = (r[0] + r[1]) / 2; g[2] = (g[0] + g[1]) / 2; b[2] = (b[0] + b[1]) / 2;
r[3] = g[3] = b[3] = 0; a[3] = 0; // 透明
}
uint32_t bits = rd_le32(blk + 4);
for (int i = 0; i < 16; ++i) {
int idx = (bits >> (i * 2)) & 3;
out[i][0] = uint8_t(r[idx]); out[i][1] = uint8_t(g[idx]);
out[i][2] = uint8_t(b[idx]); out[i][3] = uint8_t(a[idx]);
}
}
// DXT3:alpha 块(8B)= 16 个 4-bit alpha,直接展开
void decode_dxt3_alpha(const uint8_t* blk, uint8_t out[16][4]) {
for (int i = 0; i < 8; ++i) {
int a0 = blk[i] & 0x0f, a1 = (blk[i] >> 4) & 0x0f;
out[i * 2 + 0][3] = uint8_t(a0 * 17); // 0..15 → 0..255
out[i * 2 + 1][3] = uint8_t(a1 * 17);
}
}
// DXT5:alpha 块(8B)= 2 端点 + 16×3-bit 索引
void decode_dxt5_alpha(const uint8_t* blk, uint8_t out[16][4]) {
int a0 = blk[0], a1 = blk[1];
int a[8];
a[0] = a0; a[1] = a1;
if (a0 > a1) {
for (int i = 1; i < 7; ++i) a[i + 1] = ((7 - i) * a0 + i * a1) / 7;
} else {
for (int i = 1; i < 5; ++i) a[i + 1] = ((5 - i) * a0 + i * a1) / 5;
a[6] = 0; a[7] = 255;
}
uint64_t bits = 0;
for (int i = 0; i < 6; ++i) bits |= uint64_t(blk[2 + i]) << (8 * i);
for (int i = 0; i < 16; ++i) {
int idx = int((bits >> (i * 3)) & 7);
out[i][3] = uint8_t(a[idx]);
}
}
enum Fmt { F_NONE, F_DXT1, F_DXT3, F_DXT5, F_RGB };
bool valid_mask(uint32_t mask, uint32_t bits) {
if (!mask || (bits < 32 && (mask >> bits))) return false;
while (!(mask & 1)) mask >>= 1;
return (mask & (mask + 1)) == 0; // contiguous channel bits
}
uint8_t channel(uint32_t pixel, uint32_t mask) {
if (!mask) return 255;
while (!(mask & 1)) { mask >>= 1; pixel >>= 1; }
return uint8_t((uint64_t(pixel & mask) * 255 + mask / 2) / mask);
}
void decode_dxt_level(Fmt fmt, const uint8_t* src, uint32_t w, uint32_t h, uint8_t* rgba) {
const int block_bytes = (fmt == F_DXT1) ? 8 : 16;
const size_t bx = (w + 3) / 4, by = (h + 3) / 4;
for (size_t byi = 0; byi < by; ++byi) {
for (size_t bxi = 0; bxi < bx; ++bxi) {
const uint8_t* blk = src + (byi * bx + bxi) * block_bytes;
uint8_t px[16][4];
if (fmt == F_DXT1) {
decode_color_block(blk, px, /*dxt1_alpha=*/true);
} else {
decode_color_block(blk + 8, px, /*dxt1_alpha=*/false);
if (fmt == F_DXT3) decode_dxt3_alpha(blk, px);
else decode_dxt5_alpha(blk, px);
}
for (int py = 0; py < 4; ++py) {
size_t y = byi * 4 + py;
if (y >= h) break;
for (int pxx = 0; pxx < 4; ++pxx) {
size_t x = bxi * 4 + pxx;
if (x >= w) break;
uint8_t* o = &rgba[(y * w + x) * 4];
const uint8_t* s = px[py * 4 + pxx];
o[0] = s[0]; o[1] = s[1]; o[2] = s[2]; o[3] = s[3];
}
}
}
}
}
Image decode(const uint8_t* d, size_t len) {
Image img;
if (len < 128 || std::memcmp(d, "DDS ", 4) != 0) return img;
uint32_t hsize = rd_le32(d + 4);
if (hsize != 124) return img;
uint32_t h = rd_le32(d + 12);
uint32_t w = rd_le32(d + 16);
uint32_t pf_flags = rd_le32(d + 80);
const uint8_t* fourcc = d + 84;
uint32_t rgb_bitcount = rd_le32(d + 88);
Fmt fmt = F_NONE;
if (pf_flags & 0x4) { // DDPF_FOURCC
if (!std::memcmp(fourcc, "DXT1", 4)) fmt = F_DXT1;
else if (!std::memcmp(fourcc, "DXT3", 4)) fmt = F_DXT3;
else if (!std::memcmp(fourcc, "DXT5", 4)) fmt = F_DXT5;
} else if ((pf_flags & 0x40) && (rgb_bitcount == 16 || rgb_bitcount == 24 || rgb_bitcount == 32)) {
fmt = F_RGB;
}
if (fmt == F_NONE || w == 0 || h == 0 || w > 8192 || h > 8192) return img;
const uint8_t* src = d + 128;
size_t avail = len - 128;
img.w = uint16_t(w);
img.h = uint16_t(h);
img.rgba.assign(size_t(w) * h * 4, 0);
if (fmt == F_RGB) {
const uint32_t r = rd_le32(d + 92), g = rd_le32(d + 96), b = rd_le32(d + 100);
const uint32_t a = (pf_flags & 1) ? rd_le32(d + 104) : 0;
if (!valid_mask(r, rgb_bitcount) || !valid_mask(g, rgb_bitcount) || !valid_mask(b, rgb_bitcount) ||
(a && !valid_mask(a, rgb_bitcount)) || ((pf_flags & 1) && !a) ||
(r & g) || (r & b) || (g & b) || (a & (r | g | b))) return {};
const size_t bytes = rgb_bitcount / 8, row_bytes = size_t(w) * bytes;
const size_t pitch = (rd_le32(d + 8) & 8) ? rd_le32(d + 20) : row_bytes;
if (pitch < row_bytes || avail < row_bytes || pitch > avail || (h - 1) > (avail - row_bytes) / pitch) return {};
for (size_t y = 0; y < h; ++y) {
for (size_t x = 0; x < w; ++x) {
uint32_t pixel = 0;
for (size_t k = 0; k < bytes; ++k) pixel |= uint32_t(src[y * pitch + x * bytes + k]) << (8 * k);
auto* out = &img.rgba[(y * w + x) * 4];
out[0] = channel(pixel, r); out[1] = channel(pixel, g);
out[2] = channel(pixel, b); out[3] = channel(pixel, a);
}
}
img.format = rgb_bitcount == 32 && r == 0xff0000 && b == 0xff ? "BGRA8" : "RGB_MASKED";
return img;
}
const int block_bytes = (fmt == F_DXT1) ? 8 : 16;
const size_t bx = (w + 3) / 4, by = (h + 3) / 4;
if (avail < bx * by * block_bytes) { img = Image{}; return img; }
decode_dxt_level(fmt, src, uint32_t(w), uint32_t(h), img.rgba.data());
// 40250 CDXTCImage::LoadHeaderFromMemory + CGraphicImageTexture::CreateDDSTexture:
// mipmapCount = max(1, min(dwMipMapCount, MAX_MIPLEVELS)); level i starts at
// sum(dwLinearSize >> 2k) and Copy() memcpy's dwLinearSize >> 2i bytes of it. Only
// with DDSD_MIPMAPCOUNT and without DDSD_PITCH are levels > 0 filled — otherwise 40250
// creates them uninitialized; here that case is one level.
const uint32_t dds_flags = rd_le32(d + 8);
const uint32_t linear_size = rd_le32(d + 20);
uint32_t mip_count = std::min<uint32_t>(rd_le32(d + 28), 12u);
if (mip_count == 0) mip_count = 1;
if (!(dds_flags & 0x20000u) || (dds_flags & 0x8u) || linear_size == 0) mip_count = 1;
size_t offset = linear_size;
for (uint32_t i = 1; i < mip_count; ++i) {
const uint32_t lw = std::max<uint32_t>(1, w >> i), lh = std::max<uint32_t>(1, h >> i);
const size_t level_bytes = ((lw + 3) / 4) * ((lh + 3) / 4) * size_t(block_bytes);
const size_t copy_bytes = std::min<size_t>(size_t(linear_size) >> (2 * i), level_bytes);
std::vector<uint8_t> blocks(level_bytes, 0);
if (offset < avail) std::memcpy(blocks.data(), src + offset, std::min(copy_bytes, avail - offset));
offset += size_t(linear_size) >> (2 * i);
std::vector<uint8_t> level(size_t(lw) * lh * 4, 0);
decode_dxt_level(fmt, blocks.data(), lw, lh, level.data());
img.mips.push_back(std::move(level));
}
img.file_mip_levels = uint8_t(mip_count);
img.format = (fmt == F_DXT1) ? "DXT1" : (fmt == F_DXT3) ? "DXT3" : "DXT5";
return img;
}
} // namespace
Image load_dds(const uint8_t* bytes, size_t len) { return decode(bytes, len); }
Image load_dds_path(const char* path) {
FILE* f = std::fopen(path, "rb");
if (!f) return {};
std::fseek(f, 0, SEEK_END);
long n = std::ftell(f);
std::fseek(f, 0, SEEK_SET);
std::vector<uint8_t> buf(n > 0 ? size_t(n) : 0);
size_t rd = buf.empty() ? 0 : std::fread(buf.data(), 1, buf.size(), f);
std::fclose(f);
if (rd != buf.size()) return {};
return decode(buf.data(), buf.size());
}
} // namespace mtimage