ascii-gen

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

ascii_gen.c (5204B)


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