Laid out basic project structure

This commit is contained in:
Casey 2022-01-31 14:40:06 +03:00
parent f78f66cf9a
commit 18701f56a6
13 changed files with 578 additions and 5 deletions

View File

@ -1,7 +1,9 @@
CFLAGS := -Wall -Wextra -Werror -pedantic -std=c99 CFLAGS := -Wall -Wextra -Werror -pedantic -std=c99 -ggdb
CLIBS := -lm CLIBS := -lm
INCLUDES := -Isrc INCLUDES := -Isrc
OBJECTS := obj/stb_image.o OBJECTS := obj/stb_image.o obj/stb_image_resize.o \
obj/colors.o obj/args.o obj/image.o \
obj/mod_blocks.o
all: lib asciify all: lib asciify

7
README.md Normal file
View File

@ -0,0 +1,7 @@
# Why?
I just wasn't happy with state of old project, so I thought about recreating it.
# Anything new?
Nothing.
Oh wait. It's single binary now. Yay.

265
src/args.c Normal file
View File

@ -0,0 +1,265 @@
#include "args.h"
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
typedef struct {
int value;
char *strings[8];
char *description;
} __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] = {
{ ASC_MOD_BLOCKS,
{ "b", "blk", "blocks", NULL },
"Box-drawing characters (\342\226\204) (default)" },
{ ASC_MOD_BRAILLE,
{ "r", "brl", "braille", NULL },
"Braille characters (literally stolen from MineOS)" },
{ ASC_MOD_GRADIENT,
{ "g", "grd", "gradient", NULL },
"Gradient of characters. No matching at all" },
{ ASC_MOD_BRUTEFORCE,
{ "f", "guess", "bruteforce", NULL },
"Looking for best possible character" },
{ -1, { NULL }, NULL }
};
const __option_t __style_options[ASC_STL_ENDL + 1] = {
{ ASC_STL_BLACKWHITE,
{ "1", "bw", "black-white", "1bit", NULL },
"1-bit black/white" },
{ ASC_STL_ANSI_VGA,
{ "vga", "ansi-vga", NULL },
"VGA palette" },
{ ASC_STL_ANSI_XTERM,
{ "xterm", "ansi-xterm", NULL },
"xTerm palette. A bit more rough, compared to VGA" },
{ ASC_STL_ANSI_DISCORD,
{ "discord", "ansi-discord", NULL },
"Palette in Discord ANSI highlight" },
{ ASC_STL_256COLOR,
{ "256", "pal256", "8bit", NULL },
"256-color palette (default)" },
{ ASC_STL_TRUECOLOR,
{ "true", "truecolor", "24bit", NULL },
"24-bit RGB (TrueColor)" },
{ ASC_STL_PALETTE,
{ "pal", "palette", "custom", NULL },
"Custom palette (specified via -P)" },
{ -1, { NULL }, NULL }
};
const __option_t __format_options[ASC_FMT_ENDL + 1] = {
{ ASC_FMT_ANSI,
{ "ansi", "raw", NULL },
"Output, suitable for terminal (default)" },
{ ASC_FMT_HTML,
{ "html", NULL },
"Output as HTML table" },
{ ASC_FMT_JSON,
{ "json", NULL },
"Output as JSON 2D array of characters with properties" },
{ -1, { NULL }, NULL }
};
void usage(int argc, char **argv)
{
(void)argc;
fprintf(stderr, "usage: %s ", *argv);
fprintf(stderr, "[-vhd] [-O FILE] [-W WIDTH] [-H HEIGHT] ");
fprintf(stderr, "[-M MODE] [-S STYLE] [-F FORMAT] [-P PALETTE] ");
fprintf(stderr, "FILE\n\n");
fprintf(stderr, "-v\t\tEnable verbose mode\n");
fprintf(stderr, "-h\t\tShow this help\n");
fprintf(stderr, "-d\t\tEnable dithering\n");
fprintf(stderr, "\n");
fprintf(stderr, "-O FILE\t\tOutput file. Default: - (stdout)\n");
fprintf(stderr, "-W WIDTH\tOutput width (in characters)\n");
fprintf(stderr, "-H HEIGHT\tOutput height (in characters)\n");
fprintf(stderr, "-M MODE\t\tOutput mode\n");
fprintf(stderr, "-S STYLE\tStyle (palette)\n");
fprintf(stderr, "-F FORMAT\tOutput format\n");
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);
fprintf(stderr, "Options for STYLE:\n");
__print_options(__style_options);
fprintf(stderr, "Options for FORMAT:\n");
__print_options(__format_options);
}
int parse_args(int argc, char **argv, asc_args_t *args)
{
args->input_filename = NULL;
args->output_filename = "-";
args->palette_filename = NULL;
args->width = 80;
args->height = 24;
args->out_format = ASC_FMT_ANSI;
args->out_style = ASC_STL_256COLOR;
args->mode = ASC_MOD_BLOCKS;
args->dither = false;
args->verbose = false;
args->charset = " .'-*+$@";
int c;
while ((c = getopt(argc, argv, "vhdW:H:M:S:F:P:O:")) != -1)
{
switch (c)
{
case 'v':
args->verbose = true;
break;
case 'h':
usage(argc, argv);
return 1;
break;
case 'd':
args->dither = true;
break;
case 'W':
if ((args->width = atoi(optarg)) < 0)
{
fprintf(stderr, "Error: WIDTH is invalid\n");
return -1;
}
break;
case 'H':
if ((args->height = atoi(optarg)) < 0)
{
fprintf(stderr, "Error: HEIGHT is invalid\n");
return -1;
}
break;
case 'M':
{
int val = __find_value(__mode_options, optarg);
if (val < 0)
{
fprintf(stderr, "Error: invalid mode '%s'\n", optarg);
return -1;
}
args->mode = val;
}
break;
case 'S':
{
int val = __find_value(__style_options, optarg);
if (val < 0)
{
fprintf(stderr, "Error: invalid style '%s'\n", optarg);
return -1;
}
args->out_style = val;
}
break;
case 'F':
{
int val = __find_value(__format_options, optarg);
if (val < 0)
{
fprintf(stderr, "Error: invalid format '%s'\n", optarg);
return -1;
}
args->out_format = val;
}
break;
case 'P':
args->palette_filename = optarg;
break;
case 'O':
args->output_filename = optarg;
break;
case '?':
if (optopt == 'O'
|| optopt == 'W' || optopt == 'H'
|| optopt == 'S' || optopt == 'M' || optopt == 'F')
{
fprintf(stderr, "Error: missing argument for -%c\n", optopt);
}
else
{
fprintf(stderr, "Error: Unknown parameter -%c\n", optopt);
}
return -2;
}
}
if (args->out_style == ASC_STL_PALETTE && args->palette_filename == NULL)
{
fprintf(stderr, "Error: no palette file provided, but palette mode selected\n");
return -3;
}
if (argc <= optind || argc < 2)
{
fprintf(stderr, "Error: no image provided\n");
return -2;
}
args->input_filename = argv[optind];
return 0;
}
int prepare_state(int argc, char **argv, asc_args_t args, asc_state_t *state)
{
(void)argc; (void)argv;
state->args = args;
FILE *image_file;
if ((image_file = fopen(args.input_filename, "rb")) == NULL
|| ferror(image_file) != 0)
{
int err = errno;
fprintf(stderr, "Error: failed to open file %s for reading: %d: %s\n",
args.input_filename, err, strerror(err));
return -100 - err;
}
state->source_image = image_load(image_file);
fclose(image_file);
state->out_file = stdout;
if (strcmp(args.output_filename, "-"))
state->out_file = fopen(args.output_filename, "wb");
if (state->out_file == NULL)
{
int err = errno;
fprintf(stderr, "Error: failed to open file %s for writing: %d: %s\n",
args.output_filename, err, strerror(err));
return -100 - err;
}
return 0;
}
int __find_value(const __option_t *options, char *option)
{
__option_t *opt;
while ((opt = (__option_t *)options++)->value >= 0)
{
char *str = opt->strings[0];
for (int i = 0; str != NULL; i++, str = opt->strings[i])
{
if (!strcmp(str, option)) return opt->value;
}
}
return -1;
}
void __print_options(const __option_t *options)
{
__option_t *opt;
while ((opt = (__option_t *)options++)->value >= 0)
{
fprintf(stderr, " - %s:\n\t", opt->description);
char *str = opt->strings[0];
for (int i = 0; str != NULL; i++, str = opt->strings[i])
{
fprintf(stderr, "%s ", str);
}
fprintf(stderr, "\n");
}
}

59
src/args.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef _ARGS_H_
#define _ARGS_H_
#include <stdbool.h>
#include <stdio.h>
#include "image.h"
typedef enum {
ASC_MOD_BLOCKS = 0,
ASC_MOD_BRAILLE = 1,
ASC_MOD_GRADIENT = 2,
ASC_MOD_BRUTEFORCE = 3,
ASC_MOD_ENDL = 4
} asc_mode_t;
typedef enum {
ASC_FMT_ANSI = 0,
ASC_FMT_HTML = 1,
ASC_FMT_JSON = 2,
ASC_FMT_ENDL = 3
} asc_format_t;
typedef enum {
ASC_STL_BLACKWHITE = 0,
ASC_STL_ANSI_VGA = 1,
ASC_STL_ANSI_XTERM = 2,
ASC_STL_ANSI_DISCORD = 3,
ASC_STL_256COLOR = 4,
ASC_STL_TRUECOLOR = 5,
ASC_STL_PALETTE = 6,
ASC_STL_ENDL = 7
} asc_style_t;
typedef struct {
char *input_filename;
char *output_filename;
char *palette_filename;
int width;
int height;
asc_format_t out_format;
asc_style_t out_style;
asc_mode_t mode;
bool dither;
bool verbose;
char *charset;
} asc_args_t;
typedef struct {
asc_args_t args;
image_t *source_image;
image_t *image;
palette_t *palette;
FILE *out_file;
} asc_state_t;
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);
#endif

98
src/colors.c Normal file
View File

@ -0,0 +1,98 @@
#include "colors.h"
palette_t c_palette_ansi_discord = {
.n_colors = 8,
.palette = {
{ 0x4f, 0x54, 0x5c, 0 },
{ 0xd1, 0x31, 0x35, 0 },
{ 0x85, 0x99, 0x00, 0 },
{ 0xb5, 0x89, 0x00, 0 },
{ 0x26, 0x8b, 0xd2, 0 },
{ 0xd3, 0x36, 0x82, 0 },
{ 0xd3, 0x36, 0x82, 0 },
{ 0xff, 0xff, 0xff, 0 },
}
};
palette_t c_palette_ansi_vga = {
.n_colors = 16,
.palette = {
{ 0, 0, 0, 0 },
{ 170, 0, 0, 0 },
{ 0, 170, 0, 0 },
{ 170, 85, 0, 0 },
{ 0, 0, 170, 0 },
{ 170, 0, 170, 0 },
{ 0, 170, 170, 0 },
{ 170, 170, 170, 0 },
{ 85, 85, 85, 0 },
{ 255, 85, 85, 0 },
{ 85, 255, 85, 0 },
{ 255, 255, 85, 0 },
{ 85, 85, 255, 0 },
{ 255, 85, 255, 0 },
{ 85, 255, 255, 0 },
{ 255, 255, 255, 0 }
}
};
palette_t c_palette_ansi_xterm = {
.n_colors = 16,
.palette = {
{ 0, 0, 0, 0 },
{ 205, 0, 0, 0 },
{ 0, 205, 0, 0 },
{ 205, 205, 0, 0 },
{ 0, 0, 238, 0 },
{ 205, 0, 205, 0 },
{ 0, 205, 205, 0 },
{ 229, 229, 229, 0 },
{ 127, 127, 127, 0 },
{ 255, 0, 0, 0 },
{ 0, 255, 0, 0 },
{ 255, 255, 0, 0 },
{ 0, 0, 252, 0 },
{ 255, 0, 255, 0 },
{ 0, 255, 255, 0 },
{ 255, 255, 255, 0 },
}
};
int closest_color(palette_t *pal, rgba8 color)
{
int nearest = -1;
int32_t min_distance = 0x0fffffff;
for (int i = 0; i < pal->n_colors; i++)
{
rgba8 pal_color = pal->palette[i];
int16_t dr = pal_color.r - color.r;
int16_t dg = pal_color.g - color.g;
int16_t db = pal_color.b - color.b;
int32_t distance = dr * dr + dg * dg + db * db;
if (distance < min_distance)
{
min_distance = distance;
nearest = i;
}
}
return nearest;
}
void load_palette_gpl(palette_t *pal, FILE *fp)
{
(void)pal; (void)fp;
// TODO: load GNU palette file
}
void load_palette_raw(palette_t *pal, FILE *fp)
{
(void)pal; (void)fp;
// TODO: load raw palette file
}
void load_palette(palette_t *pal, FILE *fp)
{
(void)pal; (void)fp;
// TODO: guess palette file type and load it
}

24
src/colors.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef _COLORS_H_
#define _COLORS_H_
#include <stdint.h>
#include <stdio.h>
typedef struct {
uint8_t r, g, b, a;
} rgba8;
typedef struct {
int n_colors;
rgba8 palette[255];
} palette_t;
extern palette_t c_palette_ansi_discord;
extern palette_t c_palette_ansi_vga;
extern palette_t c_palette_ansi_xterm;
int closest_color(palette_t *pal, rgba8 color);
void load_palette_gpl(palette_t *pal, FILE *fp);
void load_palette_raw(palette_t *pal, FILE *fp);
void load_palette(palette_t *pal, FILE *fp);
#endif

66
src/image.c Normal file
View File

@ -0,0 +1,66 @@
#include "image.h"
#include "stb_image.h"
#include "stb_image_resize.h"
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
image_t *image_load(FILE *file)
{
image_t *img = calloc(1, sizeof(image_t));
int n;
img->pixels = (rgba8 *)stbi_load_from_file(file,
&img->width, &img->height,
&n, STBI_rgb_alpha);
return img;
}
image_t *image_resize(image_t *img, int width, int height)
{
image_t *res = calloc(1, sizeof(image_t));
res->width = width;
res->height = height;
res->pixels = calloc(width * height, sizeof(rgba8));
stbir_resize_uint8((const unsigned char *)img->pixels,
img->width, img->height, 0,
(unsigned char *)res->pixels,
res->width, res->height, 0, STBI_rgb_alpha);
return res;
}
void image_unload(image_t *img)
{
free(img->pixels);
free(img);
}
void get_size_keep_aspect(int w, int h, int dw, int dh, int *ow, int *oh)
{
*ow = dw;
*oh = dh;
float ratio = (float)w / (float)h;
float ratio_dst = (float)dw / (float)dh;
int tmp_1, tmp_2;
if (ratio_dst >= ratio)
{
tmp_1 = floor(dh * ratio);
tmp_2 = ceil(dh * ratio);
if (fabsf(ratio - (float)tmp_1 / dh) < fabsf(ratio - (float)tmp_1 / dh))
*ow = tmp_1 < 1 ? 1 : tmp_1;
else
*ow = tmp_2 < 1 ? 1 : tmp_2;
}
else
{
tmp_1 = floor(dw / ratio);
tmp_2 = ceil(dw / ratio);
if (tmp_2 == 0 ||
fabs(ratio - (float)dw / tmp_1) < fabs(ratio - (float)dw / tmp_2))
(*oh) = tmp_1 < 1 ? 1 : tmp_1;
else
(*oh) = tmp_2 < 1 ? 1 : tmp_2;
}
}

19
src/image.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef _IMAGE_H_
#define _IMAGE_H_
#include <stdio.h>
#include "colors.h"
typedef struct {
int width;
int height;
int errno;
rgba8 *pixels;
} image_t;
image_t *image_load(FILE *file);
image_t *image_resize(image_t *img, int width, int height);
void image_unload(image_t *img);
void get_size_keep_aspect(int w, int h, int dw, int dh, int *ow, int *oh);
#endif

View File

@ -1,11 +1,20 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <string.h>
#include "args.h"
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
(void)argc; asc_args_t args;
(void)argv; int res = parse_args(argc, argv, &args);
printf("ah shit, here we go again\n"); if (res == 1) return 0;
if (res < 0) return -res;
asc_state_t state;
res = prepare_state(argc, argv, args, &state);
if (res == 1) return 0;
if (res < 0) return -res;
return 0; return 0;
} }

11
src/mod_blocks.c Normal file
View File

@ -0,0 +1,11 @@
#include "mod_blocks.h"
void mod_blocks_prepare(asc_state_t *state)
{
(void)state;
}
void mod_blocks_main(asc_state_t state)
{
(void)state;
}

10
src/mod_blocks.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef _MOD_BLOCKS_
#define _MOD_BLOCKS_
#include <stdio.h>
#include "colors.h"
#include "args.h"
void mod_blocks_prepare(asc_state_t *state);
void mod_blocks_main(asc_state_t state);
#endif

2
src/stb_image_resize.c Normal file
View File

@ -0,0 +1,2 @@
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "stb_image_resize.h"

1
src/stb_image_resize.h Normal file
View File

@ -0,0 +1 @@
#include "../thirdparty/stb/stb_image_resize.h"