forked from hkc/cc-stuff
Implemented char-fast conversion mode
This commit is contained in:
parent
bedc6d9166
commit
53c9ba3db5
83
img2cpi.c
83
img2cpi.c
|
@ -14,6 +14,9 @@
|
||||||
|
|
||||||
#define MAX_COLOR_DIFFERENCE 768
|
#define MAX_COLOR_DIFFERENCE 768
|
||||||
#define K_MEANS_ITERATIONS 4
|
#define K_MEANS_ITERATIONS 4
|
||||||
|
#ifndef HAS_POPCNT
|
||||||
|
# define HAS_POPCNT 1
|
||||||
|
#endif
|
||||||
|
|
||||||
struct rgba { uint8_t r, g, b, a; };
|
struct rgba { uint8_t r, g, b, a; };
|
||||||
union color {
|
union color {
|
||||||
|
@ -118,7 +121,7 @@ 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_8x11(const struct image_pal *img, struct cc_char *characters, bool precise);
|
||||||
|
|
||||||
// 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);
|
||||||
|
@ -129,7 +132,13 @@ bool k_means_iteration(struct k_means_state *state);
|
||||||
void k_means_end(struct k_means_state *state);
|
void k_means_end(struct k_means_state *state);
|
||||||
struct palette *palette_k_means(const struct image *image, const struct palette *prototype);
|
struct palette *palette_k_means(const struct image *image, const struct palette *prototype);
|
||||||
|
|
||||||
inline static uint8_t closest_chunk_color_symbol(const typeof(float[8][11][0x10]) *chunk_palette_diffs, uint8_t color_pair);
|
inline static int popcnt32(uint32_t mask);
|
||||||
|
inline static int glyph_hamming_distance(const GlyphBitmap *lhs, const GlyphBitmap *rhs);
|
||||||
|
inline static float weighted_glyph_hamming_distance(const GlyphBitmap *lhs, const GlyphBitmap *rhs, const typeof(float[11][8]) *weights);
|
||||||
|
uint8_t closest_glyph_symbol_fast(const GlyphBitmap *target);
|
||||||
|
uint8_t closest_glyph_symbol_precise(const GlyphBitmap *target, const typeof(float[11][8]) *weights);
|
||||||
|
void construct_chunk_color_glyph(GlyphBitmap *result, typeof(float[11][8]) *weights, const typeof(float[8][11][0x10]) *chunk_palette_diffs, uint8_t color_pair);
|
||||||
|
inline static uint8_t closest_chunk_color_symbol(const typeof(float[8][11][0x10]) *chunk_palette_diffs, uint8_t color_pair, bool precise);
|
||||||
|
|
||||||
const char *known_file_extensions[] = {
|
const char *known_file_extensions[] = {
|
||||||
".png", ".jpg", ".jpeg", ".jfif", ".jpg", ".gif",
|
".png", ".jpg", ".jpeg", ".jfif", ".jpg", ".gif",
|
||||||
|
@ -248,10 +257,19 @@ int main(int argc, char **argv) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.character_mode == CHARACTER_BLOCK) {
|
switch (args.character_mode) {
|
||||||
|
case CHARACTER_BLOCK:
|
||||||
convert_2x3(quantized_image, characters);
|
convert_2x3(quantized_image, characters);
|
||||||
} else {
|
break;
|
||||||
convert_8x11(quantized_image, characters);
|
case CHARACTER_CHAR_PRECISE:
|
||||||
|
convert_8x11(quantized_image, characters, true);
|
||||||
|
break;
|
||||||
|
case CHARACTER_CHAR_FAST:
|
||||||
|
convert_8x11(quantized_image, characters, false);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "BUG: invalid args.character_mode\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: implement something other than CPIv0
|
// TODO: implement something other than CPIv0
|
||||||
|
@ -632,7 +650,7 @@ 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_8x11(const struct image_pal *img, struct cc_char *characters, bool precise) {
|
||||||
int w = img->w / 8, h = img->h / 11;
|
int w = img->w / 8, h = img->h / 11;
|
||||||
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++) {
|
||||||
|
@ -659,7 +677,7 @@ void convert_8x11(const struct image_pal *img, struct cc_char *characters) {
|
||||||
char closest_sym = 0x00, closest_color = 0xae;
|
char closest_sym = 0x00, closest_color = 0xae;
|
||||||
for (int color = 0x00; color <= 0xff; color++) {
|
for (int color = 0x00; color <= 0xff; color++) {
|
||||||
{
|
{
|
||||||
const int sym = closest_chunk_color_symbol(&chunk_palette_diffs, color);
|
const int sym = closest_chunk_color_symbol(&chunk_palette_diffs, color, precise);
|
||||||
float difference = 0;
|
float difference = 0;
|
||||||
for (int oy = 0; oy < 11; oy++) {
|
for (int oy = 0; oy < 11; oy++) {
|
||||||
unsigned char sym_line = font_atlas[sym][oy];
|
unsigned char sym_line = font_atlas[sym][oy];
|
||||||
|
@ -817,7 +835,28 @@ struct palette *palette_k_means(const struct image *image, const struct palette
|
||||||
return palette;
|
return palette;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static float weighted_glyph_hamming_distance(const GlyphBitmap *lhs, const GlyphBitmap *rhs, const typeof(float[11][8]) *weights) {
|
int popcnt32(uint32_t mask) {
|
||||||
|
#if HAS_POPCNT
|
||||||
|
return __builtin_popcount(mask);
|
||||||
|
#else
|
||||||
|
int res = 0;
|
||||||
|
for (; mask; mask >>= 1) {
|
||||||
|
res += mask & 1;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int glyph_hamming_distance(const GlyphBitmap *lhs, const GlyphBitmap *rhs) {
|
||||||
|
int dist = 0;
|
||||||
|
dist += popcnt32(*((uint32_t*)&((*lhs)[0])) ^ *((uint32_t*)&((*rhs)[0])));
|
||||||
|
dist += popcnt32(*((uint32_t*)&((*lhs)[4])) ^ *((uint32_t*)&((*rhs)[4])));
|
||||||
|
dist += popcnt32(*((uint16_t*)&((*lhs)[8])) ^ *((uint16_t*)&((*rhs)[8])));
|
||||||
|
dist += popcnt32( ( ((*lhs)[10])) ^ ( ((*rhs)[10])));
|
||||||
|
return dist;
|
||||||
|
}
|
||||||
|
|
||||||
|
float weighted_glyph_hamming_distance(const GlyphBitmap *lhs, const GlyphBitmap *rhs, const typeof(float[11][8]) *weights) {
|
||||||
float dist = 0;
|
float dist = 0;
|
||||||
for (int oy = 0; oy < 11; oy++) {
|
for (int oy = 0; oy < 11; oy++) {
|
||||||
uint8_t sym_line = (*lhs)[oy] ^ (*rhs)[oy];
|
uint8_t sym_line = (*lhs)[oy] ^ (*rhs)[oy];
|
||||||
|
@ -831,7 +870,23 @@ inline static float weighted_glyph_hamming_distance(const GlyphBitmap *lhs, cons
|
||||||
return dist;
|
return dist;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t closest_glyph_symbol(const GlyphBitmap *target, const typeof(float[11][8]) *weights) {
|
uint8_t closest_glyph_symbol_fast(const GlyphBitmap *target) {
|
||||||
|
uint8_t best = 0x01;
|
||||||
|
int best_dist = glyph_hamming_distance(target, &font_atlas[best]);
|
||||||
|
for (int sym = 0x02; sym <= 0xFF; sym++) {
|
||||||
|
if (sym == '\t' || sym == '\n' || sym == '\r' || sym == '\x0e') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int dist = glyph_hamming_distance(target, &font_atlas[sym]);
|
||||||
|
if (dist <= best_dist) {
|
||||||
|
best_dist = dist;
|
||||||
|
best = sym;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t closest_glyph_symbol_precise(const GlyphBitmap *target, const typeof(float[11][8]) *weights) {
|
||||||
uint8_t best = 0x01;
|
uint8_t best = 0x01;
|
||||||
float best_dist = weighted_glyph_hamming_distance(target, &font_atlas[best], weights);
|
float best_dist = weighted_glyph_hamming_distance(target, &font_atlas[best], weights);
|
||||||
for (int sym = 0x02; sym <= 0xFF; sym++) {
|
for (int sym = 0x02; sym <= 0xFF; sym++) {
|
||||||
|
@ -865,11 +920,15 @@ void construct_chunk_color_glyph(GlyphBitmap *result, typeof(float[11][8]) *weig
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t closest_chunk_color_symbol(const typeof(float[8][11][0x10]) *chunk_palette_diffs, uint8_t color_pair) {
|
uint8_t closest_chunk_color_symbol(const typeof(float[8][11][0x10]) *chunk_palette_diffs, uint8_t color_pair, bool precise) {
|
||||||
GlyphBitmap glyph;
|
GlyphBitmap glyph;
|
||||||
float weights[11][8];
|
float weights[11][8];
|
||||||
construct_chunk_color_glyph(&glyph, &weights, chunk_palette_diffs, color_pair);
|
construct_chunk_color_glyph(&glyph, precise ? &weights : NULL, chunk_palette_diffs, color_pair);
|
||||||
return closest_glyph_symbol(&glyph, &weights);
|
if (precise) {
|
||||||
|
return closest_glyph_symbol_precise(&glyph, &weights);
|
||||||
|
} else {
|
||||||
|
return closest_glyph_symbol_fast(&glyph);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct palette DEFAULT_PALETTE = PALETTE(
|
const struct palette DEFAULT_PALETTE = PALETTE(
|
||||||
|
|
Loading…
Reference in New Issue