diff options
author | quou <quou@disroot.org> | 2024-12-23 14:41:01 +1100 |
---|---|---|
committer | quou <quou@disroot.org> | 2024-12-23 14:41:01 +1100 |
commit | b293168cc158d65f1a5146f155921ff82119d1bc (patch) | |
tree | 08cc5ee235b4b7ca6ead45c43f9b407664971613 /convtexture.c | |
parent | 58245585cbe77e6c03ebe13f29e10393ff3c45b4 (diff) |
Texture conversion and loading
Diffstat (limited to 'convtexture.c')
-rw-r--r-- | convtexture.c | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/convtexture.c b/convtexture.c new file mode 100644 index 0000000..a04e3c9 --- /dev/null +++ b/convtexture.c @@ -0,0 +1,217 @@ +#include <stdio.h> +#include <stdlib.h> + +#include "plat.h" +#include "str.h" +#include "vid_enums.h" + +typedef struct { + float r, g, b; +} vec3; + +typedef struct { + unsigned char r, g, b, a; +} Colour; + +typedef struct { + Colour* pixels; + int w, h; +} Image; + +typedef struct { + vec3 start, end; + unsigned char indices[4 * 4]; +} Block; + +const char* format_names[] = { +#define x(n) #n, + texture_format_xmacro() +#undef x +}; + +void get_block(Block* block, Colour* pixels, int stride) { + int x, y, i; + vec3 cols[4 * 4]; + vec3 s, e; + float cd; + for (y = 0; y < 4; y++) { + for (x = 0; x < 4; x++) { + Colour pixel = pixels[x + y * stride]; + vec3 vec; + vec.r = (float)pixel.r / 255.0f; + vec.g = (float)pixel.g / 255.0f; + vec.b = (float)pixel.b / 255.0f; + cols[x + y * 4] = vec; + } + } + cd = -1.0f; + for (i = 0; i < 4 * 4; i++) { + int j; + vec3* a = &cols[i]; + for (j = i + 1; j < 4 * 4; j++) { + vec3* b = &cols[j]; + float d = + (b->r - a->r) * (b->r - a->r) + + (b->g - a->g) * (b->g - a->g) + + (b->b - a->b) * (b->b - a->b); + if (d > cd) { + cd = d; + s = *a; + e = *b; + } + } + } + for (i = 0; i < 4 * 4; i++) { + vec3* col = &cols[i]; + float d; + d = + (e.r - col->r) * (e.r - col->r) + + (e.g - col->g) * (e.g - col->g) + + (e.b - col->b) * (e.b - col->b); + if (cd < 0.0001f) + block->indices[i] = 0; + else { + d = d / cd; + block->indices[i] = (int)((1.0f - d) * 3.1f); + } + } + block->start = s; + block->end = e; +} + +void compress_block(const Block* block, FILE* f) { + int i; + unsigned start = + ((unsigned)(block->start.r * 31.9999f) << 11) | + ((unsigned)(block->start.g * 63.9999f) << 5) | + ((unsigned)(block->start.b * 31.9999f)); + unsigned end = + ((unsigned)(block->end.r * 31.9999f) << 11) | + ((unsigned)(block->end.g * 63.9999f) << 5) | + ((unsigned)(block->end.b * 31.9999f)); + unsigned palette = (start << 16) | end; + unsigned indices = 0; + fwrite(&palette, 1, 4, f); + for (i = 0; i < 4 * 4; i++) { + indices |= block->indices[i] << (i * 2); + } + fwrite(&indices, 1, 4, f); +} + +void compress_bc1(Colour* pixels, int w, int h, FILE* f) { + int x, y, cw, ch; + Block block; + cw = w / 4; + ch = h / 4; + for (y = 0; y < ch; y++) { + for (x = 0; x < cw; x++) { + get_block(&block, &pixels[x * 4 + y * 4 * w], w); + compress_block(&block, f); + } + } +} + +void convert(Image* image, Texture_Format target, FILE* f) { + fwrite("TXTR", 1, 4, f); + fwrite(&image->w, 1, 4, f); + fwrite(&image->h, 1, 4, f); + fwrite(&target, 1, 4, f); + switch (target) { + case texture_format_bc1: + compress_bc1(image->pixels, image->w, image->h, f); + break; + default: + print_err("Unsupported target format.\n"); + pbreak(40); + } +} + +Texture_Format texture_format_from_string(const char* s) { + int i, e = sizeof format_names / sizeof *format_names; + for (i = 0; i < e; i++) + if (string_equal(s, format_names[i])) + return (Texture_Format)i; + print_err("Invalid texture format %s\n", s); + pbreak(10); + return (Texture_Format)0; +} + +int main(int argc, const char** argv) { + FILE* outfile, * infile; + char bmp_magic[2]; + unsigned bmp_offset; + int bmp_w, bmp_h, s, x, y; + unsigned short bmp_bits; + Colour* buffer; + Image img; + Colour pixel; + Texture_Format target; + + if (argc < 4) { + print_err("Usage: %s infile outfile format.\n", argv[0]); + return 1; + } + + infile = fopen(argv[1], "rb"); + if (!infile) { + print_err("Failed to open %s.\n", argv[1]); + return 2; + } + target = texture_format_from_string(argv[3]); + + fread(bmp_magic, 2, 1, infile); + if (bmp_magic[0] != 'B' || bmp_magic[1] != 'M') { + print_err("Not a valid bitmap file.\n"); + return 3; + } + + fseek(infile, 10, SEEK_SET); + fread(&bmp_offset, 4, 1, infile); + fseek(infile, 18, SEEK_SET); + fread(&bmp_w, 4, 1, infile); + fread(&bmp_h, 4, 1, infile); + fseek(infile, 28, SEEK_SET); + fread(&bmp_bits, 2, 1, infile); + + if (bmp_bits != 32) { + print_err("Bitmap must have 32 bit pixels. Instead has %d bit pixels.\n", bmp_bits); + return 4; + } + + if (bmp_w % 4 != 0 || bmp_h % 4 != 0) { + print_err("Bitmap must have a size divisible by four.\n"); + return 5; + } + + fseek(infile, bmp_offset, SEEK_SET); + s = bmp_w * bmp_h; + buffer = malloc(s * 4); + /* Flip & read. */ + for (y = 0; y < bmp_h; y++) { + for (x = 0; x < bmp_w; x++) { + unsigned char t; + fread(&pixel, 1, 4, infile); + t = pixel.r; + pixel.r = pixel.b; + pixel.b = t; + if (pixel.a == 0) { + pixel.r = 0; + pixel.g = 0; + pixel.b = 0; + } + buffer[x + (bmp_h - y - 1) * bmp_w] = pixel; + } + } + outfile = fopen(argv[2], "wb"); + if (!outfile) { + print_err("Failed to open %s.\n", argv[2]); + return 6; + } + img.pixels = buffer; + img.w = bmp_w; + img.h = bmp_h; + convert(&img, target, outfile); + fclose(outfile); + return 0; +} + |