ascii-gen

simple ascii image converter
git clone git://git.jakekoroman.com/ascii-gen
Log | Files | Refs | README

ascii_gen.c (5415B)


      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <string.h>
      4 #include <stdint.h>
      5 #include <getopt.h>
      6 #include <stdbool.h>
      7 
      8 #define STB_IMAGE_IMPLEMENTATION
      9 #include "libs/stb_image.h"
     10 
     11 #define STB_IMAGE_RESIZE_IMPLEMENTATION
     12 #include "libs/stb_image_resize.h"
     13 
     14 #define BUF_CAP 256
     15 
     16 #define internal static
     17 
     18 typedef uint8_t uint8;
     19 
     20 #define SET_LONG "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^`'. "
     21 #define SET_SIZE_LONG 70
     22 #define SET_SHORT "@%#*+=-:. "
     23 #define SET_SHORT_INVERTED " .:-=+*#%@"
     24 #define SET_SIZE_SHORT 10
     25 
     26 typedef enum {
     27 	SET_TYPE_SHORT,
     28 	SET_TYPE_LONG,
     29 	SET_TYPE_CUSTOM,
     30 } SetType;
     31 
     32 typedef struct {
     33 	char in_file[BUF_CAP];
     34 	char out_file[BUF_CAP];
     35 	char set[BUF_CAP];
     36 	SetType set_type; // defaults to short
     37 	int set_size;
     38 	int scale;
     39 	bool verbose;
     40 	bool inverted;
     41 } Options;
     42 
     43 internal inline int
     44 map(int x, int a, int b, int c, int d)
     45 {
     46 	return (x - a) * (d - c) / (b - a);
     47 }
     48 
     49 internal void
     50 print_docs(const char *fname)
     51 {
     52 	printf("Usage: %s img [-o file] [-s scale] [-S set] [-hltv]\n", fname); 
     53 	printf("  -h: Shows help\n");
     54 	printf("  -o: Output to file, if no file is given it will output to stdout\n");
     55 	printf("  -s: The scale of the output image, defaults to 1\n");
     56 	printf("  -S: The character set that will be used to generate the output, defaults to the builtin 'short' character set\n");
     57 	printf("  -l: Uses the builtin 'long' character set for generating the output\n");
     58 	printf("  -t: Uses the builtin 'short' character set for generating the output\n");
     59 	printf("  -i: Uses the builtin 'short' character set but inverted\n");
     60 	printf("  -v: Shows verbose info\n");
     61 }
     62 
     63 // Parses args with getopt and fills an Options struct of all the options passed
     64 // Resource: https://azrael.digipen.edu/~mmead/www/Courses/CS180/getopt.html
     65 internal void
     66 parse_args(int argc, char **argv, Options *options)
     67 {
     68 	int opt;
     69 	options->scale = 1;
     70 
     71 	while ((opt = getopt(argc, argv, ":o:hls:S:vic")) != -1)
     72 	{
     73 		switch (opt)
     74 		{
     75 		case 'o':
     76 			strncpy(options->out_file, optarg, BUF_CAP);
     77 			break;
     78 
     79 		case 'h':
     80 			print_docs(argv[0]);
     81 			exit(0);
     82 			break;
     83 
     84 		case 's': {
     85 			options->scale = atoi(optarg);
     86 			if (options->scale == 0) {
     87 				fprintf(stderr, "Invalid scale '%s'\n", optarg);
     88 				exit(69);
     89 			}
     90 		} break;
     91 
     92 		case 'v':
     93 			options->verbose = true;
     94 			break;
     95 
     96 		case 'l':
     97 			options->set_type = SET_TYPE_LONG;
     98 			options->set_size = SET_SIZE_LONG;
     99 			strncpy(options->set, SET_LONG, BUF_CAP);
    100 			break;
    101 
    102 			// 't' for tiny I guess? wanted to keep 's' for scale:
    103 		case 't':
    104 			// Redundant because it will default to short but sometimes
    105 			// it's nice to request it even though its default.
    106 			options->set_type = SET_TYPE_SHORT;
    107 			break;
    108 
    109 		case 'i':
    110 			options->inverted = true;
    111 			break;
    112 
    113 		case 'S': {
    114 			int size = strnlen(optarg, BUF_CAP);
    115 
    116 			options->set_size = size;
    117 			options->set_type = SET_TYPE_CUSTOM;
    118 			strncpy(options->set, optarg, size);
    119 			printf("sizeof input: %d\n", size);
    120 		} break;
    121 
    122 		case '?':
    123 			printf("Unknown Option: %c\n", optopt);
    124 			exit(69);
    125 			break;
    126 
    127 		case ':':
    128 			printf("Missing arg for %c\n", optopt);
    129 			exit(69);
    130 			break;
    131 
    132 		default:
    133 			printf("Something broke\n");
    134 			break;
    135 		}
    136 	}
    137 
    138 	if (options->set_type == SET_TYPE_SHORT) {
    139 		options->set_size = SET_SIZE_SHORT;
    140 		if (options->inverted)
    141 			strncpy(options->set, SET_SHORT_INVERTED, BUF_CAP);
    142 		else
    143 			strncpy(options->set, SET_SHORT, BUF_CAP);
    144 	}
    145 
    146 	if (optind < argc)
    147 		strncpy(options->in_file, argv[optind], BUF_CAP);
    148 }
    149 
    150 int
    151 main(int argc, char **argv)
    152 {
    153 	if (argc < 2) {
    154 		printf("Basic Usage: %s img [-o file]\n", argv[0]);
    155 		exit(69);
    156 	}
    157 
    158 	Options options = {0};
    159 	parse_args(argc, argv, &options);
    160 
    161 	if (options.verbose) {
    162 		printf("In File	 : %s\n", options.in_file);
    163 		printf("Out File : %s\n", options.out_file);
    164 		printf("Scale	 : %d\n", options.scale);
    165 		printf("Set size : %d\n", options.set_size);
    166 		printf("Set type : %d\n", options.set_type);
    167 		printf("Set		 : %s\n", options.set);
    168 	}
    169 
    170 	// TODO(jake): Maybe bundle this stuff up? including the resized stuff?
    171 	int width, height;
    172 	int channels = 1;
    173 	uint8 *image_bytes = stbi_load(options.in_file,&width, &height, NULL, channels);
    174 
    175 	if (!image_bytes) {
    176 		fprintf(stderr, "Couldn't Open file %s\n", options.in_file);
    177 		exit(69);
    178 	}
    179 
    180 	int nw = width / options.scale;
    181 	int nh = height / options.scale;
    182 	bool resized = false;
    183 	uint8 *resized_bytes;
    184 
    185 	if (options.scale > 1) {
    186 		int stride = 0;
    187 		resized = true;
    188 		resized_bytes = malloc(sizeof(uint8) * (nw * nh));
    189 		if (!stbir_resize_uint8(image_bytes, width, height, stride,
    190 								resized_bytes, nw, nh, stride, channels)) // only resizes if the scale is greater than 1
    191 		{
    192 			printf("Couldn't Resize Image\n");
    193 			exit(69);
    194 		}
    195 	} else {
    196 		resized_bytes = image_bytes;
    197 	}
    198 
    199 	FILE *f = NULL;
    200 	if (strncmp(options.out_file, "", BUF_CAP)) {
    201 		f = fopen(options.out_file, "w");
    202 		if (f == NULL) {
    203 			printf("Couldnt't create output file '%s'\n", options.out_file);
    204 			exit(69);
    205 		}
    206 	} else {
    207 		f = stdout;
    208 	}
    209 
    210 	for (size_t i = 0; i < nw * nh; ++i) {
    211 		int map_result = map(resized_bytes[i], 0, 255, 0, options.set_size - 1);
    212 		int idx = (options.set_size - 1) - map_result;
    213 
    214 		if (i % nw == 0) {
    215 			fprintf(f, "\n");
    216 		}
    217 
    218 		fprintf(f, "%c", options.set[idx]);
    219 	}
    220 	fprintf(f, "\n");
    221 
    222 	if (!(f == NULL || f == stdout))
    223 		fclose(f);
    224 
    225 	if (image_bytes)
    226 		stbi_image_free(image_bytes);
    227 
    228 	if (resized_bytes && resized)
    229 		stbi_image_free(resized_bytes);
    230 
    231 	return 0;
    232 }