cc-stuff/cpi2png.c

92 lines
2.8 KiB
C

// x-run: make test-cpi2png
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stb/stb_image.h>
#include <stb/stb_image_write.h>
#include <string.h>
#include "cc-common.h"
int main(int argc, char **argv) {
if (argc < 3) {
fprintf(stderr, "Usage: %s [input.cpi] [output.png]\n", argv[0]);
}
FILE *fp_in = fopen(argv[1], "rb");
assert(fp_in != NULL && "Failed to open input file");
unsigned char header[4];
unsigned char version = 0;
assert(fread(header, 1, 4, fp_in) == 4 && "Failed to read header: not enough bytes");
if (0 == memcmp(header, "CCPI", 4)) { // Original CCPI (CPIv0)
version = 0;
} else if (0 == memcmp(header, "CPI", 3)) { // Newer CCPI (CPIvX)
version = header[3];
} else {
assert(false && "Not a CPI/CCPI image: invalid header");
}
if (version & 0x80) {
fprintf(stderr, "Draft version: 0x%02x may not be supported properly! Here be dragons!\n", version);
}
unsigned int width = 0, height = 0;
if (version == 0) {
width = fgetc(fp_in);
height = fgetc(fp_in);
(void)fgetc(fp_in); // XXX: ignore scale
} else if (version == 1) {
assert(read_varint(fp_in, &width) > 0 && "Failed to read width varint");
assert(read_varint(fp_in, &height) > 0 && "Failed to read height varint");
} else {
assert(false && "Failed to read size: unsupported version");
}
union color *canvas = malloc(width * height * 6 * 9 * sizeof(union color));
// XXX: may change in future when we introduce variable-size palettes
// though, it may never change, if I'm being honest. Why would I choose
// worse image quality with less colors when I can use all of them?
union color colors[16] = { 0 };
// NOTE: our `union color` type is 4 bytes long, while palette stored in the
// file itself uses 3 bytes per color, so we can't just `fread` them at once,
// sadly.
for (int i = 0; i < 16; i++) {
colors[i].rgba.r = fgetc(fp_in);
colors[i].rgba.g = fgetc(fp_in);
colors[i].rgba.b = fgetc(fp_in);
colors[i].rgba.a = 0xff;
}
unsigned char *buffer = calloc(width * height, 2);
fread(buffer, 2, width * height, fp_in);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
unsigned char sym = buffer[(x + y * width) * 2];
unsigned char color = buffer[(x + y * width) * 2 + 1];
union color background = colors[color & 0xF];
union color foreground = colors[color >> 4];
for (int oy = 0; oy < 9; oy++) {
for (int ox = 0; ox < 6; ox++) {
union color pix = ((0x80 >> (ox + 1)) & cc_font_atlas[sym][oy + 1]) ? foreground : background;
canvas[ox + (x + (y * 9 + oy) * width) * 6] = pix;
}
}
}
}
stbi_write_png(argv[2], width * 6, height * 9, 4, canvas, 0);
free(canvas);
fclose(fp_in);
return EXIT_SUCCESS;
}