#include "render.h" #include #include typedef struct { float r, g, b; } vec3; typedef struct { vec3 start, end; unsigned char indices[4 * 4]; } Block; 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; fwrite(&w, 1, 4, f); fwrite(&h, 1, 4, f); 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); } } } 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; Colour pixel; if (argc < 3) { fprintf(stderr, "Usage: %s infile outfile.\n", argv[0]); return 1; } infile = fopen(argv[1], "rb"); if (!infile) { fprintf(stderr, "Failed to open %s.\n", argv[1]); return 2; } fread(bmp_magic, 2, 1, infile); if (bmp_magic[0] != 'B' || bmp_magic[1] != 'M') { fprintf(stderr, "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) { fprintf(stderr, "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) { fprintf(stderr, "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) { fprintf(stderr, "Failed to open %s.\n", argv[2]); return 6; } compress_bc1(buffer, bmp_w, bmp_h, outfile); fclose(outfile); return 0; }