main.c (9921B)
1 #define _XOPEN_SOURCE 700 2 3 #include "mandelbrot.h" 4 5 #include <sys/socket.h> 6 #include <netdb.h> 7 8 #include <fcntl.h> 9 10 #include <getopt.h> 11 12 enum palette_type { 13 PALETTE_BASIC, 14 PALETTE_MONOCHROME, 15 PALETTE_GRAYSCALE, 16 PALETTE_HISTOGRAM, 17 PALETTE_SMOOTH, 18 }; 19 20 struct opts { 21 int verbose; 22 char *server_host; 23 char *server_port; 24 uint64_t xres, yres; 25 uint64_t max_iters; 26 enum palette_type palette; 27 char *outfile; 28 } opts = { 29 .verbose = 0, 30 .server_host = NULL, 31 .server_port = NULL, 32 .xres = 800, .yres = 600, 33 .max_iters = 100, 34 .palette = PALETTE_SMOOTH, 35 .outfile = "/tmp/mandelbrot.bmp", 36 }; 37 38 #define OPTSTR "hvS:r:i:p:o:" 39 40 static void 41 usage(char *prog) 42 { 43 fprintf(stderr, "Usage: %s [-hv] [-S <servaddr>] " 44 "[-r <xres>x<yres> ] [-i <maxiters>] " 45 "[-p basic|mono|gray|hist|smooth ] [-o <out.bmp>]\n", 46 prog); 47 48 fprintf(stderr, "\t-h : display help information\n"); 49 fprintf(stderr, "\t-v : enable verbose logging\n"); 50 fprintf(stderr, "\t-S : attempt to connect to the given server addr\n"); 51 fprintf(stderr, "\t-r : set output resolution (default: 800x600)\n"); 52 fprintf(stderr, "\t-i : set maximum iterations per pixel (default: 100)\n"); 53 fprintf(stderr, "\t-p : set colour palette (default: smooth)\n"); 54 fprintf(stderr, "\t-o : set output filename (default: /tmp/mandelbrot.bmp)\n"); 55 } 56 57 static int 58 parse_opts(int argc, char **argv, struct opts *opts) 59 { 60 int opt; 61 while ((opt = getopt(argc, argv, OPTSTR)) > 0) { 62 switch (opt) { 63 case 'v': 64 opts->verbose = 1; 65 break; 66 67 case 'S': { 68 char *split = strrchr(optarg, ':'); 69 if (!split) { 70 fprintf(stderr, "Failed to parse server addr: %s\n", optarg); 71 return -1; 72 } 73 74 *split = '\0'; 75 76 opts->server_host = optarg; 77 opts->server_port = ++split; 78 } break; 79 80 case 'r': { 81 char *saveptr; 82 char *xres = strtok_r(optarg, "x", &saveptr); 83 char *yres = strtok_r(NULL, "x", &saveptr); 84 85 if (!xres || !yres) { 86 fprintf(stderr, "Must provide resolution in format: <xres>x<yres>\n"); 87 return -1; 88 } 89 90 if ((opts->xres = strtoull(xres, NULL, 10)) == 0) { 91 fprintf(stderr, "Failed to parse x-resolution: %s\n", xres); 92 return -1; 93 } 94 95 if ((opts->yres = strtoull(yres, NULL, 10)) == 0) { 96 fprintf(stderr, "Failed to parse y-resolution: %s\n", yres); 97 return -1; 98 } 99 } break; 100 101 case 'i': 102 if ((opts->max_iters = strtoull(optarg, NULL, 10)) == 0) { 103 fprintf(stderr, "Failed to parse max iters: %s\n", optarg); 104 return -1; 105 } 106 break; 107 108 case 'p': 109 if (strcmp(optarg, "basic") == 0) { 110 opts->palette = PALETTE_BASIC; 111 } else if (strcmp(optarg, "mono") == 0) { 112 opts->palette = PALETTE_MONOCHROME; 113 } else if (strcmp(optarg, "gray") == 0) { 114 opts->palette = PALETTE_GRAYSCALE; 115 } else if (strcmp(optarg, "hist") == 0) { 116 opts->palette = PALETTE_HISTOGRAM; 117 } else if (strcmp(optarg, "smooth") == 0) { 118 opts->palette = PALETTE_SMOOTH; 119 } else { 120 fprintf(stderr, "Unknown palette: %s\n", optarg); 121 return -1; 122 } 123 break; 124 125 case 'o': 126 opts->outfile = optarg; 127 break; 128 129 default: 130 return -1; 131 } 132 } 133 134 return 0; 135 } 136 137 static inline uint32_t 138 hsv_to_rgb(float hue, float sat, float val) 139 { 140 float h = fmodf(hue, 360.0); 141 142 float hp = h / 60.0; 143 float c = val * sat; 144 float x = c * (1 - fabsf(fmodf(hp, 2.0) - 1)); 145 146 float rgb[3] = {0}; 147 148 if (0 <= hp && hp < 1) { rgb[0] = c; rgb[1] = x; } 149 else if (1 <= hp && hp < 2) { rgb[0] = x; rgb[1] = c; } 150 else if (2 <= hp && hp < 3) { rgb[1] = c; rgb[2] = x; } 151 else if (3 <= hp && hp < 4) { rgb[1] = x; rgb[2] = c; } 152 else if (4 <= hp && hp < 5) { rgb[0] = x; rgb[2] = c; } 153 else if (5 <= hp && hp < 6) { rgb[0] = c; rgb[2] = x; } 154 155 float m = val - c; 156 rgb[0] += m; 157 rgb[1] += m; 158 rgb[2] += m; 159 160 return pixel(rgb[0] * 255, rgb[1] * 255, rgb[2] * 255, 255); 161 } 162 163 static inline uint32_t 164 palette(float abs2, uint32_t iters, uint32_t max_iters, enum palette_type type) 165 { 166 switch (type) { 167 case PALETTE_BASIC: 168 return (iters == max_iters) ? pixel(0, 0, 0, 255) 169 : pixel(255, 255, 255, 255); 170 171 case PALETTE_MONOCHROME: { 172 float f = (float) iters / max_iters; 173 return (iters == max_iters) ? pixel(0, 0, 0, 255) 174 : pixel(f * 255, f * 255, f * 255, 255); 175 } break; 176 177 case PALETTE_GRAYSCALE: { 178 float smooth_iters = (iters + 1) - (logf(logf(sqrtf(abs2))) / M_LN2); 179 float sf = CLAMP(smooth_iters / max_iters, 0.0f, 1.0f); 180 float v = 255 * (1 - sf); 181 182 return (iters == max_iters) ? pixel(0, 0, 0, 255) : pixel(v, v, v, 255); 183 } break; 184 185 case PALETTE_HISTOGRAM: { 186 float f = (float) iters / max_iters; 187 // TODO: actually implement histogram colouring 188 return (iters == max_iters) ? pixel(0, 0, 0, 255) 189 : pixel(f * 255, f * 255, f * 255, 255); 190 } break; 191 192 case PALETTE_SMOOTH: { 193 float smooth_iters = (iters + 1) - (logf(logf(sqrtf(abs2))) / M_LN2); 194 float hue = 0.95 + 20 * smooth_iters; 195 return (iters == max_iters) ? pixel(0, 0, 0, 255) 196 : hsv_to_rgb(hue, 0.8, 1.0); 197 } break; 198 } 199 } 200 201 static void 202 paint_region(struct render_region *region, uint32_t *pixbuf, uint32_t *iterbuf, float *abs2buf, 203 enum palette_type palette_type) 204 { 205 size_t pixels = region->xres * region->yres; 206 207 for (size_t i = 0; i < pixels; i++) 208 pixbuf[i] = palette(abs2buf[i], iterbuf[i], region->max_iters, 209 palette_type); 210 } 211 212 static void 213 render_networked_mandelbrot(char const *host, char const *port, 214 uint32_t *pixbuf, uint32_t *iterbuf, float *abs2buf, 215 size_t width, size_t height, size_t max_iters, 216 enum palette_type palette_type) 217 { 218 struct addrinfo hints = { 219 .ai_family = AF_UNSPEC, 220 .ai_socktype = SOCK_STREAM, 221 .ai_protocol = IPPROTO_TCP, 222 .ai_flags = AI_NUMERICSERV, 223 }, *addrinfo, *ptr; 224 225 int res; 226 if ((res = getaddrinfo(host, port, &hints, &addrinfo))) { 227 fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(res)); 228 exit(EXIT_FAILURE); 229 } 230 231 int fd; 232 for (ptr = addrinfo; ptr; ptr = ptr->ai_next) { 233 fd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); 234 if (fd < 0) 235 continue; 236 237 if (connect(fd, ptr->ai_addr, ptr->ai_addrlen) < 0) { 238 close(fd); 239 continue; 240 } 241 242 break; 243 } 244 245 freeaddrinfo(addrinfo); 246 247 if (!ptr) { 248 fprintf(stderr, "Failed to connect to server\n"); 249 exit(EXIT_FAILURE); 250 } 251 252 struct render_region region = { 253 .xres = width, .yres = height, 254 .escape_val = 2, 255 .max_iters = max_iters, 256 .x0 = -2.0, .y0 = -1.5, .x1 = 0.5, .y1 = 1.5, 257 }; 258 259 // TODO: assemble request packet as fixed point 260 int nbytes_send = sendall(fd, ®ion, sizeof region); 261 printf("sent: %d bytes\n", nbytes_send); 262 263 // TODO: actually wait on a number of render results 264 size_t pixels = width * height; 265 int nbytes_recv = 0; 266 nbytes_recv += recvall(fd, iterbuf, pixels * sizeof *iterbuf); 267 nbytes_recv += recvall(fd, abs2buf, pixels * sizeof *abs2buf); 268 printf("received: %d bytes\n", nbytes_recv); 269 270 paint_region(®ion, pixbuf, iterbuf, abs2buf, palette_type); 271 272 close(fd); 273 } 274 275 static void 276 render_mandelbrot(uint32_t *pixbuf, uint32_t *iterbuf, float *abs2buf, 277 size_t width, size_t height, size_t max_iters, 278 enum palette_type palette_type) 279 { 280 struct render_region region = { 281 .xres = width, .yres = height, 282 .escape_val = 2, 283 .max_iters = max_iters, 284 .x0 = -2.0, .y0 = -1.5, .x1 = 0.5, .y1 = 1.5, 285 }; 286 287 render_region(®ion, iterbuf, abs2buf); 288 paint_region(®ion, pixbuf, iterbuf, abs2buf, palette_type); 289 } 290 291 static void 292 write_bitmap(int fd, uint32_t const *buf, size_t width, size_t height) 293 { 294 uint32_t pixels = width * height; 295 uint16_t pixel_size = 8 * sizeof *buf; 296 uint32_t pixel_bytes = pixels * sizeof *buf; 297 298 uint32_t dib_header_size = 40; 299 uint32_t bitmap_header_size = 14; 300 uint32_t header_size = bitmap_header_size + dib_header_size; 301 302 uint32_t file_size = header_size + pixel_bytes; 303 uint32_t reserved = 0; 304 305 int32_t xres = width, yres = -height; 306 uint16_t planes = 0; 307 uint32_t compression = 0; 308 uint32_t xdpi = 0, ydpi = 0; 309 uint32_t palette = 0, important = 0; 310 311 // bitmap header 312 write(fd, "BM", 2); 313 write(fd, &file_size, sizeof file_size); 314 write(fd, &reserved, sizeof reserved); 315 write(fd, &header_size, sizeof header_size); 316 317 // dib header: BITMAPINFOHEADER 318 write(fd, &dib_header_size, sizeof dib_header_size); 319 write(fd, &xres, sizeof xres); 320 write(fd, &yres, sizeof yres); 321 write(fd, &planes, sizeof planes); 322 write(fd, &pixel_size, sizeof pixel_size); 323 write(fd, &compression, sizeof compression); 324 write(fd, &pixel_bytes, sizeof pixel_bytes); 325 write(fd, &xdpi, sizeof xdpi); 326 write(fd, &ydpi, sizeof ydpi); 327 write(fd, &palette, sizeof palette); 328 write(fd, &important, sizeof important); 329 330 // pixel data 331 write(fd, buf, pixel_bytes); 332 } 333 334 int 335 main(int argc, char **argv) 336 { 337 if (parse_opts(argc, argv, &opts) < 0) { 338 usage(argv[0]); 339 exit(EXIT_FAILURE); 340 } 341 342 if (opts.verbose) { 343 fprintf(stderr, "info: verbose: %d\n", opts.verbose); 344 if (opts.server_host && opts.server_port) { 345 fprintf(stderr, "info: server: %s:%s\n", 346 opts.server_host, opts.server_port); 347 } 348 fprintf(stderr, "info: resolution: %" PRIu64 "x%" PRIu64 "\n", 349 opts.xres, opts.yres); 350 fprintf(stderr, "info: max iters: %" PRIu64 "\n", opts.max_iters); 351 fprintf(stderr, "info: palette: %d\n", opts.palette); 352 fprintf(stderr, "info: output: %s\n", opts.outfile); 353 } 354 355 size_t pixels = opts.xres * opts.yres; 356 357 uint32_t *pixbuf = malloc(pixels * sizeof *pixbuf); 358 assert(pixbuf); 359 360 uint32_t *iterbuf = malloc(pixels * sizeof *iterbuf); 361 assert(iterbuf); 362 363 float *abs2buf = malloc(pixels * sizeof *abs2buf); 364 assert(abs2buf); 365 366 if (opts.server_host && opts.server_port) { 367 render_networked_mandelbrot(opts.server_host, opts.server_port, 368 pixbuf, iterbuf, abs2buf, 369 opts.xres, opts.yres, opts.max_iters, 370 opts.palette); 371 } else { 372 render_mandelbrot(pixbuf, iterbuf, abs2buf, 373 opts.xres, opts.yres, opts.max_iters, 374 opts.palette); 375 } 376 377 int fd = creat(opts.outfile, 0644); 378 assert(fd > 0); 379 380 write_bitmap(fd, pixbuf, opts.xres, opts.yres); 381 382 close(fd); 383 384 exit(EXIT_SUCCESS); 385 }