// x-run: make test-cpi2png #include #include #include #include #include #include #include #include "cc-common.h" bool read_varint(FILE *fp, unsigned int *out); 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) && "Failed to read width varint"); assert(read_varint(fp_in, &height) && "Failed to read height varint"); } else { assert(false && "Failed to read size: unsupported version"); } union color *canvas = malloc(width * height * 8 * 11 * 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 = calloc(16, sizeof(union color)); // 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; } for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { unsigned char sym = fgetc(fp_in); unsigned char color = fgetc(fp_in); union color background = colors[color & 0xF]; union color foreground = colors[color >> 4]; for (int oy = 0; oy < 11; oy++) { for (int ox = 0; ox < 8; ox++) { union color pix = ((0x80 >> ox) & cc_font_atlas[sym][oy]) ? foreground : background; canvas[ox + (x + (y * 11 + oy) * width) * 8] = pix; } } } } stbi_write_png(argv[2], width * 8, height * 11, 4, canvas, 0); free(colors); free(canvas); fclose(fp_in); return EXIT_SUCCESS; } bool read_varint(FILE *fp, unsigned int *out) { int position = 0; while (true) { unsigned char curr = fgetc(fp); *out |= (curr & 0x7F) << position; if ((curr & 0x80) == 0) break; position += 7; if (position >= 32) return false; } return true; }