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 CLIBS := -lm
INCLUDES := -Isrc INCLUDES := -Isrc
OBJECTS := obj/stb_image.o obj/stb_image_resize.o \ 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) [![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 5. Separate binaries for each mode
6. Git submodules. Yay. 6. Git submodules. Yay.
## Build instructions ## Build instructions
### 1. Make sure that you have C compiler and make ### 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 <getopt.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <errno.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 { typedef struct {
int value; int value;
char *strings[8]; char *strings[8];
char *description; char *description;
void *unused[2];
} __option_t; } __option_t;
int __find_value(const __option_t *options, char *option); int __find_value(const __option_t *options, char *option);
void __print_options(const __option_t *options); 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, { ASC_MOD_BLOCKS,
{ "b", "blk", "blocks", NULL }, { "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, { ASC_MOD_BRAILLE,
{ "r", "brl", "braille", NULL }, { "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, { ASC_MOD_GRADIENT,
{ "g", "grd", "gradient", NULL }, { "g", "grd", "gradient", NULL },
"Gradient of characters. No matching at all" }, "Gradient of characters. No matching at all",
NULL, NULL },
{ ASC_MOD_BRUTEFORCE, { ASC_MOD_BRUTEFORCE,
{ "f", "guess", "bruteforce", NULL }, { "f", "guess", "bruteforce", NULL },
"Looking for best possible character" }, "Looking for best possible character",
{ -1, { NULL }, NULL } NULL, NULL },
{ -1, { NULL }, NULL, NULL, NULL }
}; };
const __option_t __style_options[ASC_STL_ENDL + 1] = { const __option_t __style_options[ASC_STL_ENDL + 1] = {
{ ASC_STL_BLACKWHITE, { ASC_STL_BLACKWHITE,
{ "1", "bw", "black-white", "1bit", NULL }, { "1", "bw", "black-white", "1bit", NULL },
"1-bit black/white" }, "1-bit black/white", { NULL, NULL } },
{ ASC_STL_ANSI_VGA, { ASC_STL_ANSI_VGA,
{ "vga", "ansi-vga", NULL }, { "vga", "ansi-vga" },
"VGA palette" }, "VGA palette", { NULL, NULL } },
{ ASC_STL_ANSI_XTERM, { ASC_STL_ANSI_XTERM,
{ "xterm", "ansi-xterm", NULL }, { "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, { ASC_STL_ANSI_DISCORD,
{ "discord", "ansi-discord", NULL }, { "discord", "ansi-discord", NULL },
"Palette in Discord ANSI highlight" }, "Palette in Discord ANSI highlight", { NULL, NULL } },
{ ASC_STL_256COLOR, { ASC_STL_256COLOR,
{ "256", "pal256", "8bit", NULL }, { "256", "pal256", "8bit", NULL },
"256-color palette (default)" }, "256-color palette (default)", { NULL, NULL } },
{ ASC_STL_TRUECOLOR, { ASC_STL_TRUECOLOR,
{ "true", "truecolor", "24bit", NULL }, { "true", "truecolor", "24bit", NULL },
"24-bit RGB (TrueColor)" }, "24-bit RGB (TrueColor)", { NULL, NULL } },
{ ASC_STL_PALETTE, { ASC_STL_PALETTE,
{ "pal", "palette", "custom", NULL }, { "pal", "palette", "custom", NULL },
"Custom palette (specified via -P). Either GIMP palette file or N*3 RGB pixels" }, "Custom palette (specified via -P). "\
{ -1, { NULL }, NULL } "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] = { const __option_t __format_options[ASC_FMT_ENDL + 1] = {
{ ASC_FMT_ANSI, { ASC_FMT_ANSI,
{ "ansi", "raw", NULL }, { "ansi", "raw", NULL },
"Output, suitable for terminal (default)" }, "Output, suitable for terminal (default)",
{ NULL, NULL } },
{ ASC_FMT_HTML, { ASC_FMT_HTML,
{ "html", NULL }, { "html", NULL },
"Output as HTML table" }, "Output as HTML table",
{ NULL, NULL } },
{ ASC_FMT_JSON, { ASC_FMT_JSON,
{ "json", NULL }, { "json", NULL },
"Output as JSON 2D array of characters with properties" }, "Output as JSON 2D array of characters with properties",
{ -1, { NULL }, NULL } { NULL, NULL } },
{ -1, { NULL }, NULL, { NULL, NULL } }
}; };
void usage(int argc, char **argv) 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, "-P PALETTE\tPath to palette file (when -S pal)\n");
fprintf(stderr, "\n\n"); fprintf(stderr, "\n\n");
fprintf(stderr, "Options for MODE:\n"); fprintf(stderr, "Options for MODE:\n");
__print_options(__mode_options); __print_options((const __option_t*)asc_handlers);
fprintf(stderr, "Options for STYLE:\n"); fprintf(stderr, "Options for STYLE:\n");
__print_options(__style_options); __print_options(__style_options);
fprintf(stderr, "Options for FORMAT:\n"); fprintf(stderr, "Options for FORMAT:\n");
@ -112,6 +124,10 @@ void version(int argc, char **argv)
fprintf(stderr, "See LICENSE file for details\n"); 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) int parse_args(int argc, char **argv, asc_args_t *args)
{ {
args->input_filename = NULL; args->input_filename = NULL;
@ -136,11 +152,9 @@ int parse_args(int argc, char **argv, asc_args_t *args)
case 'h': case 'h':
usage(argc, argv); usage(argc, argv);
return 1; return 1;
break;
case 'V': case 'V':
version(argc, argv); version(argc, argv);
return 1; return 1;
break;
case 'd': case 'd':
args->dither = true; args->dither = true;
break; break;
@ -160,7 +174,7 @@ int parse_args(int argc, char **argv, asc_args_t *args)
break; break;
case 'M': case 'M':
{ {
int val = __find_value(__mode_options, optarg); int val = __find_value((const __option_t*)asc_handlers, optarg);
if (val < 0) if (val < 0)
{ {
fprintf(stderr, "Error: invalid mode '%s'\n", optarg); 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); fprintf(stderr, "Error: Unknown parameter -%c\n", optopt);
} }
return -2; return -2;
default:
fprintf(stderr, "Error: UNREACHABLE: getopt switch gone wrong\n");
break;
} }
} }
if (args->out_style == ASC_STL_PALETTE && args->palette_filename == NULL) 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; return -3;
} }
if (argc <= optind || argc < 2) if (argc <= optind || argc < 2)
@ -225,11 +242,16 @@ int parse_args(int argc, char **argv, asc_args_t *args)
return 0; 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) int prepare_state(int argc, char **argv, asc_args_t args, asc_state_t *state)
{ {
(void)argc; (void)argv; (void)argc; (void)argv;
state->args = args; state->args = args;
// Loading image
FILE *image_file; FILE *image_file;
if ((image_file = fopen(args.input_filename, "rb")) == NULL if ((image_file = fopen(args.input_filename, "rb")) == NULL
|| ferror(image_file) != 0) || 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)); args.input_filename, err, strerror(err));
return -100 - err; return -100 - err;
} }
state->source_image = image_load(image_file); state->source_image = image_load(image_file);
fclose(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"); case ASC_STL_PALETTE:
if (fp == NULL) {
{ FILE *fp = fopen(args.palette_filename, "rb");
int err = errno; if (fp == NULL)
fprintf(stderr, "Error: failed to open file %s for reading: %d: %s\n", {
args.palette_filename, err, strerror(err)); int err = errno;
return -100 - err; fprintf(stderr, "Error: failed to open file %s for reading: %d: %s\n",
} args.palette_filename, err, strerror(err));
state->palette = calloc(1, sizeof(palette_t)); return -100 - err;
if (!load_palette(state->palette, fp)) }
{ state->palette = calloc(1, sizeof(palette_t));
fprintf(stderr, "Error: failed to read palette\n"); if (!load_palette(state->palette, fp))
fclose(fp); {
return -7; fprintf(stderr, "Error: failed to read palette\n");
} fclose(fp);
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) // Output file configuration
make_pal256(&c_palette_256, c_palette_ansi_vga);
state->out_file = stdout; state->out_file = stdout;
if (strcmp(args.output_filename, "-")) if (strcmp(args.output_filename, "-"))
state->out_file = fopen(args.output_filename, "wb"); 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)); args.output_filename, err, strerror(err));
return -100 - err; return -100 - err;
} }
return 0; return 0;
} }

View File

@ -69,6 +69,19 @@ typedef struct {
FILE *out_file; FILE *out_file;
} asc_state_t; } 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); void usage(int argc, char **argv);
int parse_args(int argc, char **argv, asc_args_t *args); 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); 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; 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) void make_pal256(palette_t *dst, palette_t ansi)
{ {
if (dst->n_colors == 256) return; 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 (fread(head, sizeof(char), 12, fp) < 12) return false;
if (fseek(fp, 0, SEEK_SET) != 0) return false; if (fseek(fp, 0, SEEK_SET) != 0) return false;
if (!strncmp(head, "GIMP Palette", 12)) if (!strncmp(head, "GIMP Palette", 12))
{
return load_palette_gpl(pal, fp); return load_palette_gpl(pal, fp);
} return load_palette_raw(pal, fp);
else
{
return load_palette_raw(pal, fp);
}
} }

View File

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

View File

@ -3,41 +3,27 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.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; case ASC_STL_BLACKWHITE:
switch (state->args.out_style) case ASC_STL_ANSI_VGA:
{ case ASC_STL_ANSI_XTERM:
case ASC_STL_BLACKWHITE: case ASC_STL_ANSI_DISCORD:
res = image_dither(state->image, c_palette_bw); case ASC_STL_256COLOR:
break; res = image_dither(sta->image, *get_palette_by_id(sta->args.out_style));
case ASC_STL_ANSI_VGA: break;
res = image_dither(state->image, c_palette_ansi_vga); case ASC_STL_PALETTE:
break; res = image_dither(sta->image, *sta->palette);
case ASC_STL_ANSI_XTERM: break;
res = image_dither(state->image, c_palette_ansi_xterm); case ASC_STL_TRUECOLOR:
break; case ASC_STL_ENDL:
case ASC_STL_ANSI_DISCORD: return;
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;
}
} }
image_unload(sta->image);
sta->image = res;
} }
void c_fatal(int code, const char *reason) 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); fprintf(stderr, "Error: %s\n", reason);
exit(code); 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" #include "args.h"
#define CLAMP(min, val, max) ((val)>(max)?(max):((val)<(min)?(min):(val)))
void c_fatal(int code, const char *reason); void c_fatal(int code, const char *reason);
void m_prepare_dither(asc_state_t *state); void m_prepare_dither(asc_state_t *state);
palette_t *get_palette_by_id(asc_style_t stl);
#endif #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 "image.h"
#include "stb_image.h" #include "stb_image.h"
#include "stb_image_resize.h" #include "stb_image_resize.h"
@ -29,22 +30,19 @@ image_t *image_resize(image_t *img, int width, int height)
return res; 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) 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; if (x < 0 || x >= img->width || y < 0 || y >= img->height) return;
int i = x + y * img->width; int i = x + y * img->width;
rgba8 pix = img->pixels[i]; rgba8 pix = img->pixels[i];
int dst[3] = { pix.r, pix.g, pix.b }; int dst[3] = { pix.r, pix.g, pix.b };
dst[0] += (int)(((float)err[0]) * bias); img->pixels[i].r = CLAMP(0, dst[0] + (int)((float)err[0] * bias), 255);
dst[1] += (int)(((float)err[1]) * bias); img->pixels[i].g = CLAMP(0, dst[1] + (int)((float)err[1] * bias), 255);
dst[2] += (int)(((float)err[2]) * bias); img->pixels[i].b = CLAMP(0, dst[2] + (int)((float)err[2] * bias), 255);
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]));
} }
// 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 *image_dither_fn(image_t *img, dither_quantizer_t quantize, void *param)
{ {
image_t *res = calloc(1, sizeof(image_t)); image_t *res = calloc(1, sizeof(image_t));

View File

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

View File

@ -22,6 +22,7 @@
#include "args.h" #include "args.h"
#include "mod_blocks.h" #include "mod_blocks.h"
#include "mod_braille.h" #include "mod_braille.h"
#include "commons.h"
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
@ -35,25 +36,11 @@ int main(int argc, char **argv)
if (res == 1) return 0; if (res == 1) return 0;
if (res < 0) return -res; if (res < 0) return -res;
switch (args.mode) asc_handler_t handler = asc_handlers[args.mode];
{ if (handler.prepare == NULL)
case ASC_MOD_BLOCKS: c_fatal(12, "this mode is not implemented yet");
mod_blocks_prepare(&state);
mod_blocks_main(state); handler.prepare(&state);
break; handler.main(state);
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;
}
return 0; return 0;
} }

View File

@ -3,6 +3,7 @@
#include "image.h" #include "image.h"
#include "colors.h" #include "colors.h"
#include "commons.h" #include "commons.h"
#include "fmt_strings.h"
const char *BLOCKS[4] = { " ", "\xe2\x96\x80", "\xe2\x96\x84", "\xe2\x96\x88" }; const char *BLOCKS[4] = { " ", "\xe2\x96\x80", "\xe2\x96\x84", "\xe2\x96\x88" };
const char *BLOCKS_ESC[4] = { " ", "\\u2580", "\\u2584", "\\u2588" }; 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); state->args.width, state->args.height * 2, &w, &h);
h = (h / 2) * 2; h = (h / 2) * 2;
state->image = image_resize(state->source_image, w, h); 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) 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 __blk_start_line(FILE *fp, asc_format_t fmt, bool final)
{ {
(void)final; (void)final;
switch (fmt) if (fmt == ASC_FMT_JSON)
{ fprintf(fp, S_JSON_LSTA);
case ASC_FMT_JSON: else if (fmt == ASC_FMT_HTML)
fprintf(fp, " [\n"); fprintf(fp, S_HTML_LSTA);
break;
case ASC_FMT_HTML:
fprintf(fp, "<tr>");
default:
break;
}
} }
void __blk_end_line(FILE *fp, asc_format_t fmt, asc_style_t stl, bool final) 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) switch (fmt)
{ {
case ASC_FMT_JSON: case ASC_FMT_JSON:
fprintf(fp, "{ \"char\": \"%s\", \"fg\": %d, \"bg\": %d }", fprintf(fp, S_JSON_PBLK, BLOCKS_ESC[1], top_int, bot_int);
BLOCKS_ESC[1], top_int, bot_int);
if (!final) fprintf(fp, ", "); if (!final) fprintf(fp, ", ");
break; break;
case ASC_FMT_HTML: case ASC_FMT_HTML:
fprintf(fp, "<td style=\"color: rgb(%d, %d, %d); background: rgb(%d, %d, %d);\">%s</td>", fprintf(fp, S_HTML_PBLK, top_rgb.r, top_rgb.g, top_rgb.b,
top_rgb.r, top_rgb.g, top_rgb.b, bot_rgb.r, bot_rgb.g, bot_rgb.b, BLOCKS[1]);
bot_rgb.r, bot_rgb.g, bot_rgb.b,
BLOCKS[1]);
break; break;
default: default:
fprintf(fp, "\033[%d;%dm%s", fprintf(fp, S_ANSI_S, ct + (ct >= 8 ? 82 : 30), cb + (cb >= 8 ? 92 : 40),
ct + (ct >= 8 ? 82 : 30),
cb + (cb >= 8 ? 92 : 40),
BLOCKS[1]); BLOCKS[1]);
break; 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_XTERM:
case ASC_STL_ANSI_DISCORD: case ASC_STL_ANSI_DISCORD:
{ {
palette_t pal; palette_t *pal = get_palette_by_id(state.args.out_style);
switch (state.args.out_style) int index_top = closest_color(*pal, top),
{ index_bot = closest_color(*pal, bot);
case ASC_STL_ANSI_VGA: pal = c_palette_ansi_vga; break; __blk_putc_ansi(fp, fmt, final, index_top, index_bot, *pal);
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);
} }
break; break;
case ASC_STL_256COLOR: 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: case ASC_STL_PALETTE:
{ {
palette_t *pal = state.palette; palette_t *pal = state.palette;
rgba8 pal_top = pal->palette[closest_color(*pal, top)]; __blk_putc_truecolor(fp, fmt, final,
rgba8 pal_bot = pal->palette[closest_color(*pal, bot)]; clamp_to_pal(*pal, top), clamp_to_pal(*pal, bot));
__blk_putc_truecolor(fp, fmt, final, pal_top, pal_bot);
} }
break;
case ASC_STL_ENDL: case ASC_STL_ENDL:
break; break;
} }

View File

@ -3,18 +3,31 @@
#include "image.h" #include "image.h"
#include "colors.h" #include "colors.h"
#include "commons.h" #include "commons.h"
#include "fmt_strings.h"
int __bra_best_match_i(rgba8 a, rgba8 b, rgba8 t); 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_raw(asc_state_t s, uint8_t ch);
void __bra_putc_esc(asc_state_t state, uint8_t ch); void __bra_putc_esc(asc_state_t s, uint8_t ch);
void __bra_start_output(asc_state_t state); void __bra_start_output(asc_state_t s);
void __bra_start_line(asc_state_t state, bool final); void __bra_start_line(asc_state_t s, bool final);
void __bra_put_pixel(asc_state_t sta, rgba8 min, rgba8 max, uint8_t ch, 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 state, int i_min, int i_max, uint8_t ch, palette_t pal, 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 state, int i_min, int i_max, uint8_t ch, 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 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 final);
void __bra_end_line(asc_state_t state, bool final); void __bra_end_line(asc_state_t s, bool final);
void __bra_end_output(asc_state_t state); 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) 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); state->args.width * 2, state->args.height * 4, &w, &h);
w = (w / 2) * 2; h = (h / 4) * 4; w = (w / 2) * 2; h = (h / 4) * 4;
state->image = image_resize(state->source_image, w, h); 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) 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; image_t *img = state.image;
uint8_t braille_char = 0x00; uint8_t braille_char = 0x00;
rgba8 pix2x4[8]; rgba8 block[8];
rgba8 color_max, color_min; rgba8 color_max, color_min;
int dist_max, dist_min, dist_min_d = 0xffffff, dist; 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); __bra_start_line(state, final);
for (int x = 0; x < img->width; x += 2) for (int x = 0; x < img->width; x += 2)
{ {
pix2x4[0] = img->pixels[(x + 0) + (y + 0) * img->width]; __bra_update2x4(img, block, x, y);
pix2x4[3] = img->pixels[(x + 1) + (y + 0) * img->width]; color_max = color_min = block[0];
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];
dist_max = 0; dist_max = 0;
dist_min = dist_min_d; dist_min = dist_min_d;
for (int i = 0; i < 8; i++) 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) if (dist < dist_min)
{ {
dist_min = dist; dist_min = dist;
color_min = pix2x4[i]; color_min = block[i];
} }
if (dist > dist_max) if (dist > dist_max)
{ {
dist_max = dist; dist_max = dist;
color_max = pix2x4[i]; color_max = block[i];
} }
} }
braille_char = 0x00; braille_char = 0x00;
for (int i = 0; i < 8; i++) 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); 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) void __bra_start_output(asc_state_t state)
{ {
switch (state.args.out_format) int w = state.image->width / 2, h = state.image->height / 4;
{ if (state.args.out_format == ASC_FMT_JSON)
case ASC_FMT_JSON: fprintf(state.out_file, S_JSON_HEAD, w, h);
fprintf(state.out_file, "{\n"); else if (state.args.out_format == ASC_FMT_HTML)
fprintf(state.out_file, " \"width\": %d,\n", state.image->width / 2); fprintf(state.out_file, S_HTML_HEAD);
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;
}
} }
void __bra_start_line(asc_state_t state, bool final) void __bra_start_line(asc_state_t state, bool final)
{ {
(void)final; (void)final;
switch (state.args.out_format) if (state.args.out_format == ASC_FMT_JSON)
{ fprintf(state.out_file, S_JSON_LSTA);
case ASC_FMT_JSON: else if (state.args.out_format == ASC_FMT_HTML)
fprintf(state.out_file, " [\n"); fprintf(state.out_file, S_HTML_LSTA);
break;
case ASC_FMT_HTML:
fprintf(state.out_file, "<tr>");
default:
break;
}
} }
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_VGA:
case ASC_STL_ANSI_XTERM: case ASC_STL_ANSI_XTERM:
case ASC_STL_ANSI_DISCORD: case ASC_STL_ANSI_DISCORD:
{ {
palette_t pal; palette_t pal = *get_palette_by_id(s.args.out_style);
switch (state.args.out_style) __bra_putc_ansi(s,
{ closest_color(pal, min), closest_color(pal, max), ch, pal, fin);
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);
} }
break; break;
case ASC_STL_256COLOR: case ASC_STL_256COLOR:
{ __bra_putc_256(s, closest_color(c_palette_256, min),
__bra_putc_256(state, closest_color(c_palette_256, min), closest_color(c_palette_256, max), ch, fin);
closest_color(c_palette_256, max), ch, final);
}
break; break;
case ASC_STL_TRUECOLOR: case ASC_STL_TRUECOLOR:
__bra_putc_true(state, min, max, ch, final); __bra_putc_true(s, min, max, ch, fin);
break; break;
case ASC_STL_PALETTE: case ASC_STL_PALETTE:
{ __bra_putc_true(s, clamp_to_pal(*s.palette, min),
palette_t *pal = state.palette; clamp_to_pal(*s.palette, max), ch, fin);
__bra_putc_true(state, pal->palette[closest_color(*pal, min)],
pal->palette[closest_color(*pal, max)], ch, final);
}
break; break;
case ASC_STL_BLACKWHITE: case ASC_STL_BLACKWHITE:
case ASC_STL_ENDL: 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]; rgba8 bg = pal.palette[bgi], fg = pal.palette[fgi];
int min_int = min_rgb.r << 16 | min_rgb.g << 8 | min_rgb.b; FILE *fp = s.out_file;
int max_int = max_rgb.r << 16 | max_rgb.g << 8 | max_rgb.b; switch (s.args.out_format)
FILE *fp = state.out_file;
switch (state.args.out_format)
{ {
case ASC_FMT_JSON: case ASC_FMT_JSON:
fprintf(fp, "{ \"char\": \"\\u28%d\", \"fg\": %d, \"bg\": %d }", fprintf(fp, S_JSON_PRGB, ch, RGBN(fg), RGBN(bg));
ch, max_int, min_int); if (!fin) fprintf(fp, ", ");
if (!final) fprintf(fp, ", ");
break; break;
case ASC_FMT_HTML: case ASC_FMT_HTML:
fprintf(fp, "<td style=\"color: rgb(%d, %d, %d); background: rgb(%d, %d, %d);\">&#%d;</td>", fprintf(fp, S_HTML_PCBR, fg.r, fg.g, fg.b,
max_rgb.r, max_rgb.g, max_rgb.b, min_rgb.r, min_rgb.g, min_rgb.b, bg.r, bg.g, bg.b, 0x2800 | ch);
0x2800 | ch);
break; break;
case ASC_FMT_ANSI: case ASC_FMT_ANSI:
fprintf(fp, "\033[%d;%dm", i_max + (i_max > 8 ? 82 : 30), i_min + (i_min > 8 ? 82 : 30)); fprintf(fp, S_ANSI, fgi + (fgi > 8 ? 82 : 30), bgi + (bgi > 8 ? 82 : 30));
__bra_putc_raw(state, ch); __bra_putc_raw(s, ch);
break; break;
case ASC_FMT_ENDL: case ASC_FMT_ENDL:
break; 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 bg = c_palette_256.palette[bgi];
rgba8 max_rgb = c_palette_256.palette[i_max]; rgba8 fg = c_palette_256.palette[fgi];
int min_int = min_rgb.r << 16 | min_rgb.g << 8 | min_rgb.b; FILE *fp = s.out_file;
int max_int = max_rgb.r << 16 | max_rgb.g << 8 | max_rgb.b; switch (s.args.out_format)
FILE *fp = state.out_file;
switch (state.args.out_format)
{ {
case ASC_FMT_JSON: case ASC_FMT_JSON:
fprintf(fp, "{ \"char\": \"\\u28%d\", \"fg\": %d, \"bg\": %d }", fprintf(fp, S_JSON_PRGB, 0x2800 | ch, RGBN(fg), RGBN(bg));
ch, max_int, min_int);
if (!final) fprintf(fp, ", "); if (!final) fprintf(fp, ", ");
break; break;
case ASC_FMT_HTML: case ASC_FMT_HTML:
fprintf(fp, "<td style=\"color: rgb(%d, %d, %d); background: rgb(%d, %d, %d);\">&#%d;</td>", fprintf(fp, S_HTML_PCBR, fg.r, fg.g, fg.b, bg.r, bg.g, bg.b, 0x2800 | ch);
max_rgb.r, max_rgb.g, max_rgb.b, min_rgb.r, min_rgb.g, min_rgb.b,
0x2800 | ch);
break; break;
case ASC_FMT_ANSI: case ASC_FMT_ANSI:
fprintf(fp, "\033[38;5;%d;48;5;%dm", i_max, i_min); fprintf(fp, S_ANSI_256, fgi, bgi);
__bra_putc_raw(state, ch); __bra_putc_raw(s, ch);
break; break;
case ASC_FMT_ENDL: case ASC_FMT_ENDL:
break; 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 max_int = fg.r << 16 | fg.g << 8 | fg.b;
int min_int = min.r << 16 | min.g << 8 | min.b; int min_int = bg.r << 16 | bg.g << 8 | bg.b;
FILE *fp = state.out_file; FILE *fp = s.out_file;
switch (state.args.out_format) if (s.args.out_format == ASC_FMT_JSON)
{ {
case ASC_FMT_JSON: fprintf(fp, S_JSON_PRGB, 0x2800 | ch, max_int, min_int);
fprintf(fp, "{ \"char\": \"\\u%d\", \"fg\": %d, \"bg\": %d }", if (!fin) fprintf(fp, ", ");
0x2800 | ch, max_int, min_int); }
if (!final) fprintf(fp, ", "); else if (s.args.out_format == ASC_FMT_HTML)
break; {
case ASC_FMT_HTML: fprintf(fp, S_HTML_PCBR, fg.r, fg.g, fg.b, bg.r, bg.g, bg.b, 0x2800 | ch);
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); else
break; {
default: fprintf(fp, S_ANSI_RGB, fg.r, fg.g, fg.b, bg.r, bg.g, bg.b);
fprintf(fp, "\033[38;2;%d;%d;%d;48;2;%d;%d;%dm", __bra_putc_raw(s, ch);
max.r, max.g, max.b, min.r, min.g, min.b);
__bra_putc_raw(state, ch);
break;
} }
} }
void __bra_end_line(asc_state_t state, bool final) 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: if (state.args.out_style != ASC_STL_BLACKWHITE)
fprintf(state.out_file, final ? " ]\n" : " ],\n"); fprintf(state.out_file, "\033[0m");
break; fprintf(state.out_file, "\n");
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;
} }
} }
void __bra_end_output(asc_state_t state) void __bra_end_output(asc_state_t state)
{ {
switch (state.args.out_format) if (state.args.out_format == ASC_FMT_JSON)
{ fprintf(state.out_file, " ]\n}");
case ASC_FMT_JSON: else if (state.args.out_format == ASC_FMT_HTML)
fprintf(state.out_file, " ]\n}"); fprintf(state.out_file, "</table>\n");
break;
case ASC_FMT_HTML:
fprintf(state.out_file, "</table>\n");
break;
default:
break;
}
} }