OpenMP, CPIv1 writer and proper size for converter
Also moved *_varint into commons, writers are now separate functions, maybe I could move them into commons sometime and add readers in case I would use them at some point.
This commit is contained in:
parent
d0cf362c1b
commit
c45e9f88bc
3
Makefile
3
Makefile
|
@ -18,4 +18,7 @@ dependencies/stb/%.o: dependencies/stb/%.h
|
||||||
wsvpn: wsvpn.o dependencies/mongoose/mongoose.o
|
wsvpn: wsvpn.o dependencies/mongoose/mongoose.o
|
||||||
$(CC) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o "$@"
|
$(CC) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o "$@"
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) -v img2cpi cpi2png wsvpn wsvpn.o cc-common.o dependencies/stb/*.o
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
|
|
31
cc-common.c
31
cc-common.c
|
@ -1,5 +1,36 @@
|
||||||
#include "cc-common.h"
|
#include "cc-common.h"
|
||||||
|
|
||||||
|
int 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 -position / 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (position + 7) / 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
int write_varint(FILE *fp, unsigned int in) {
|
||||||
|
unsigned mask = 0xFFFFFF80;
|
||||||
|
int written = 0;
|
||||||
|
while (true) {
|
||||||
|
if ((in & mask) == 0) {
|
||||||
|
fputc(in & 0xff, fp);
|
||||||
|
return written + 1;
|
||||||
|
}
|
||||||
|
fputc((in & 0x7F) | 0x80, fp);
|
||||||
|
written++;
|
||||||
|
in >>= 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const struct palette cc_default_palette = PALETTE(
|
const struct palette cc_default_palette = PALETTE(
|
||||||
{ { 0xf0, 0xf0, 0xf0, 0xff } },
|
{ { 0xf0, 0xf0, 0xf0, 0xff } },
|
||||||
{ { 0xf2, 0xb2, 0x33, 0xff } },
|
{ { 0xf2, 0xb2, 0x33, 0xff } },
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
#define _CC_COMMON_H_
|
#define _CC_COMMON_H_
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
typedef uint8_t GlyphBitmap[11];
|
typedef uint8_t GlyphBitmap[11];
|
||||||
|
|
||||||
|
@ -22,4 +24,8 @@ struct palette {
|
||||||
const extern GlyphBitmap cc_font_atlas[256];
|
const extern GlyphBitmap cc_font_atlas[256];
|
||||||
const extern struct palette cc_default_palette, cc_default_gray_palette;
|
const extern struct palette cc_default_palette, cc_default_gray_palette;
|
||||||
|
|
||||||
|
|
||||||
|
int read_varint(FILE *fp, unsigned int *out);
|
||||||
|
int write_varint(FILE *fp, unsigned int in);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
33
cpi2png.c
33
cpi2png.c
|
@ -10,8 +10,6 @@
|
||||||
|
|
||||||
#include "cc-common.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) {
|
if (argc < 3) {
|
||||||
fprintf(stderr, "Usage: %s [input.cpi] [output.png]\n", argv[0]);
|
fprintf(stderr, "Usage: %s [input.cpi] [output.png]\n", argv[0]);
|
||||||
|
@ -43,8 +41,8 @@ int main(int argc, char **argv) {
|
||||||
height = fgetc(fp_in);
|
height = fgetc(fp_in);
|
||||||
(void)fgetc(fp_in); // XXX: ignore scale
|
(void)fgetc(fp_in); // XXX: ignore scale
|
||||||
} else if (version == 1) {
|
} else if (version == 1) {
|
||||||
assert(read_varint(fp_in, &width) && "Failed to read width varint");
|
assert(read_varint(fp_in, &width) > 0 && "Failed to read width varint");
|
||||||
assert(read_varint(fp_in, &height) && "Failed to read height varint");
|
assert(read_varint(fp_in, &height) > 0 && "Failed to read height varint");
|
||||||
} else {
|
} else {
|
||||||
assert(false && "Failed to read size: unsupported version");
|
assert(false && "Failed to read size: unsupported version");
|
||||||
}
|
}
|
||||||
|
@ -55,7 +53,7 @@ int main(int argc, char **argv) {
|
||||||
// XXX: may change in future when we introduce variable-size palettes
|
// 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
|
// 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?
|
// worse image quality with less colors when I can use all of them?
|
||||||
union color *colors = calloc(16, sizeof(union color));
|
union color colors[16] = { 0 };
|
||||||
|
|
||||||
// NOTE: our `union color` type is 4 bytes long, while palette stored in the
|
// 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,
|
// file itself uses 3 bytes per color, so we can't just `fread` them at once,
|
||||||
|
@ -67,10 +65,13 @@ int main(int argc, char **argv) {
|
||||||
colors[i].rgba.a = 0xff;
|
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 y = 0; y < height; y++) {
|
||||||
for (int x = 0; x < width; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
unsigned char sym = fgetc(fp_in);
|
unsigned char sym = buffer[(x + y * width) * 2];
|
||||||
unsigned char color = fgetc(fp_in);
|
unsigned char color = buffer[(x + y * width) * 2 + 1];
|
||||||
union color background = colors[color & 0xF];
|
union color background = colors[color & 0xF];
|
||||||
union color foreground = colors[color >> 4];
|
union color foreground = colors[color >> 4];
|
||||||
for (int oy = 0; oy < 9; oy++) {
|
for (int oy = 0; oy < 9; oy++) {
|
||||||
|
@ -84,25 +85,7 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
stbi_write_png(argv[2], width * 6, height * 9, 4, canvas, 0);
|
stbi_write_png(argv[2], width * 6, height * 9, 4, canvas, 0);
|
||||||
|
|
||||||
free(colors);
|
|
||||||
free(canvas);
|
free(canvas);
|
||||||
fclose(fp_in);
|
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;
|
|
||||||
}
|
|
||||||
|
|
94
img2cpi.c
94
img2cpi.c
|
@ -13,6 +13,9 @@
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "cc-common.h"
|
#include "cc-common.h"
|
||||||
|
#ifdef USE_OPENMP
|
||||||
|
#include <omp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define MAX_COLOR_DIFFERENCE 768
|
#define MAX_COLOR_DIFFERENCE 768
|
||||||
#define K_MEANS_ITERATIONS 4
|
#define K_MEANS_ITERATIONS 4
|
||||||
|
@ -91,17 +94,23 @@ struct k_means_state {
|
||||||
|
|
||||||
bool parse_cmdline(int argc, char **argv);
|
bool parse_cmdline(int argc, char **argv);
|
||||||
void show_help(const char *progname, bool show_all, FILE *fp);
|
void show_help(const char *progname, bool show_all, FILE *fp);
|
||||||
|
|
||||||
struct image *image_load(const char *fp);
|
struct image *image_load(const char *fp);
|
||||||
struct image *image_new(int w, int h);
|
struct image *image_new(int w, int h);
|
||||||
struct image *image_resize(struct image *original, int new_w, int new_h);
|
struct image *image_resize(struct image *original, int new_w, int new_h);
|
||||||
struct image_pal *image_quantize(struct image *original, const struct palette *palette);
|
struct image_pal *image_quantize(struct image *original, const struct palette *palette);
|
||||||
|
void image_unload(struct image *img);
|
||||||
|
|
||||||
float get_color_difference(union color a, union color b);
|
float get_color_difference(union color a, union color b);
|
||||||
float get_color_brightness(union color clr);
|
float get_color_brightness(union color clr);
|
||||||
void image_unload(struct image *img);
|
|
||||||
void get_size_keep_aspect(int w, int h, int dw, int dh, int *ow, int *oh);
|
void get_size_keep_aspect(int w, int h, int dw, int dh, int *ow, int *oh);
|
||||||
|
|
||||||
void convert_2x3(const struct image_pal *img, struct cc_char *characters);
|
void convert_2x3(const struct image_pal *img, struct cc_char *characters);
|
||||||
void convert_8x11(const struct image_pal *img, struct cc_char *characters);
|
void convert_6x9(const struct image_pal *img, struct cc_char *characters);
|
||||||
|
|
||||||
|
int save_cpi_0(FILE *fp, const struct palette *pal, const struct cc_char *chars, int w, int h);
|
||||||
|
int save_cpi_1(FILE *fp, const struct palette *pal, const struct cc_char *chars, int w, int h);
|
||||||
|
|
||||||
// Only one global custom palette is maintained
|
// Only one global custom palette is maintained
|
||||||
struct palette *custom_palette_resize(uint8_t size);
|
struct palette *custom_palette_resize(uint8_t size);
|
||||||
|
@ -172,7 +181,7 @@ int main(int argc, char **argv) {
|
||||||
if (args.fast_mode) {
|
if (args.fast_mode) {
|
||||||
canvas = image_new(args.width * 2, args.height * 3);
|
canvas = image_new(args.width * 2, args.height * 3);
|
||||||
} else {
|
} else {
|
||||||
canvas = image_new(args.width * 8, args.height * 11);
|
canvas = image_new(args.width * 6, args.height * 9);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!canvas) {
|
if (!canvas) {
|
||||||
|
@ -225,23 +234,14 @@ int main(int argc, char **argv) {
|
||||||
if (args.fast_mode) {
|
if (args.fast_mode) {
|
||||||
convert_2x3(quantized_image, characters);
|
convert_2x3(quantized_image, characters);
|
||||||
} else {
|
} else {
|
||||||
convert_8x11(quantized_image, characters);
|
convert_6x9(quantized_image, characters);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: implement something other than CPIv0
|
|
||||||
FILE *fp = fopen(args.output_path, "wb");
|
FILE *fp = fopen(args.output_path, "wb");
|
||||||
fwrite("CCPI", 1, 4, fp);
|
if (args.width < 256) {
|
||||||
fputc(args.width, fp);
|
save_cpi_0(fp, palette, characters, args.width, args.height);
|
||||||
fputc(args.height, fp);
|
} else {
|
||||||
fputc(0x00, fp);
|
save_cpi_1(fp, palette, characters, args.width, args.height);
|
||||||
for (int i = 0; i < 16; i++) {
|
|
||||||
fputc(palette->colors[i].rgba.r, fp);
|
|
||||||
fputc(palette->colors[i].rgba.g, fp);
|
|
||||||
fputc(palette->colors[i].rgba.b, fp);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < args.width * args.height; i++) {
|
|
||||||
fputc(characters[i].character, fp);
|
|
||||||
fputc(characters[i].bg | (characters[i].fg << 4), fp);
|
|
||||||
}
|
}
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
|
@ -250,6 +250,47 @@ int main(int argc, char **argv) {
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int _write_palette_full(FILE *fp, const struct palette *pal) {
|
||||||
|
int written = 0;
|
||||||
|
assert(pal->count == 16 && "Invalid palette size");
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
written += fputc(pal->colors[i].rgba.r, fp);
|
||||||
|
written += fputc(pal->colors[i].rgba.g, fp);
|
||||||
|
written += fputc(pal->colors[i].rgba.b, fp);
|
||||||
|
}
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _write_pixeldata_v0(FILE *fp, const struct cc_char *chars, int w, int h) {
|
||||||
|
int written = 0;
|
||||||
|
for (int i = 0; i < w * h; i++) {
|
||||||
|
written += fputc(chars[i].character, fp);
|
||||||
|
written += fputc(chars[i].bg | (chars[i].fg << 4), fp);
|
||||||
|
}
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
|
int save_cpi_0(FILE *fp, const struct palette *pal, const struct cc_char *chars, int w, int h) {
|
||||||
|
int written = 0;
|
||||||
|
written += fwrite("CCPI", 1, 4, fp);
|
||||||
|
written += fputc(w, fp);
|
||||||
|
written += fputc(h, fp);
|
||||||
|
written += fputc(0x00, fp);
|
||||||
|
written += _write_palette_full(fp, pal);
|
||||||
|
written += _write_pixeldata_v0(fp, chars, w, h);
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
|
int save_cpi_1(FILE *fp, const struct palette *pal, const struct cc_char *chars, int w, int h) {
|
||||||
|
int written = 0;
|
||||||
|
written += fwrite("CPI\x01", 1, 4, fp);
|
||||||
|
written += write_varint(fp, w);
|
||||||
|
written += write_varint(fp, h);
|
||||||
|
written += _write_palette_full(fp, pal);
|
||||||
|
written += _write_pixeldata_v0(fp, chars, w, h);
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
bool parse_cmdline(int argc, char **argv) {
|
bool parse_cmdline(int argc, char **argv) {
|
||||||
static struct option options[] = {
|
static struct option options[] = {
|
||||||
{ "help", no_argument, 0, 'h' },
|
{ "help", no_argument, 0, 'h' },
|
||||||
|
@ -593,8 +634,8 @@ void convert_2x3(const struct image_pal *img, struct cc_char *characters) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void convert_8x11(const struct image_pal *img, struct cc_char *characters) {
|
void convert_6x9(const struct image_pal *img, struct cc_char *characters) {
|
||||||
int w = img->w / 8, h = img->h / 11;
|
int w = img->w / 6, h = img->h / 9;
|
||||||
float palette_self_diffs[0x100][0x10] = {{(float) 0xffffff}};
|
float palette_self_diffs[0x100][0x10] = {{(float) 0xffffff}};
|
||||||
for (int input_color = 0x0; input_color < 0x100 && input_color < img->palette->count; input_color++) {
|
for (int input_color = 0x0; input_color < 0x100 && input_color < img->palette->count; input_color++) {
|
||||||
for (int output_color = 0x0; output_color < 0x10 && output_color < img->palette->count; output_color++) {
|
for (int output_color = 0x0; output_color < 0x10 && output_color < img->palette->count; output_color++) {
|
||||||
|
@ -603,12 +644,15 @@ void convert_8x11(const struct image_pal *img, struct cc_char *characters) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int y = 0; y < h; y++) {
|
for (int y = 0; y < h; y++) {
|
||||||
|
#ifdef USE_OPENMP
|
||||||
|
#pragma omp parallel for
|
||||||
|
#endif
|
||||||
for (int x = 0; x < w; x++) {
|
for (int x = 0; x < w; x++) {
|
||||||
float chunk_palette_diffs[8][11][0x10] = {{{(float) 0xffffff}}};
|
float chunk_palette_diffs[6][9][0x10] = {{{(float) 0xffffff}}};
|
||||||
for (int ox = 0; ox < 8; ox++) {
|
for (int ox = 0; ox < 6; ox++) {
|
||||||
for (int oy = 0; oy < 11; oy++) {
|
for (int oy = 0; oy < 9; oy++) {
|
||||||
uint8_t pixel_unresolved = img->pixels[
|
uint8_t pixel_unresolved = img->pixels[
|
||||||
ox + (x + (y * 11 + oy) * w) * 8
|
ox + (x + (y * 9 + oy) * w) * 6
|
||||||
];
|
];
|
||||||
for (int color = 0x0; color < 0x10 && color < img->palette->count; color++) {
|
for (int color = 0x0; color < 0x10 && color < img->palette->count; color++) {
|
||||||
chunk_palette_diffs[ox][oy][color] = palette_self_diffs[pixel_unresolved][color];
|
chunk_palette_diffs[ox][oy][color] = palette_self_diffs[pixel_unresolved][color];
|
||||||
|
@ -624,9 +668,9 @@ void convert_8x11(const struct image_pal *img, struct cc_char *characters) {
|
||||||
}
|
}
|
||||||
for (int color = 0x00; color <= 0xff; color++) {
|
for (int color = 0x00; color <= 0xff; color++) {
|
||||||
float difference = 0;
|
float difference = 0;
|
||||||
for (int oy = 0; oy < 11; oy++) {
|
for (int oy = 0; oy < 9; oy++) {
|
||||||
unsigned char sym_line = cc_font_atlas[sym][oy];
|
unsigned char sym_line = cc_font_atlas[sym][oy];
|
||||||
for (int ox = 0; ox < 8; ox++) {
|
for (int ox = 0; ox < 6; ox++) {
|
||||||
bool lit = sym_line & (0x80 >> ox);
|
bool lit = sym_line & (0x80 >> ox);
|
||||||
difference += chunk_palette_diffs[ox][oy][lit ? color >> 4 : color & 0xF];
|
difference += chunk_palette_diffs[ox][oy][lit ? color >> 4 : color & 0xF];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue