2024-10-03 20:28:49 +03:00
|
|
|
// x-run: make test-cpi2png
|
2024-10-03 18:57:44 +03:00
|
|
|
|
2024-10-03 20:28:49 +03:00
|
|
|
#include <assert.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdio.h>
|
2024-10-03 18:57:44 +03:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stb/stb_image.h>
|
|
|
|
#include <stb/stb_image_write.h>
|
2024-10-03 20:28:49 +03:00
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "cc-common.h"
|
2024-10-03 18:57:44 +03:00
|
|
|
|
|
|
|
int main(int argc, char **argv) {
|
2024-10-03 20:28:49 +03:00
|
|
|
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)
|
2024-10-03 20:39:04 +03:00
|
|
|
version = 0;
|
2024-10-03 20:28:49 +03:00
|
|
|
} 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) {
|
2024-10-05 12:44:16 +03:00
|
|
|
assert(read_varint(fp_in, &width) > 0 && "Failed to read width varint");
|
|
|
|
assert(read_varint(fp_in, &height) > 0 && "Failed to read height varint");
|
2024-10-03 20:28:49 +03:00
|
|
|
} else {
|
|
|
|
assert(false && "Failed to read size: unsupported version");
|
|
|
|
}
|
|
|
|
|
2024-10-03 22:25:58 +03:00
|
|
|
union color *canvas = malloc(width * height * 6 * 9 * sizeof(union color));
|
2024-10-03 20:28:49 +03:00
|
|
|
|
|
|
|
|
|
|
|
// 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?
|
2024-10-05 12:44:16 +03:00
|
|
|
union color colors[16] = { 0 };
|
2024-10-03 18:57:44 +03:00
|
|
|
|
2024-10-03 20:28:49 +03:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2024-10-05 12:44:16 +03:00
|
|
|
unsigned char *buffer = calloc(width * height, 2);
|
|
|
|
fread(buffer, 2, width * height, fp_in);
|
|
|
|
|
2024-10-03 20:28:49 +03:00
|
|
|
for (int y = 0; y < height; y++) {
|
|
|
|
for (int x = 0; x < width; x++) {
|
2024-10-05 12:44:16 +03:00
|
|
|
unsigned char sym = buffer[(x + y * width) * 2];
|
|
|
|
unsigned char color = buffer[(x + y * width) * 2 + 1];
|
2024-10-03 20:28:49 +03:00
|
|
|
union color background = colors[color & 0xF];
|
|
|
|
union color foreground = colors[color >> 4];
|
2024-10-03 22:25:58 +03:00
|
|
|
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;
|
2024-10-03 20:28:49 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-03 22:25:58 +03:00
|
|
|
stbi_write_png(argv[2], width * 6, height * 9, 4, canvas, 0);
|
2024-10-03 20:28:49 +03:00
|
|
|
|
|
|
|
free(canvas);
|
|
|
|
fclose(fp_in);
|
2024-10-03 18:57:44 +03:00
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|