|
|
|
@ -1,8 +1,11 @@
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <complex.h>
|
|
|
|
|
#include <getopt.h>
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <png.h>
|
|
|
|
|
#include <pthread.h>
|
|
|
|
|
|
|
|
|
|
typedef uint32_t pixel_t;
|
|
|
|
|
|
|
|
|
@ -17,8 +20,7 @@ const int max_it = 170;
|
|
|
|
|
|
|
|
|
|
/* Compute the out-coloring based on the iteration counter. */
|
|
|
|
|
pixel_t outcolor(int it) {
|
|
|
|
|
/* return 0x00010001 * ((it * 0xff) / max_it); */
|
|
|
|
|
return 0x00000001 * ((it * 0xff) / max_it);
|
|
|
|
|
return 0x00010001 * ((it * 0xff) / max_it);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* "Compute" the in-coloring. */
|
|
|
|
@ -26,12 +28,10 @@ pixel_t incolor() {
|
|
|
|
|
return 0x00000000; /* black */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Computes the visual representation of a mandelbrot set for a given line. */
|
|
|
|
|
void compmandelbrot_line(bitmap_t* mbrot, size_t y) {
|
|
|
|
|
|
|
|
|
|
void drawmandelbrot(bitmap_t * mbrot) {
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < mbrot->width * mbrot->height; i++) {
|
|
|
|
|
size_t y = i / mbrot->width;
|
|
|
|
|
size_t x = i % mbrot->width;
|
|
|
|
|
for (size_t x = 0; x < mbrot->width; x++) {
|
|
|
|
|
|
|
|
|
|
float complex c = ((3.0f * x / mbrot->width) - 2.0f)
|
|
|
|
|
+ I * ((2.0f * y / mbrot->height) - 1.0f);
|
|
|
|
@ -57,14 +57,42 @@ void drawmandelbrot(bitmap_t * mbrot) {
|
|
|
|
|
color = incolor();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t i = y * mbrot->width + x;
|
|
|
|
|
mbrot->pixels[i] = color;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Next line to compute. */
|
|
|
|
|
size_t next_y = 0;
|
|
|
|
|
|
|
|
|
|
/* Mutex for next_y. */
|
|
|
|
|
pthread_mutex_t next_y_mutex;
|
|
|
|
|
|
|
|
|
|
/* Thread to compute mandelbrot. */
|
|
|
|
|
void *compmandelbrot(void *arg) {
|
|
|
|
|
bitmap_t* mbrot = (bitmap_t*) arg;
|
|
|
|
|
|
|
|
|
|
size_t y = 0;
|
|
|
|
|
while (y < mbrot->height) {
|
|
|
|
|
int ret;
|
|
|
|
|
ret = pthread_mutex_lock(&next_y_mutex);
|
|
|
|
|
assert(ret == 0);
|
|
|
|
|
|
|
|
|
|
y = next_y++;
|
|
|
|
|
|
|
|
|
|
ret = pthread_mutex_unlock(&next_y_mutex);
|
|
|
|
|
assert(ret == 0);
|
|
|
|
|
|
|
|
|
|
if (y < mbrot->height) {
|
|
|
|
|
compmandelbrot_line(mbrot, y);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pthread_exit(NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Write "bitmap" to a PNG file specified by "path"; returns 0 on
|
|
|
|
|
success, non-zero on error. */
|
|
|
|
|
|
|
|
|
|
static int save_png_to_file(bitmap_t * bitmap, const char *path) {
|
|
|
|
|
FILE *fp;
|
|
|
|
|
|
|
|
|
@ -78,8 +106,6 @@ static int save_png_to_file(bitmap_t * bitmap, const char *path) {
|
|
|
|
|
* is set to a value which means 'success'. */
|
|
|
|
|
int status = -1;
|
|
|
|
|
|
|
|
|
|
/* FIXME The following number is set by trial and error only. I cannot see
|
|
|
|
|
* where it it is documented in the libpng manual. */
|
|
|
|
|
int pixel_size = 3;
|
|
|
|
|
int depth = 8;
|
|
|
|
|
|
|
|
|
@ -121,7 +147,6 @@ static int save_png_to_file(bitmap_t * bitmap, const char *path) {
|
|
|
|
|
png_malloc(png_ptr, sizeof(uint8_t) * bitmap->width * pixel_size);
|
|
|
|
|
row_pointers[y] = row;
|
|
|
|
|
for (x = 0; x < bitmap->width; ++x) {
|
|
|
|
|
/* FIXME */
|
|
|
|
|
pixel_t *pixel = bitmap->pixels + (y*bitmap->width + x);
|
|
|
|
|
*row++ = (*pixel & 0x00ff0000) >> 16;
|
|
|
|
|
*row++ = (*pixel & 0x0000ff00) >> 8;
|
|
|
|
@ -144,7 +169,6 @@ static int save_png_to_file(bitmap_t * bitmap, const char *path) {
|
|
|
|
|
png_free(png_ptr, row_pointers);
|
|
|
|
|
|
|
|
|
|
png_failure:
|
|
|
|
|
/* FIXME info struct destroy? */
|
|
|
|
|
png_create_info_struct_failed:
|
|
|
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
|
|
|
png_create_write_struct_failed:
|
|
|
|
@ -154,21 +178,54 @@ fopen_failed:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
|
|
|
|
|
|
|
|
|
/* Get command line options. */
|
|
|
|
|
int num_threads = 1;
|
|
|
|
|
char* options = "j:";
|
|
|
|
|
if (getopt(argc, argv, options) == 'j') {
|
|
|
|
|
num_threads = (int) strtol(optarg, NULL, 10);
|
|
|
|
|
assert(num_threads != 0);
|
|
|
|
|
} else {
|
|
|
|
|
printf("Usage: multibrot -j <number_of_threads>\n");
|
|
|
|
|
exit(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set up mandelbrot bitmap. */
|
|
|
|
|
bitmap_t mbrot;
|
|
|
|
|
mbrot.width = 640;
|
|
|
|
|
mbrot.height = 480; /* FIXME calculate */
|
|
|
|
|
mbrot.width = 1600;
|
|
|
|
|
mbrot.height = 1200;
|
|
|
|
|
mbrot.pixels = calloc(sizeof(pixel_t), mbrot.width * mbrot.height);
|
|
|
|
|
assert(mbrot.pixels != NULL);
|
|
|
|
|
|
|
|
|
|
/* Start computing threads. */
|
|
|
|
|
int ret;
|
|
|
|
|
pthread_t* threads;
|
|
|
|
|
threads = calloc(sizeof(pthread_t), num_threads);
|
|
|
|
|
|
|
|
|
|
/* Do the mandelbrot. */
|
|
|
|
|
drawmandelbrot(&mbrot);
|
|
|
|
|
ret = pthread_mutex_init(&next_y_mutex, NULL);
|
|
|
|
|
assert(ret == 0);
|
|
|
|
|
|
|
|
|
|
for (int t = 0; t < num_threads; t++) {
|
|
|
|
|
ret = pthread_create(&threads[t], NULL, compmandelbrot, (void *) &mbrot);
|
|
|
|
|
assert(ret == 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Wait for the threads to finish. */
|
|
|
|
|
for (int t = 0; t < num_threads; t++) {
|
|
|
|
|
ret = pthread_join(threads[t], NULL);
|
|
|
|
|
assert(ret == 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Save PNG */
|
|
|
|
|
char *file = "multibrot.png";
|
|
|
|
|
if (save_png_to_file(&mbrot, file) != 0) {
|
|
|
|
|
fprintf(stderr, "Could not save PNG to %s.\n", file);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
ret = save_png_to_file(&mbrot, file);
|
|
|
|
|
assert(ret == 0);
|
|
|
|
|
|
|
|
|
|
/* Quit. */
|
|
|
|
|
pthread_exit(NULL);
|
|
|
|
|
free(threads);
|
|
|
|
|
free(mbrot.pixels);
|
|
|
|
|
ret = pthread_mutex_destroy(&next_y_mutex);
|
|
|
|
|
assert(ret == 0);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|