CPI -> PNG renderer
Supports CPIv0 and CPIv1 for now, but other formats are experimental anyways, so not a big problem
This commit is contained in:
parent
8589b2a730
commit
d4ca60c9f9
8
Makefile
8
Makefile
|
@ -3,8 +3,14 @@ LDLIBS += -lm
|
||||||
|
|
||||||
all: img2cpi cpi2png wsvpn
|
all: img2cpi cpi2png wsvpn
|
||||||
|
|
||||||
|
test-cpi2png: cpi2png
|
||||||
|
./cpi2png ./cpi-images/rat.cpi /tmp/rat.png
|
||||||
|
|
||||||
img2cpi: img2cpi.c cc-common.o dependencies/stb/stb_image.o dependencies/stb/stb_image_resize2.o
|
img2cpi: img2cpi.c cc-common.o dependencies/stb/stb_image.o dependencies/stb/stb_image_resize2.o
|
||||||
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o "$@"
|
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o "$@" $(CPPFLAGS)
|
||||||
|
|
||||||
|
cpi2png: cpi2png.c cc-common.o dependencies/stb/stb_image_write.o
|
||||||
|
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o "$@" $(CPPFLAGS)
|
||||||
|
|
||||||
dependencies/stb/%.o: dependencies/stb/%.h
|
dependencies/stb/%.o: dependencies/stb/%.h
|
||||||
$(CC) -DSTB_IMAGE_IMPLEMENTATION -DSTB_IMAGE_RESIZE_IMPLEMENTATION -DSTB_IMAGE_WRITE_IMPLEMENTATION -x c $^ -c -o "$@"
|
$(CC) -DSTB_IMAGE_IMPLEMENTATION -DSTB_IMAGE_RESIZE_IMPLEMENTATION -DSTB_IMAGE_WRITE_IMPLEMENTATION -x c $^ -c -o "$@"
|
||||||
|
|
102
cpi2png.c
102
cpi2png.c
|
@ -1,14 +1,108 @@
|
||||||
// x-run: make cpi2png
|
// x-run: make test-cpi2png
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
|
||||||
#include <stb/stb_image.h>
|
#include <stb/stb_image.h>
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
||||||
#include <stb/stb_image_write.h>
|
#include <stb/stb_image_write.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "cc-common.h"
|
||||||
|
|
||||||
|
bool read_varint(FILE *fp, unsigned int *out);
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
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)
|
||||||
|
printf("CPIv0 (old header)\n");
|
||||||
|
} 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;
|
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;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue