mandelbrot

mandelbrot.git
git clone git://git.lenczewski.org/mandelbrot.git
Log | Files | Refs

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, &region, 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(&region, 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(&region, iterbuf, abs2buf);
    288 	paint_region(&region, 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 }