diff options
author | quou <quou@disroot.org> | 2025-01-01 15:26:48 +1100 |
---|---|---|
committer | quou <quou@disroot.org> | 2025-01-01 15:37:09 +1100 |
commit | 568ba73c71b650f905bd1b3f60f10871316eefdc (patch) | |
tree | a7c604c2a24b0dfd694096bd9dd9f0fdeb1d7ad1 /convtexture.c | |
parent | 221d157d069ab248c9f11653fa37a670d5a7a212 (diff) |
BC4 and BC5 compression
Diffstat (limited to 'convtexture.c')
-rw-r--r-- | convtexture.c | 130 |
1 files changed, 127 insertions, 3 deletions
diff --git a/convtexture.c b/convtexture.c index a321a9d..b3be7dc 100644 --- a/convtexture.c +++ b/convtexture.c @@ -23,6 +23,11 @@ typedef struct { unsigned char indices[4 * 4]; } Block; +typedef struct { + float start, end; + unsigned char indices[4 * 4]; +} Block_BC4; + const char* format_names[] = { #define x(n) #n, texture_format_xmacro() @@ -36,6 +41,10 @@ unsigned encode_endpoint(const vec3* v) { ((unsigned)(v->b * 31.9999f)); } +unsigned encode_endpoint_bc4(float v) { + return (unsigned)(v * 255.0f); +} + void get_block(Block* block, Colour* pixels, int stride) { int x, y, i; vec3 cols[4 * 4]; @@ -125,13 +134,97 @@ void compress_block(const Block* block, FILE* f) { fwrite(&start, 1, 2, f); fwrite(&end, 1, 2, f); for (i = 0; i < 4 * 4; i++) { - int idx = (4 * 4 - 1) - i; - idx = i; - indices |= block->indices[i] << (idx * 2); + indices |= block->indices[i] << (i * 2); } fwrite(&indices, 1, 4, f); } +void get_block_bc4( + Block_BC4* block, + Colour* pixels, + int stride, + int channel +) { + int x, y; + float palette[8]; + palette[0] = 0.08f; + palette[1] = 0.92f; + float biggest = -1.0f; + float smallest = 1.0f; + for (y = 0; y < 4; y++) { + for (x = 0; x < 4; x++) { + uint8_t* pixel = (uint8_t*)&pixels[x + y * stride]; + float v = (float)pixel[channel] / 255.0f; + if (v > biggest) biggest = v; + if (v < smallest) smallest = v; + if (v > palette[0]) palette[0] = v; + if (v < palette[1]) palette[1] = v; + } + } + if (biggest == smallest) { + int i, idx = 1; + block->end = biggest; + if (palette[0] > 0.9f) { + idx = 0; + block->start = smallest; + block->end = 0.0f; + } else + block->start = 1.0f; + assert(block->start > block->end); + for (i = 0; i < 16; i++) + block->indices[i] = idx; + } + if (biggest > 0.92f && smallest < 0.08f) { + float t = palette[1]; + palette[1] = palette[0]; + palette[0] = t; + palette[2] = (4.0f * palette[0] + 1.0f * palette[1]) / 5.0f; + palette[3] = (3.0f * palette[0] + 2.0f * palette[1]) / 5.0f; + palette[4] = (2.0f * palette[0] + 3.0f * palette[1]) / 5.0f; + palette[5] = (1.0f * palette[0] + 4.0f * palette[1]) / 5.0f; + palette[6] = 0.0f; + palette[7] = 1.0f; + } else { + palette[2] = (6.0f * palette[0] + 1.0f * palette[1]) / 7.0f; + palette[3] = (5.0f * palette[0] + 2.0f * palette[1]) / 7.0f; + palette[4] = (4.0f * palette[0] + 3.0f * palette[1]) / 7.0f; + palette[5] = (3.0f * palette[0] + 4.0f * palette[1]) / 7.0f; + palette[6] = (2.0f * palette[0] + 5.0f * palette[1]) / 7.0f; + palette[7] = (1.0f * palette[0] + 6.0f * palette[1]) / 7.0f; + } + block->start = palette[0]; + block->end = palette[1]; + for (y = 0; y < 4; y++) { + for (x = 0; x < 4; x++) { + uint8_t* pixel = (uint8_t*)&pixels[x + y * stride]; + float v = (float)pixel[channel] / 255.0f; + float d = 100.0f; + int i; + for (i = 0; i < 8; i++) { + float d2 = (v - palette[i]); + d2 = d2 < 0.0f? -d2: d2; + if (d2 < d) { + d = d2; + block->indices[x + y * 4] = i; + } + } + } + } +} + +void compress_block_bc4(const Block_BC4* block, FILE* f) { + int i; + unsigned start = encode_endpoint_bc4(block->start); + unsigned end = encode_endpoint_bc4(block->end); + uint64_t indices = 0; + fwrite(&start, 1, 1, f); + fwrite(&end, 1, 1, f); + for (i = 0; i < 4 * 4; i++) { + indices |= ((uint64_t)block->indices[i]) << (i * 3); + } /* 48 bits of indices */ + fwrite(&indices, 1, 6, f); +} + void compress_bc1(Colour* pixels, int w, int h, FILE* f) { int x, y, cw, ch; Block block; @@ -145,6 +238,31 @@ void compress_bc1(Colour* pixels, int w, int h, FILE* f) { } } +void compress_bc4(Colour* pixels, int w, int h, FILE* f) { + int x, y, cw = w / 4, ch = h / 4; + Block_BC4 block; + for (y = 0; y < ch; y++) { + for (x = 0; x < cw; x++) { + get_block_bc4(&block, &pixels[x * 4 + y * 4 * w], w, 0); + compress_block_bc4(&block, f); + } + } +} + +void compress_bc5(Colour* pixels, int w, int h, FILE* f) { + int x, y, cw = w / 4, ch = h / 4; + Block_BC4 a, b; + for (y = 0; y < ch; y++) { + for (x = 0; x < cw; x++) { + Colour* p = &pixels[x * 4 + y * 4 * w]; + get_block_bc4(&a, p, w, 0); + get_block_bc4(&b, p, w, 1); + compress_block_bc4(&a, f); + compress_block_bc4(&b, f); + } + } +} + void convert(Image* image, Texture_Format target, FILE* f) { fwrite("TXTR", 1, 4, f); fwrite(&image->w, 1, 4, f); @@ -154,6 +272,12 @@ void convert(Image* image, Texture_Format target, FILE* f) { case texture_format_bc1: compress_bc1(image->pixels, image->w, image->h, f); break; + case texture_format_bc4: + compress_bc4(image->pixels, image->w, image->h, f); + break; + case texture_format_bc5: + compress_bc5(image->pixels, image->w, image->h, f); + break; default: print_err("Unsupported target format.\n"); pbreak(40); |