OCLint fixes and some housekeeping stuff

// TODO: convert __bra_* and __blk_* to use palette from state
This commit is contained in:
Casey 2022-02-04 15:48:21 +03:00
parent ea6f383b02
commit 2e359443d7
15 changed files with 308 additions and 292 deletions

7
.oclint Normal file
View File

@ -0,0 +1,7 @@
rule-configurations:
- key: SHORT_VARIABLE_NAME
value: 1
- key: LONG_VARIABLE_NAME
value: 25
- key: LONG_LINE
value: 80

View File

@ -1,4 +1,4 @@
CFLAGS := -Wall -Wextra -Werror -pedantic -std=c99
CFLAGS := -Wall -Wextra -Werror -pedantic -std=c99 -Wno-attributes
CLIBS := -lm
INCLUDES := -Isrc
OBJECTS := obj/stb_image.o obj/stb_image_resize.o \

View File

@ -1,4 +1,4 @@
# YAITAA - Yet Another Image To A(NSI) Art (converter).
# YAITAA - Yet Another Image To A(NSI) Art (converter)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/9fdbd9f8bc7843df9a6715b72b4da2fd)](https://www.codacy.com/gh/hatkidchan/yaitaa/dashboard?utm_source=github.com&utm_medium=referral&utm_content=hatkidchan/yaitaa&utm_campaign=Badge_Grade)
@ -17,7 +17,6 @@ recap:
5. Separate binaries for each mode
6. Git submodules. Yay.
## Build instructions
### 1. Make sure that you have C compiler and make

View File

@ -1,73 +1,85 @@
#include "version.h"
#include "args.h"
#include "colors.h"
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "version.h"
#include "args.h"
#include "colors.h"
#include "commons.h"
#include "mod_blocks.h"
#include "mod_braille.h"
typedef struct {
int value;
char *strings[8];
char *description;
void *unused[2];
} __option_t;
int __find_value(const __option_t *options, char *option);
void __print_options(const __option_t *options);
const __option_t __mode_options[ASC_MOD_ENDL + 1] = {
const asc_handler_t asc_handlers[ASC_MOD_ENDL + 1] = {
{ ASC_MOD_BLOCKS,
{ "b", "blk", "blocks", NULL },
"Box-drawing characters (\342\226\204) (default)" },
"Box-drawing characters (\342\226\204) (default)",
mod_blocks_prepare, mod_blocks_main },
{ ASC_MOD_BRAILLE,
{ "r", "brl", "braille", NULL },
"Braille characters (literally stolen from MineOS)" },
"Braille characters (literally stolen from MineOS)",
mod_braille_prepare, mod_braille_main },
{ ASC_MOD_GRADIENT,
{ "g", "grd", "gradient", NULL },
"Gradient of characters. No matching at all" },
"Gradient of characters. No matching at all",
NULL, NULL },
{ ASC_MOD_BRUTEFORCE,
{ "f", "guess", "bruteforce", NULL },
"Looking for best possible character" },
{ -1, { NULL }, NULL }
"Looking for best possible character",
NULL, NULL },
{ -1, { NULL }, NULL, NULL, NULL }
};
const __option_t __style_options[ASC_STL_ENDL + 1] = {
{ ASC_STL_BLACKWHITE,
{ "1", "bw", "black-white", "1bit", NULL },
"1-bit black/white" },
"1-bit black/white", { NULL, NULL } },
{ ASC_STL_ANSI_VGA,
{ "vga", "ansi-vga", NULL },
"VGA palette" },
{ "vga", "ansi-vga" },
"VGA palette", { NULL, NULL } },
{ ASC_STL_ANSI_XTERM,
{ "xterm", "ansi-xterm", NULL },
"xTerm palette. A bit more rough, compared to VGA" },
"xTerm palette. A bit more rough, compared to VGA", { NULL, NULL } },
{ ASC_STL_ANSI_DISCORD,
{ "discord", "ansi-discord", NULL },
"Palette in Discord ANSI highlight" },
"Palette in Discord ANSI highlight", { NULL, NULL } },
{ ASC_STL_256COLOR,
{ "256", "pal256", "8bit", NULL },
"256-color palette (default)" },
"256-color palette (default)", { NULL, NULL } },
{ ASC_STL_TRUECOLOR,
{ "true", "truecolor", "24bit", NULL },
"24-bit RGB (TrueColor)" },
"24-bit RGB (TrueColor)", { NULL, NULL } },
{ ASC_STL_PALETTE,
{ "pal", "palette", "custom", NULL },
"Custom palette (specified via -P). Either GIMP palette file or N*3 RGB pixels" },
{ -1, { NULL }, NULL }
"Custom palette (specified via -P). "\
"Either GIMP palette file or N*3 RGB pixels", { NULL, NULL } },
{ -1, { NULL }, NULL, { NULL, NULL } }
};
const __option_t __format_options[ASC_FMT_ENDL + 1] = {
{ ASC_FMT_ANSI,
{ "ansi", "raw", NULL },
"Output, suitable for terminal (default)" },
"Output, suitable for terminal (default)",
{ NULL, NULL } },
{ ASC_FMT_HTML,
{ "html", NULL },
"Output as HTML table" },
"Output as HTML table",
{ NULL, NULL } },
{ ASC_FMT_JSON,
{ "json", NULL },
"Output as JSON 2D array of characters with properties" },
{ -1, { NULL }, NULL }
"Output as JSON 2D array of characters with properties",
{ NULL, NULL } },
{ -1, { NULL }, NULL, { NULL, NULL } }
};
void usage(int argc, char **argv)
@ -93,7 +105,7 @@ void usage(int argc, char **argv)
fprintf(stderr, "-P PALETTE\tPath to palette file (when -S pal)\n");
fprintf(stderr, "\n\n");
fprintf(stderr, "Options for MODE:\n");
__print_options(__mode_options);
__print_options((const __option_t*)asc_handlers);
fprintf(stderr, "Options for STYLE:\n");
__print_options(__style_options);
fprintf(stderr, "Options for FORMAT:\n");
@ -112,6 +124,10 @@ void version(int argc, char **argv)
fprintf(stderr, "See LICENSE file for details\n");
}
__attribute__((annotate("oclint:suppress[high cyclomatic complexity]")))
__attribute__((annotate("oclint:suppress[high npath complexity]")))
__attribute__((annotate("oclint:suppress[high ncss method]")))
__attribute__((annotate("oclint:suppress[long method]")))
int parse_args(int argc, char **argv, asc_args_t *args)
{
args->input_filename = NULL;
@ -136,11 +152,9 @@ int parse_args(int argc, char **argv, asc_args_t *args)
case 'h':
usage(argc, argv);
return 1;
break;
case 'V':
version(argc, argv);
return 1;
break;
case 'd':
args->dither = true;
break;
@ -160,7 +174,7 @@ int parse_args(int argc, char **argv, asc_args_t *args)
break;
case 'M':
{
int val = __find_value(__mode_options, optarg);
int val = __find_value((const __option_t*)asc_handlers, optarg);
if (val < 0)
{
fprintf(stderr, "Error: invalid mode '%s'\n", optarg);
@ -209,11 +223,14 @@ int parse_args(int argc, char **argv, asc_args_t *args)
fprintf(stderr, "Error: Unknown parameter -%c\n", optopt);
}
return -2;
default:
fprintf(stderr, "Error: UNREACHABLE: getopt switch gone wrong\n");
break;
}
}
if (args->out_style == ASC_STL_PALETTE && args->palette_filename == NULL)
{
fprintf(stderr, "Error: no palette file provided, but palette mode selected\n");
fprintf(stderr, "Error: no palette file provided in palette mode\n");
return -3;
}
if (argc <= optind || argc < 2)
@ -225,11 +242,16 @@ int parse_args(int argc, char **argv, asc_args_t *args)
return 0;
}
__attribute__((annotate("oclint:suppress[high cyclomatic complexity]")))
__attribute__((annotate("oclint:suppress[high npath complexity]")))
__attribute__((annotate("oclint:suppress[high ncss method]")))
__attribute__((annotate("oclint:suppress[long method]")))
int prepare_state(int argc, char **argv, asc_args_t args, asc_state_t *state)
{
(void)argc; (void)argv;
state->args = args;
// Loading image
FILE *image_file;
if ((image_file = fopen(args.input_filename, "rb")) == NULL
|| ferror(image_file) != 0)
@ -239,33 +261,48 @@ int prepare_state(int argc, char **argv, asc_args_t args, asc_state_t *state)
args.input_filename, err, strerror(err));
return -100 - err;
}
state->source_image = image_load(image_file);
fclose(image_file);
if (args.out_style == ASC_STL_PALETTE)
// Palette configuration
switch (args.out_style)
{
FILE *fp = fopen(args.palette_filename, "rb");
if (fp == NULL)
{
int err = errno;
fprintf(stderr, "Error: failed to open file %s for reading: %d: %s\n",
args.palette_filename, err, strerror(err));
return -100 - err;
}
state->palette = calloc(1, sizeof(palette_t));
if (!load_palette(state->palette, fp))
{
fprintf(stderr, "Error: failed to read palette\n");
fclose(fp);
return -7;
}
fclose(fp);
case ASC_STL_PALETTE:
{
FILE *fp = fopen(args.palette_filename, "rb");
if (fp == NULL)
{
int err = errno;
fprintf(stderr, "Error: failed to open file %s for reading: %d: %s\n",
args.palette_filename, err, strerror(err));
return -100 - err;
}
state->palette = calloc(1, sizeof(palette_t));
if (!load_palette(state->palette, fp))
{
fprintf(stderr, "Error: failed to read palette\n");
fclose(fp);
return -7;
}
fclose(fp);
}
break;
case ASC_STL_256COLOR:
make_pal256(&c_palette_256, c_palette_ansi_vga);
state->palette = &c_palette_256;
break;
case ASC_STL_ANSI_VGA:
case ASC_STL_ANSI_XTERM:
case ASC_STL_ANSI_DISCORD:
case ASC_STL_BLACKWHITE:
state->palette = get_palette_by_id(args.out_style);
break;
case ASC_STL_TRUECOLOR:
case ASC_STL_ENDL:
break;
}
if (args.out_style == ASC_STL_256COLOR)
make_pal256(&c_palette_256, c_palette_ansi_vga);
// Output file configuration
state->out_file = stdout;
if (strcmp(args.output_filename, "-"))
state->out_file = fopen(args.output_filename, "wb");
@ -276,6 +313,7 @@ int prepare_state(int argc, char **argv, asc_args_t args, asc_state_t *state)
args.output_filename, err, strerror(err));
return -100 - err;
}
return 0;
}

View File

@ -69,6 +69,19 @@ typedef struct {
FILE *out_file;
} asc_state_t;
typedef void (*asc_module_initializer_fn)(asc_state_t *state);
typedef void (*asc_module_handler_fn)(asc_state_t state);
typedef struct {
int id;
char *strings[8];
char *description;
asc_module_initializer_fn prepare;
asc_module_handler_fn main;
} asc_handler_t;
extern const asc_handler_t asc_handlers[ASC_MOD_ENDL + 1];
void usage(int argc, char **argv);
int parse_args(int argc, char **argv, asc_args_t *args);
int prepare_state(int argc, char **argv, asc_args_t args, asc_state_t *state);

View File

@ -119,6 +119,11 @@ rgba8 pal256_to_rgb(palette_t pal, int ndx)
return out;
}
rgba8 clamp_to_pal(palette_t pal, rgba8 color)
{
return pal.palette[closest_color(pal, color)];
}
void make_pal256(palette_t *dst, palette_t ansi)
{
if (dst->n_colors == 256) return;
@ -175,12 +180,7 @@ bool load_palette(palette_t *pal, FILE *fp)
if (fread(head, sizeof(char), 12, fp) < 12) return false;
if (fseek(fp, 0, SEEK_SET) != 0) return false;
if (!strncmp(head, "GIMP Palette", 12))
{
return load_palette_gpl(pal, fp);
}
else
{
return load_palette_raw(pal, fp);
}
return load_palette_raw(pal, fp);
}

View File

@ -22,6 +22,7 @@
#include <stdbool.h>
#define PURE_BLACK ((rgba8){ 0, 0, 0, 0 })
#define RGBN(c) (c.r << 16 | c.g << 8 | c.b)
typedef struct {
uint8_t r, g, b, a;
@ -41,6 +42,7 @@ extern palette_t c_palette_ansi_xterm;
int color_difference(rgba8 a, rgba8 b);
int closest_color(palette_t pal, rgba8 color);
rgba8 pal256_to_rgb(palette_t pal, int ndx);
rgba8 clamp_to_pal(palette_t pal, rgba8 color);
void make_pal256(palette_t *dst, palette_t ansi);
bool load_palette_gpl(palette_t *pal, FILE *fp);
bool load_palette_raw(palette_t *pal, FILE *fp);

View File

@ -3,41 +3,27 @@
#include <stdio.h>
#include <stdlib.h>
void m_prepare_dither(asc_state_t *state)
void m_prepare_dither(asc_state_t *sta)
{
if (state->args.dither)
image_t *res = NULL;
switch (sta->args.out_style)
{
image_t *res = NULL;
switch (state->args.out_style)
{
case ASC_STL_BLACKWHITE:
res = image_dither(state->image, c_palette_bw);
break;
case ASC_STL_ANSI_VGA:
res = image_dither(state->image, c_palette_ansi_vga);
break;
case ASC_STL_ANSI_XTERM:
res = image_dither(state->image, c_palette_ansi_xterm);
break;
case ASC_STL_ANSI_DISCORD:
res = image_dither(state->image, c_palette_ansi_discord);
break;
case ASC_STL_256COLOR:
res = image_dither(state->image, c_palette_256);
break;
case ASC_STL_PALETTE:
res = image_dither(state->image, *state->palette);
break;
case ASC_STL_TRUECOLOR:
case ASC_STL_ENDL:
break;
}
if (res != NULL)
{
image_unload(state->image);
state->image = res;
}
case ASC_STL_BLACKWHITE:
case ASC_STL_ANSI_VGA:
case ASC_STL_ANSI_XTERM:
case ASC_STL_ANSI_DISCORD:
case ASC_STL_256COLOR:
res = image_dither(sta->image, *get_palette_by_id(sta->args.out_style));
break;
case ASC_STL_PALETTE:
res = image_dither(sta->image, *sta->palette);
break;
case ASC_STL_TRUECOLOR:
case ASC_STL_ENDL:
return;
}
image_unload(sta->image);
sta->image = res;
}
void c_fatal(int code, const char *reason)
@ -45,3 +31,18 @@ void c_fatal(int code, const char *reason)
fprintf(stderr, "Error: %s\n", reason);
exit(code);
}
palette_t *get_palette_by_id(asc_style_t stl)
{
palette_t *pal = &c_palette_bw;
switch (stl)
{
case ASC_STL_BLACKWHITE: pal = &c_palette_bw; break;
case ASC_STL_ANSI_VGA: pal = &c_palette_ansi_vga; break;
case ASC_STL_ANSI_XTERM: pal = &c_palette_ansi_xterm; break;
case ASC_STL_ANSI_DISCORD: pal = &c_palette_ansi_discord; break;
case ASC_STL_256COLOR: pal = &c_palette_256; break;
default: c_fatal(9, "[UNREACH] Palette is unset"); break;
}
return pal;
}

View File

@ -20,7 +20,10 @@
#include "args.h"
#define CLAMP(min, val, max) ((val)>(max)?(max):((val)<(min)?(min):(val)))
void c_fatal(int code, const char *reason);
void m_prepare_dither(asc_state_t *state);
palette_t *get_palette_by_id(asc_style_t stl);
#endif

26
src/fmt_strings.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef _FMT_STRINGS_H
#define _FMT_STRINGS_H
#define S_JSON_HEAD "{\n \"width\": %d,\n \"height\": %d,\n \"data\": [\n"
#define S_JSON_LSTA " [\n"
#define S_JSON_PRGB "{ \"char\": \"\\u%04x\", \"fg\": %d, \"bg\": %d }"
#define S_JSON_PBLK "{ \"char\": \"%s\", \"fg\": %d, \"bg\": %d }"
#define S_JSON_LEND " ],\n"
#define S_JSON_LEND_FINAL " ]\n"
#define S_JSON_TAIL "}"
#define S_HTML_HEAD "<table style=\"border-collapse: collapse;\">\n"
#define S_HTML_LSTA "<tr>"
#define S_HTML_PCBR "<td style=\"color: rgb(%d, %d, %d); "\
"background: rgb(%d, %d, %d);\">&#%d;</td>"
#define S_HTML_PBLK "<td style=\"color: rgb(%d, %d, %d); "\
"background: rgb(%d, %d, %d);\">%s</td>"
#define S_HTML_LEND "</tr>\n"
#define S_HTML_TAIL "</table>"
#define S_ANSI "\033[%d;%dm"
#define S_ANSI_S "\033[%d;%dm%s"
#define S_ANSI_RGB "\033[38;2;%d;%d;%d;48;2;%d;%d;%dm"
#define S_ANSI_256 "\033[38;5;%d;48;5;%dm"
#endif

View File

@ -1,3 +1,4 @@
#include "commons.h"
#include "image.h"
#include "stb_image.h"
#include "stb_image_resize.h"
@ -29,22 +30,19 @@ image_t *image_resize(image_t *img, int width, int height)
return res;
}
__attribute__((annotate("oclint:suppress[high cyclomatic complexity]")))
void __dither_update_pixel(image_t *img, int x, int y, int err[3], float bias)
{
if (x < 0 || x >= img->width || y < 0 || y >= img->height) return;
int i = x + y * img->width;
rgba8 pix = img->pixels[i];
int dst[3] = { pix.r, pix.g, pix.b };
dst[0] += (int)(((float)err[0]) * bias);
dst[1] += (int)(((float)err[1]) * bias);
dst[2] += (int)(((float)err[2]) * bias);
img->pixels[i].r = (dst[0] > 255 ? 255 : (dst[0] < 0 ? 0 : dst[0]));
img->pixels[i].g = (dst[1] > 255 ? 255 : (dst[1] < 0 ? 0 : dst[1]));
img->pixels[i].b = (dst[2] > 255 ? 255 : (dst[2] < 0 ? 0 : dst[2]));
img->pixels[i].r = CLAMP(0, dst[0] + (int)((float)err[0] * bias), 255);
img->pixels[i].g = CLAMP(0, dst[1] + (int)((float)err[1] * bias), 255);
img->pixels[i].b = CLAMP(0, dst[2] + (int)((float)err[2] * bias), 255);
}
// TODO: make it work better sometime in future (for some reason it sucks rn)
image_t *image_dither_fn(image_t *img, dither_quantizer_t quantize, void *param)
{
image_t *res = calloc(1, sizeof(image_t));

View File

@ -23,7 +23,7 @@
typedef struct {
int width;
int height;
int errno;
int err;
rgba8 *pixels;
} image_t;

View File

@ -22,6 +22,7 @@
#include "args.h"
#include "mod_blocks.h"
#include "mod_braille.h"
#include "commons.h"
int main(int argc, char **argv)
{
@ -35,25 +36,11 @@ int main(int argc, char **argv)
if (res == 1) return 0;
if (res < 0) return -res;
switch (args.mode)
{
case ASC_MOD_BLOCKS:
mod_blocks_prepare(&state);
mod_blocks_main(state);
break;
case ASC_MOD_BRAILLE:
mod_braille_prepare(&state);
mod_braille_main(state);
break;
case ASC_MOD_GRADIENT:
fprintf(stderr, "Error: ASC_MOD_GRADIENT is not implemented yet\n");
break;
case ASC_MOD_BRUTEFORCE:
fprintf(stderr, "Error: ASC_MOD_BRUTEFORCE is not implemented yet\n");
break;
case ASC_MOD_ENDL:
break;
}
asc_handler_t handler = asc_handlers[args.mode];
if (handler.prepare == NULL)
c_fatal(12, "this mode is not implemented yet");
handler.prepare(&state);
handler.main(state);
return 0;
}

View File

@ -3,6 +3,7 @@
#include "image.h"
#include "colors.h"
#include "commons.h"
#include "fmt_strings.h"
const char *BLOCKS[4] = { " ", "\xe2\x96\x80", "\xe2\x96\x84", "\xe2\x96\x88" };
const char *BLOCKS_ESC[4] = { " ", "\\u2580", "\\u2584", "\\u2588" };
@ -16,7 +17,8 @@ void mod_blocks_prepare(asc_state_t *state)
state->args.width, state->args.height * 2, &w, &h);
h = (h / 2) * 2;
state->image = image_resize(state->source_image, w, h);
m_prepare_dither(state);
if (state->args.dither)
m_prepare_dither(state);
}
void __blk_start_output(asc_state_t state)
@ -55,16 +57,10 @@ void __blk_end_output(asc_state_t state)
void __blk_start_line(FILE *fp, asc_format_t fmt, bool final)
{
(void)final;
switch (fmt)
{
case ASC_FMT_JSON:
fprintf(fp, " [\n");
break;
case ASC_FMT_HTML:
fprintf(fp, "<tr>");
default:
break;
}
if (fmt == ASC_FMT_JSON)
fprintf(fp, S_JSON_LSTA);
else if (fmt == ASC_FMT_HTML)
fprintf(fp, S_HTML_LSTA);
}
void __blk_end_line(FILE *fp, asc_format_t fmt, asc_style_t stl, bool final)
@ -108,20 +104,15 @@ void __blk_putc_ansi(FILE *fp, asc_format_t fmt, bool final, int ct, int cb, pal
switch (fmt)
{
case ASC_FMT_JSON:
fprintf(fp, "{ \"char\": \"%s\", \"fg\": %d, \"bg\": %d }",
BLOCKS_ESC[1], top_int, bot_int);
fprintf(fp, S_JSON_PBLK, BLOCKS_ESC[1], top_int, bot_int);
if (!final) fprintf(fp, ", ");
break;
case ASC_FMT_HTML:
fprintf(fp, "<td style=\"color: rgb(%d, %d, %d); background: rgb(%d, %d, %d);\">%s</td>",
top_rgb.r, top_rgb.g, top_rgb.b,
bot_rgb.r, bot_rgb.g, bot_rgb.b,
BLOCKS[1]);
fprintf(fp, S_HTML_PBLK, top_rgb.r, top_rgb.g, top_rgb.b,
bot_rgb.r, bot_rgb.g, bot_rgb.b, BLOCKS[1]);
break;
default:
fprintf(fp, "\033[%d;%dm%s",
ct + (ct >= 8 ? 82 : 30),
cb + (cb >= 8 ? 92 : 40),
fprintf(fp, S_ANSI_S, ct + (ct >= 8 ? 82 : 30), cb + (cb >= 8 ? 92 : 40),
BLOCKS[1]);
break;
}
@ -193,17 +184,10 @@ void __blk_put_pixel(asc_state_t state, rgba8 top, rgba8 bot, bool final)
case ASC_STL_ANSI_XTERM:
case ASC_STL_ANSI_DISCORD:
{
palette_t pal;
switch (state.args.out_style)
{
case ASC_STL_ANSI_VGA: pal = c_palette_ansi_vga; break;
case ASC_STL_ANSI_XTERM: pal = c_palette_ansi_xterm; break;
case ASC_STL_ANSI_DISCORD: pal = c_palette_ansi_discord; break;
default: c_fatal(9, "[UNREACH] Palette is unset"); return;
}
int index_top = closest_color(pal, top),
index_bot = closest_color(pal, bot);
__blk_putc_ansi(fp, fmt, final, index_top, index_bot, pal);
palette_t *pal = get_palette_by_id(state.args.out_style);
int index_top = closest_color(*pal, top),
index_bot = closest_color(*pal, bot);
__blk_putc_ansi(fp, fmt, final, index_top, index_bot, *pal);
}
break;
case ASC_STL_256COLOR:
@ -219,10 +203,10 @@ void __blk_put_pixel(asc_state_t state, rgba8 top, rgba8 bot, bool final)
case ASC_STL_PALETTE:
{
palette_t *pal = state.palette;
rgba8 pal_top = pal->palette[closest_color(*pal, top)];
rgba8 pal_bot = pal->palette[closest_color(*pal, bot)];
__blk_putc_truecolor(fp, fmt, final, pal_top, pal_bot);
__blk_putc_truecolor(fp, fmt, final,
clamp_to_pal(*pal, top), clamp_to_pal(*pal, bot));
}
break;
case ASC_STL_ENDL:
break;
}

View File

@ -3,18 +3,31 @@
#include "image.h"
#include "colors.h"
#include "commons.h"
#include "fmt_strings.h"
int __bra_best_match_i(rgba8 a, rgba8 b, rgba8 t);
void __bra_putc_raw(asc_state_t state, uint8_t ch);
void __bra_putc_esc(asc_state_t state, uint8_t ch);
void __bra_start_output(asc_state_t state);
void __bra_start_line(asc_state_t state, bool final);
void __bra_put_pixel(asc_state_t sta, rgba8 min, rgba8 max, uint8_t ch, bool final);
void __bra_putc_ansi(asc_state_t state, int i_min, int i_max, uint8_t ch, palette_t pal, bool final);
void __bra_putc_256(asc_state_t state, int i_min, int i_max, uint8_t ch, bool final);
void __bra_putc_true(asc_state_t state, rgba8 min, rgba8 max, uint8_t ch, bool final);
void __bra_end_line(asc_state_t state, bool final);
void __bra_end_output(asc_state_t state);
void __bra_putc_raw(asc_state_t s, uint8_t ch);
void __bra_putc_esc(asc_state_t s, uint8_t ch);
void __bra_start_output(asc_state_t s);
void __bra_start_line(asc_state_t s, bool final);
void __bra_put_pixel(asc_state_t s, rgba8 bg, rgba8 fg, uint8_t ch, bool final);
void __bra_putc_ansi(asc_state_t s, int bg, int fg, uint8_t ch, palette_t pal, bool final);
void __bra_putc_256(asc_state_t s, int bg, int fg, uint8_t ch, bool final);
void __bra_putc_true(asc_state_t s, rgba8 bg, rgba8 fg, uint8_t ch, bool final);
void __bra_end_line(asc_state_t s, bool final);
void __bra_end_output(asc_state_t s);
void __bra_update2x4(image_t *img, rgba8 block[8], int x, int y)
{
block[0] = img->pixels[(x + 0) + (y + 0) * img->width];
block[3] = img->pixels[(x + 1) + (y + 0) * img->width];
block[1] = img->pixels[(x + 0) + (y + 1) * img->width];
block[4] = img->pixels[(x + 1) + (y + 1) * img->width];
block[2] = img->pixels[(x + 0) + (y + 2) * img->width];
block[5] = img->pixels[(x + 1) + (y + 2) * img->width];
block[6] = img->pixels[(x + 0) + (y + 3) * img->width];
block[7] = img->pixels[(x + 1) + (y + 3) * img->width];
}
void mod_braille_prepare(asc_state_t *state)
@ -25,7 +38,8 @@ void mod_braille_prepare(asc_state_t *state)
state->args.width * 2, state->args.height * 4, &w, &h);
w = (w / 2) * 2; h = (h / 4) * 4;
state->image = image_resize(state->source_image, w, h);
m_prepare_dither(state);
if (state->args.dither)
m_prepare_dither(state);
}
void mod_braille_main(asc_state_t state)
@ -33,7 +47,7 @@ void mod_braille_main(asc_state_t state)
image_t *img = state.image;
uint8_t braille_char = 0x00;
rgba8 pix2x4[8];
rgba8 block[8];
rgba8 color_max, color_min;
int dist_max, dist_min, dist_min_d = 0xffffff, dist;
@ -45,37 +59,30 @@ void mod_braille_main(asc_state_t state)
__bra_start_line(state, final);
for (int x = 0; x < img->width; x += 2)
{
pix2x4[0] = img->pixels[(x + 0) + (y + 0) * img->width];
pix2x4[3] = img->pixels[(x + 1) + (y + 0) * img->width];
pix2x4[1] = img->pixels[(x + 0) + (y + 1) * img->width];
pix2x4[4] = img->pixels[(x + 1) + (y + 1) * img->width];
pix2x4[2] = img->pixels[(x + 0) + (y + 2) * img->width];
pix2x4[5] = img->pixels[(x + 1) + (y + 2) * img->width];
pix2x4[6] = img->pixels[(x + 0) + (y + 3) * img->width];
pix2x4[7] = img->pixels[(x + 1) + (y + 3) * img->width];
color_max = color_min = pix2x4[0];
__bra_update2x4(img, block, x, y);
color_max = color_min = block[0];
dist_max = 0;
dist_min = dist_min_d;
for (int i = 0; i < 8; i++)
{
dist = color_difference(pix2x4[i], PURE_BLACK);
dist = color_difference(block[i], PURE_BLACK);
if (dist < dist_min)
{
dist_min = dist;
color_min = pix2x4[i];
color_min = block[i];
}
if (dist > dist_max)
{
dist_max = dist;
color_max = pix2x4[i];
color_max = block[i];
}
}
braille_char = 0x00;
for (int i = 0; i < 8; i++)
{
if (__bra_best_match_i(color_min, color_max, pix2x4[i]) != 0)
if (__bra_best_match_i(color_min, color_max, block[i]) != 0)
{
braille_char |= (1 << i);
}
@ -107,72 +114,45 @@ void __bra_putc_esc(asc_state_t state, uint8_t ch)
void __bra_start_output(asc_state_t state)
{
switch (state.args.out_format)
{
case ASC_FMT_JSON:
fprintf(state.out_file, "{\n");
fprintf(state.out_file, " \"width\": %d,\n", state.image->width / 2);
fprintf(state.out_file, " \"height\": %d,\n", state.image->height / 4);
fprintf(state.out_file, " \"data\": [");
break;
case ASC_FMT_HTML:
fprintf(state.out_file, "<table style=\"border-collapse: collapse;\">\n");
break;
default:
break;
}
int w = state.image->width / 2, h = state.image->height / 4;
if (state.args.out_format == ASC_FMT_JSON)
fprintf(state.out_file, S_JSON_HEAD, w, h);
else if (state.args.out_format == ASC_FMT_HTML)
fprintf(state.out_file, S_HTML_HEAD);
}
void __bra_start_line(asc_state_t state, bool final)
{
(void)final;
switch (state.args.out_format)
{
case ASC_FMT_JSON:
fprintf(state.out_file, " [\n");
break;
case ASC_FMT_HTML:
fprintf(state.out_file, "<tr>");
default:
break;
}
if (state.args.out_format == ASC_FMT_JSON)
fprintf(state.out_file, S_JSON_LSTA);
else if (state.args.out_format == ASC_FMT_HTML)
fprintf(state.out_file, S_HTML_LSTA);
}
void __bra_put_pixel(asc_state_t state, rgba8 min, rgba8 max, uint8_t ch, bool final)
void __bra_put_pixel(asc_state_t s, rgba8 min, rgba8 max, uint8_t ch, bool fin)
{
switch (state.args.out_style)
switch (s.args.out_style)
{
case ASC_STL_ANSI_VGA:
case ASC_STL_ANSI_XTERM:
case ASC_STL_ANSI_DISCORD:
{
palette_t pal;
switch (state.args.out_style)
{
case ASC_STL_ANSI_VGA: pal = c_palette_ansi_vga; break;
case ASC_STL_ANSI_XTERM: pal = c_palette_ansi_xterm; break;
case ASC_STL_ANSI_DISCORD: pal = c_palette_ansi_discord; break;
default: c_fatal(9, "[UNREACH] Palette is unset"); return;
}
__bra_putc_ansi(state,
closest_color(pal, min), closest_color(pal, max), ch, pal, final);
palette_t pal = *get_palette_by_id(s.args.out_style);
__bra_putc_ansi(s,
closest_color(pal, min), closest_color(pal, max), ch, pal, fin);
}
break;
case ASC_STL_256COLOR:
{
__bra_putc_256(state, closest_color(c_palette_256, min),
closest_color(c_palette_256, max), ch, final);
}
__bra_putc_256(s, closest_color(c_palette_256, min),
closest_color(c_palette_256, max), ch, fin);
break;
case ASC_STL_TRUECOLOR:
__bra_putc_true(state, min, max, ch, final);
__bra_putc_true(s, min, max, ch, fin);
break;
case ASC_STL_PALETTE:
{
palette_t *pal = state.palette;
__bra_putc_true(state, pal->palette[closest_color(*pal, min)],
pal->palette[closest_color(*pal, max)], ch, final);
}
__bra_putc_true(s, clamp_to_pal(*s.palette, min),
clamp_to_pal(*s.palette, max), ch, fin);
break;
case ASC_STL_BLACKWHITE:
case ASC_STL_ENDL:
@ -180,115 +160,93 @@ void __bra_put_pixel(asc_state_t state, rgba8 min, rgba8 max, uint8_t ch, bool f
}
}
void __bra_putc_ansi(asc_state_t state, int i_min, int i_max, uint8_t ch, palette_t pal, bool final)
void __bra_putc_ansi
(asc_state_t s, int bgi, int fgi, uint8_t ch, palette_t pal, bool fin)
{
rgba8 min_rgb = pal.palette[i_min], max_rgb = pal.palette[i_max];
int min_int = min_rgb.r << 16 | min_rgb.g << 8 | min_rgb.b;
int max_int = max_rgb.r << 16 | max_rgb.g << 8 | max_rgb.b;
FILE *fp = state.out_file;
switch (state.args.out_format)
rgba8 bg = pal.palette[bgi], fg = pal.palette[fgi];
FILE *fp = s.out_file;
switch (s.args.out_format)
{
case ASC_FMT_JSON:
fprintf(fp, "{ \"char\": \"\\u28%d\", \"fg\": %d, \"bg\": %d }",
ch, max_int, min_int);
if (!final) fprintf(fp, ", ");
fprintf(fp, S_JSON_PRGB, ch, RGBN(fg), RGBN(bg));
if (!fin) fprintf(fp, ", ");
break;
case ASC_FMT_HTML:
fprintf(fp, "<td style=\"color: rgb(%d, %d, %d); background: rgb(%d, %d, %d);\">&#%d;</td>",
max_rgb.r, max_rgb.g, max_rgb.b, min_rgb.r, min_rgb.g, min_rgb.b,
0x2800 | ch);
fprintf(fp, S_HTML_PCBR, fg.r, fg.g, fg.b,
bg.r, bg.g, bg.b, 0x2800 | ch);
break;
case ASC_FMT_ANSI:
fprintf(fp, "\033[%d;%dm", i_max + (i_max > 8 ? 82 : 30), i_min + (i_min > 8 ? 82 : 30));
__bra_putc_raw(state, ch);
fprintf(fp, S_ANSI, fgi + (fgi > 8 ? 82 : 30), bgi + (bgi > 8 ? 82 : 30));
__bra_putc_raw(s, ch);
break;
case ASC_FMT_ENDL:
break;
}
}
void __bra_putc_256(asc_state_t state, int i_min, int i_max, uint8_t ch, bool final)
void __bra_putc_256(asc_state_t s, int bgi, int fgi, uint8_t ch, bool final)
{
rgba8 min_rgb = c_palette_256.palette[i_min];
rgba8 max_rgb = c_palette_256.palette[i_max];
int min_int = min_rgb.r << 16 | min_rgb.g << 8 | min_rgb.b;
int max_int = max_rgb.r << 16 | max_rgb.g << 8 | max_rgb.b;
FILE *fp = state.out_file;
switch (state.args.out_format)
rgba8 bg = c_palette_256.palette[bgi];
rgba8 fg = c_palette_256.palette[fgi];
FILE *fp = s.out_file;
switch (s.args.out_format)
{
case ASC_FMT_JSON:
fprintf(fp, "{ \"char\": \"\\u28%d\", \"fg\": %d, \"bg\": %d }",
ch, max_int, min_int);
fprintf(fp, S_JSON_PRGB, 0x2800 | ch, RGBN(fg), RGBN(bg));
if (!final) fprintf(fp, ", ");
break;
case ASC_FMT_HTML:
fprintf(fp, "<td style=\"color: rgb(%d, %d, %d); background: rgb(%d, %d, %d);\">&#%d;</td>",
max_rgb.r, max_rgb.g, max_rgb.b, min_rgb.r, min_rgb.g, min_rgb.b,
0x2800 | ch);
fprintf(fp, S_HTML_PCBR, fg.r, fg.g, fg.b, bg.r, bg.g, bg.b, 0x2800 | ch);
break;
case ASC_FMT_ANSI:
fprintf(fp, "\033[38;5;%d;48;5;%dm", i_max, i_min);
__bra_putc_raw(state, ch);
fprintf(fp, S_ANSI_256, fgi, bgi);
__bra_putc_raw(s, ch);
break;
case ASC_FMT_ENDL:
break;
}
}
void __bra_putc_true(asc_state_t state, rgba8 min, rgba8 max, uint8_t ch, bool final)
void __bra_putc_true(asc_state_t s, rgba8 bg, rgba8 fg, uint8_t ch, bool fin)
{
int max_int = max.r << 16 | max.g << 8 | max.b;
int min_int = min.r << 16 | min.g << 8 | min.b;
FILE *fp = state.out_file;
switch (state.args.out_format)
int max_int = fg.r << 16 | fg.g << 8 | fg.b;
int min_int = bg.r << 16 | bg.g << 8 | bg.b;
FILE *fp = s.out_file;
if (s.args.out_format == ASC_FMT_JSON)
{
case ASC_FMT_JSON:
fprintf(fp, "{ \"char\": \"\\u%d\", \"fg\": %d, \"bg\": %d }",
0x2800 | ch, max_int, min_int);
if (!final) fprintf(fp, ", ");
break;
case ASC_FMT_HTML:
fprintf(fp, "<td style=\"color: rgb(%d, %d, %d); background: rgb(%d, %d, %d);\">&#%d;</td>",
max.r, max.g, max.b, min.r, min.g, min.b, 0x2800 | ch);
break;
default:
fprintf(fp, "\033[38;2;%d;%d;%d;48;2;%d;%d;%dm",
max.r, max.g, max.b, min.r, min.g, min.b);
__bra_putc_raw(state, ch);
break;
fprintf(fp, S_JSON_PRGB, 0x2800 | ch, max_int, min_int);
if (!fin) fprintf(fp, ", ");
}
else if (s.args.out_format == ASC_FMT_HTML)
{
fprintf(fp, S_HTML_PCBR, fg.r, fg.g, fg.b, bg.r, bg.g, bg.b, 0x2800 | ch);
}
else
{
fprintf(fp, S_ANSI_RGB, fg.r, fg.g, fg.b, bg.r, bg.g, bg.b);
__bra_putc_raw(s, ch);
}
}
void __bra_end_line(asc_state_t state, bool final)
{
switch (state.args.out_format)
if (state.args.out_format == ASC_FMT_JSON)
fprintf(state.out_file, final ? S_JSON_LEND_FINAL : S_JSON_LEND);
else if (state.args.out_format == ASC_FMT_HTML)
fprintf(state.out_file, S_HTML_LEND);
else
{
case ASC_FMT_JSON:
fprintf(state.out_file, final ? " ]\n" : " ],\n");
break;
case ASC_FMT_HTML:
fprintf(state.out_file, "</tr>\n");
break;
default:
if (state.args.out_style != ASC_STL_BLACKWHITE)
fprintf(state.out_file, "\033[0m");
fprintf(state.out_file, "\n");
break;
if (state.args.out_style != ASC_STL_BLACKWHITE)
fprintf(state.out_file, "\033[0m");
fprintf(state.out_file, "\n");
}
}
void __bra_end_output(asc_state_t state)
{
switch (state.args.out_format)
{
case ASC_FMT_JSON:
fprintf(state.out_file, " ]\n}");
break;
case ASC_FMT_HTML:
fprintf(state.out_file, "</table>\n");
break;
default:
break;
}
if (state.args.out_format == ASC_FMT_JSON)
fprintf(state.out_file, " ]\n}");
else if (state.args.out_format == ASC_FMT_HTML)
fprintf(state.out_file, "</table>\n");
}