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 }