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 }