├── TODO ├── io.h ├── main.h ├── Makefile ├── colour.h ├── mandelbrot.h ├── colour.c ├── cli.c ├── mandelbrot.c ├── io.c ├── main.c ├── gui.c └── main.glade /TODO: -------------------------------------------------------------------------------- 1 | * Detect the number of CPUs dynamically 2 | * Add saving to files 3 | * Add more colouring algorithms 4 | -------------------------------------------------------------------------------- /io.h: -------------------------------------------------------------------------------- 1 | #ifndef MANDELBROT_IO_H 2 | #define MANDELBROT_IO_H 3 | 4 | int write_png_output(pixel *buf, long xpx, long ypx, const char *filename); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | #define MAXITERS 10240 2 | #define COLOURS 256 3 | #define NUMTHREADS 2 4 | 5 | #include "mandelbrot.h" 6 | 7 | void *thread_mandel(void *closure); 8 | void run_threads(pixel *mandelset, double xmin, double xmax, double ymin, double ymax, double xpx, double ypx); 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-g 2 | GTK_CFLAGS= 3 | 4 | cli: 5 | gcc $(CFLAGS) -o mandel colour.c io.c main.c cli.c mandelbrot.c -lpng -lpthread 6 | gui: 7 | gcc $(CFLAGS) -export-dynamic `pkg-config --libs --cflags gtk+-2.0 gmodule-export-2.0` -o gui_mandel colour.c io.c main.c gui.c mandelbrot.c -lpng -lpthread 8 | -------------------------------------------------------------------------------- /colour.h: -------------------------------------------------------------------------------- 1 | #ifndef MANDELBROT_COLOUR_H 2 | #define MANDELBROT_COLOUR_H 3 | #include "mandelbrot.h" 4 | 5 | void colour_basic(pixel *dest, long x, long y, long xpx, long ypx, long m, 6 | double min_r, double min_i, double max_r, double max_i); 7 | void colour_bands(pixel *dest, long x, long y, long xpx, long ypx, long m, 8 | double min_r, double min_i, double max_r, double max_i); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /mandelbrot.h: -------------------------------------------------------------------------------- 1 | #ifndef MANDELBROT_H 2 | #define MANDELBROT_H 3 | 4 | #define MAXITERS 10240 5 | #define COLOURS 256 6 | 7 | typedef struct _complex { 8 | double real; 9 | double imaginary; 10 | } complex; 11 | 12 | typedef struct _pixel { 13 | unsigned char r, g, b; 14 | } pixel; 15 | 16 | typedef void (*colour_function)(pixel *, long, long, long, long, long, long, long, long, long); 17 | 18 | typedef struct _thread_closure { 19 | pixel *dest; 20 | double xmin, xmax, ymin, ymax; 21 | long xpx, ypx; 22 | long step; /* How many lines to skip */ 23 | long offset; /* The offset to start from */ 24 | colour_function colour_func; 25 | } thread_closure; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /colour.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mandelbrot.h" 3 | 4 | void colour_basic(pixel *dest, 5 | long x, long y, long xpx, long ypx, long m, 6 | double min_r, double min_i, double max_r, double max_i) { 7 | if(m == MAXITERS) { 8 | m = 0; 9 | } else if(m > 255) { 10 | m = 255; 11 | } 12 | 13 | dest[y * xpx + x].r = 0; 14 | dest[y * xpx + x].g = 0; 15 | dest[y * xpx + x].b = m; 16 | } 17 | 18 | void colour_bands(pixel *dest, 19 | long x, long y, long xpx, long ypx, long m, 20 | double min_r, double min_i, double max_r, double max_i) { 21 | 22 | printf("min_r: %f, max_r: %f, min_i: %f, max_i: %f\n", min_r, max_r, min_i, max_i); 23 | 24 | if(m == MAXITERS) { 25 | m = 0; 26 | } else if(m > 255) { 27 | m = 255; 28 | } 29 | 30 | dest[y * xpx + x].r = 0; 31 | dest[y * xpx + x].g = 0; 32 | dest[y * xpx + x].b = m; 33 | 34 | } 35 | 36 | -------------------------------------------------------------------------------- /cli.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "main.h" 4 | #include "mandelbrot.h" 5 | 6 | int main(int argc, char *argv[]) { 7 | 8 | double xmin, xmax, ymin, ymax; 9 | long xpx, ypx, arraysize; 10 | char *tail; 11 | 12 | /* default: -2.0, 2.0, -2.0, 2.0, 1024, 1024 */ 13 | if(argc < 6) { 14 | xmin = -2.0; 15 | xmax = 2.0; 16 | ymin = -2.0; 17 | ymax = 2.0; 18 | xpx = 2048; 19 | ypx = 2048; 20 | } else { 21 | xmin = strtod( argv[1], &tail ); 22 | xmax = strtod( argv[2], &tail ); 23 | ymin = strtod( argv[3], &tail ); 24 | ymax = strtod( argv[4], &tail ); 25 | xpx = strtol( argv[5], &tail, 0 ); 26 | ypx = strtol( argv[6], &tail, 0 ); 27 | } 28 | 29 | printf( "Xmin: %f, Xmax: %f, Ymin: %f, Ymax: %f, Xpx: %ld, Ypx: %ld\n", xmin, xmax, ymin, ymax, xpx, ypx ); 30 | 31 | arraysize = xpx * ypx; 32 | pixel *mandelset = malloc(sizeof(pixel) * arraysize); 33 | printf("Allocated buffer of size %ld\n", sizeof(pixel) * arraysize); 34 | 35 | run_threads(mandelset, xmin, xmax, ymin, ymax, xpx, ypx); 36 | 37 | write_png_output(mandelset, xpx, ypx, NULL); 38 | } 39 | 40 | -------------------------------------------------------------------------------- /mandelbrot.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "mandelbrot.h" 7 | #include "colour.h" 8 | 9 | void mandel_basic(pixel *dest, 10 | double xmin, double xmax, double ymin, double ymax, 11 | long xpx, long ypx, long step, long offset, 12 | colour_function colour_func) { 13 | 14 | long ni, ix, iy, m; 15 | double min_r = 100000, min_i = 100000, max_r = -100000, max_i = -100000; 16 | complex c; 17 | complex original; 18 | double dx, dy, tmp; 19 | 20 | dx = (xmax - xmin) / (double) xpx; 21 | dy = (ymax - ymin) / (double) ypx; 22 | 23 | for ( iy = 0; iy < ypx; iy++ ) { 24 | for ( ix = offset; ix < xpx; ix += step ) { 25 | c.real = xmin + ix * dx; 26 | c.imaginary = ymax - iy * dy; 27 | original.real = c.real; 28 | original.imaginary = c.imaginary; 29 | ni = 0; 30 | while ( ni < MAXITERS && (c.real * c.real * c.imaginary * c.imaginary < 4.0 ) ) { 31 | tmp = c.real * c.real - c.imaginary * c.imaginary + original.real; 32 | c.imaginary = c.imaginary * c.real * 2 + original.imaginary; 33 | c.real = tmp; 34 | 35 | if(c.real < min_r) min_r = c.real; 36 | if(c.imaginary < min_i) min_i = c.imaginary; 37 | if(c.real > max_r) max_r = c.real; 38 | if(c.imaginary > max_i) max_i = c.imaginary; 39 | 40 | ni++; 41 | } 42 | m = ni - log( log( sqrt( c.real * c.real * c.imaginary * c.imaginary ) ) ) / log( 2 ); 43 | colour_func(dest, ix, iy, xpx, ypx, m, min_r, min_i, max_r, max_i); 44 | } 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /io.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "mandelbrot.h" 6 | 7 | int write_png_output(pixel *buf, long xpx, long ypx, const char *filename) { 8 | int y; 9 | pixel *buf_row = buf; 10 | png_structp png_ptr; 11 | png_infop info_ptr; 12 | 13 | png_byte **row_pointers = malloc(ypx * sizeof(png_byte *)); 14 | for(y = 0; y < ypx; y++) { 15 | row_pointers[y] = (png_byte *)buf_row; 16 | buf_row += xpx; 17 | } 18 | 19 | if(filename == NULL) { 20 | filename = "mb.png"; 21 | } 22 | 23 | FILE *fp = fopen(filename, "wb"); 24 | 25 | if (fp == NULL) return -1; 26 | 27 | /* Initialize the write struct. */ 28 | png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 29 | if (png_ptr == NULL) { 30 | fclose(fp); 31 | return -1; 32 | } 33 | 34 | 35 | /* Initialize the info struct. */ 36 | info_ptr = png_create_info_struct(png_ptr); 37 | if (info_ptr == NULL) { 38 | png_destroy_write_struct(&png_ptr, NULL); 39 | fclose(fp); 40 | return -1; 41 | } 42 | 43 | /* Set up error handling. */ 44 | if (setjmp(png_jmpbuf(png_ptr))) { 45 | png_destroy_write_struct(&png_ptr, &info_ptr); 46 | fclose(fp); 47 | return -1; 48 | } 49 | 50 | png_init_io(png_ptr, fp); 51 | 52 | /* Set image attributes. */ 53 | png_set_IHDR(png_ptr, 54 | info_ptr, 55 | xpx, 56 | ypx, 57 | 8, 58 | PNG_COLOR_TYPE_RGB, 59 | PNG_INTERLACE_NONE, 60 | PNG_COMPRESSION_TYPE_DEFAULT, 61 | PNG_FILTER_TYPE_DEFAULT); 62 | 63 | png_write_info(png_ptr, info_ptr); 64 | 65 | png_write_image(png_ptr, row_pointers); 66 | png_write_end(png_ptr, info_ptr); 67 | fclose(fp); 68 | free(row_pointers); 69 | } 70 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "main.h" 6 | #include "mandelbrot.h" 7 | #include "colour.h" 8 | #include "io.h" 9 | 10 | 11 | void *thread_mandel(void *closure) { 12 | thread_closure *tc = (thread_closure *) closure; 13 | 14 | printf("Entering continuous_mandel in thread %ld\n", tc->offset); 15 | mandel_basic(tc->dest, 16 | tc->xmin, tc->xmax, tc->ymin, tc->ymax, 17 | tc->xpx, tc->ypx, tc->step, tc->offset, tc->colour_func ); 18 | printf("Exiting thread %ld\n", tc->offset); 19 | pthread_exit(closure); 20 | } 21 | 22 | void run_threads(pixel *mandelset, double xmin, double xmax, double ymin, double ymax, double xpx, double ypx) { 23 | 24 | thread_closure thread_info[NUMTHREADS]; 25 | pthread_t threads[NUMTHREADS]; 26 | pthread_attr_t attr; 27 | int t; 28 | long rc; 29 | void *status; 30 | 31 | pthread_attr_init(&attr); 32 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 33 | 34 | 35 | for(t=0; t < NUMTHREADS; t++) { 36 | thread_info[t].dest = mandelset; 37 | thread_info[t].xmin = xmin; 38 | thread_info[t].xmax = xmax; 39 | thread_info[t].ymin = ymin; 40 | thread_info[t].ymax = ymax; 41 | thread_info[t].xpx = xpx; 42 | thread_info[t].ypx = ypx; 43 | thread_info[t].offset = t; 44 | thread_info[t].step = NUMTHREADS; 45 | thread_info[t].colour_func = (colour_function)&colour_basic; 46 | 47 | rc = pthread_create(&threads[t], NULL, thread_mandel, (void *) &thread_info[t]); 48 | if(rc) { 49 | printf("Creating thread failed with error %ld\n", rc); 50 | } 51 | } 52 | 53 | pthread_attr_destroy(&attr); 54 | for(t = 0; t < NUMTHREADS; t++) { 55 | rc = pthread_join(threads[t], &status); 56 | if(rc) { 57 | printf("ERROR; return code from pthread_join() is %ld\n", rc); 58 | exit(-1); 59 | } 60 | printf("Main: completed join with thread %d\n", t, (long)status); 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /gui.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "main.h" 6 | #include "mandelbrot.h" 7 | 8 | pixel *buffer = NULL; 9 | double xmin = -2.0, xmax = 2.0; 10 | double ymin = -2.0, ymax = 2.0; 11 | double cached_xmin, cached_xmax, cached_ymin, cached_ymax; 12 | double dx, dy; 13 | 14 | long xpx = 512, ypx = 512; 15 | 16 | GtkStatusbar *status_bar; 17 | GtkImage *drawing_area; 18 | GtkSpinButton *x_spin, *y_spin, *width_spin; 19 | 20 | void close_app(GtkObject *object, gpointer user_data) { 21 | gtk_main_quit(); 22 | } 23 | 24 | void trigger_render(GtkObject *object, gpointer user_data) { 25 | double width; 26 | 27 | printf("Triggered rendering.\n"); 28 | 29 | run_threads(buffer, xmin, xmax, ymin, ymax, xpx, ypx); 30 | 31 | cached_xmin = xmin; 32 | cached_xmax = xmax; 33 | cached_ymin = ymin; 34 | cached_ymax = ymax; 35 | dx = (xmax - xmin) / (double) xpx; 36 | dy = (ymax - ymin) / (double) ypx; 37 | width = xmax - xmin; 38 | 39 | gtk_widget_queue_draw(GTK_WIDGET(drawing_area)); 40 | gtk_spin_button_set_value(x_spin, xmin + (width / 2)); 41 | gtk_spin_button_set_value(y_spin, ymin + (width / 2)); 42 | gtk_spin_button_set_value(width_spin, width); 43 | printf("Rendered: centre (%.3f, %.3f), width %.3f, %ldx%ldpx\n", 44 | xmin + (width / 2), 45 | ymin + (width / 2), 46 | width, 47 | xpx, ypx); 48 | } 49 | 50 | static void update_status(long xpos, long ypos) { 51 | char description[25]; 52 | double x, y; 53 | 54 | x = cached_xmin + (xpos * dx); 55 | y = cached_ymax - (ypos * dy); 56 | 57 | snprintf(description, 25, "(%.3f, %.3f)", x, y); 58 | gtk_statusbar_push(status_bar, gtk_statusbar_get_context_id(status_bar, "information"), description); 59 | } 60 | 61 | static void recentre(long xpos, long ypos) { 62 | double x, y; 63 | double xsize = xmax - xmin; 64 | double ysize = ymax - ymin; 65 | char description[30]; 66 | 67 | printf("dx: %f, dy: %f\n", dx, dy); 68 | printf("Xpos: %ld; Ypos: %ld\n", xpos, ypos); 69 | x = cached_xmin + ((double) xpos * dx); 70 | y = cached_ymax - ((double) ypos * dy); 71 | printf("Clicked on (%.3f, %.3f)\n", x, y); 72 | 73 | xmin = x - (xsize / 2); 74 | xmax = x + (xsize / 2); 75 | ymin = y - (ysize / 2); 76 | ymax = y + (ysize / 2); 77 | 78 | snprintf(description, 35, "Recentred to (%.3f, %.3f)", x, y); 79 | gtk_statusbar_push(status_bar, gtk_statusbar_get_context_id(status_bar, "information"), description); 80 | } 81 | 82 | gboolean click_event(GtkWidget *widget, GdkEvent *event, gpointer data) { 83 | long xpos, ypos; 84 | 85 | if (event->motion.is_hint) { 86 | int x, y; 87 | gdk_window_get_pointer (widget->window, &x, &y, NULL); 88 | event->motion.x = x; 89 | event->motion.y = y; 90 | } 91 | 92 | xpos = event->motion.x; 93 | ypos = event->motion.y; 94 | recentre(xpos, ypos); 95 | return TRUE; 96 | 97 | } 98 | 99 | gboolean x_spin_changed(GtkWidget *widget, GdkEvent *event, gpointer data) { 100 | double new_x, width; 101 | 102 | new_x = gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget)); 103 | width = xmax - xmin; 104 | 105 | xmin = new_x - (width / 2); 106 | xmax = new_x + (width / 2); 107 | 108 | return TRUE; 109 | } 110 | 111 | gboolean y_spin_changed(GtkWidget *widget, GdkEvent *event, gpointer data) { 112 | double new_y, height; 113 | 114 | new_y = gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget)); 115 | height = ymax - ymin; 116 | 117 | ymin = new_y - (height / 2); 118 | ymax = new_y + (height / 2); 119 | 120 | return TRUE; 121 | } 122 | 123 | gboolean width_spin_changed(GtkWidget *widget, GdkEvent *event, gpointer data) { 124 | double x, y, new_width; 125 | 126 | new_width = gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget)); 127 | x = xmin + (xmax - xmin) / 2; 128 | y = ymin + (ymax - ymin) / 2; 129 | 130 | xmin = x - (new_width / 2); 131 | xmax = x + (new_width / 2); 132 | ymin = y - (new_width / 2); 133 | ymax = y + (new_width / 2); 134 | 135 | return TRUE; 136 | } 137 | 138 | gboolean motion_event(GtkWidget *widget, GdkEvent *event, gpointer data) { 139 | long xpos, ypos; 140 | 141 | if (event->motion.is_hint) { 142 | int x, y; 143 | gdk_window_get_pointer (widget->window, &x, &y, NULL); 144 | event->motion.x = x; 145 | event->motion.y = y; 146 | } 147 | 148 | xpos = event->motion.x; 149 | ypos = event->motion.y; 150 | update_status(xpos, ypos); 151 | return TRUE; 152 | } 153 | 154 | int main(int argc, char *argv[]) { 155 | 156 | GtkBuilder* gtk_builder; 157 | GtkWidget *main_window, *box; 158 | GdkPixbuf *pixbuf; 159 | double width; 160 | 161 | dx = (xmax - xmin) / (double) xpx; 162 | dy = (ymax - ymin) / (double) ypx; 163 | cached_xmin = xmin; 164 | cached_xmax = xmax; 165 | cached_ymin = ymin; 166 | cached_ymax = ymax; 167 | 168 | gtk_init(&argc, &argv); 169 | 170 | gtk_builder = gtk_builder_new(); 171 | gtk_builder_add_from_file(gtk_builder, "main.glade", NULL); 172 | gtk_builder_connect_signals(gtk_builder, NULL); 173 | main_window = GTK_WIDGET(gtk_builder_get_object(gtk_builder, "main_window")); 174 | 175 | if(buffer == NULL) { 176 | buffer = malloc(xpx * ypx * sizeof(pixel)); 177 | } 178 | 179 | run_threads(buffer, xmin, xmax, ymin, ymax, xpx, ypx); 180 | 181 | status_bar = GTK_STATUSBAR(gtk_builder_get_object(gtk_builder, "status_bar")); 182 | gtk_statusbar_push(status_bar, gtk_statusbar_get_context_id(status_bar, "information"), ""); 183 | 184 | box = GTK_WIDGET(gtk_builder_get_object(gtk_builder, "event_box")); 185 | 186 | width = xmax - xmin; 187 | printf("Width: %f\n", width); 188 | width_spin = GTK_SPIN_BUTTON(gtk_builder_get_object(gtk_builder, "width_spin")); 189 | gtk_spin_button_set_value(width_spin, width); 190 | 191 | x_spin = GTK_SPIN_BUTTON(gtk_builder_get_object(gtk_builder, "centre_x_spin")); 192 | gtk_spin_button_set_value(x_spin, xmin + width / 2); 193 | 194 | y_spin = GTK_SPIN_BUTTON(gtk_builder_get_object(gtk_builder, "centre_y_spin")); 195 | gtk_spin_button_set_value(y_spin, ymin + width / 2); 196 | 197 | 198 | pixbuf = gdk_pixbuf_new_from_data( 199 | (const guchar *)buffer, GDK_COLORSPACE_RGB, 200 | 0, 8, xpx, ypx, xpx * sizeof(pixel), NULL, NULL); 201 | 202 | drawing_area = (GtkImage *)gtk_image_new_from_pixbuf(pixbuf); 203 | gtk_container_add(GTK_CONTAINER(box), GTK_WIDGET(drawing_area)); 204 | 205 | gtk_widget_show_all(main_window); 206 | 207 | gtk_main(); 208 | return 0; 209 | } 210 | 211 | -------------------------------------------------------------------------------- /main.glade: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | 520 8 | 600 9 | 10 | 11 | 12 | 13 | True 14 | vertical 15 | 16 | 17 | True 18 | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 0 27 | 28 | 29 | 30 | 31 | True 32 | 33 | 34 | True 35 | 36 | 37 | True 38 | X 39 | 40 | 41 | 0 42 | 43 | 44 | 45 | 46 | True 47 | True 48 | 49 | x_adjustment 50 | 3 51 | True 52 | 53 | 54 | 55 | 1 56 | 57 | 58 | 59 | 60 | True 61 | Y 62 | 63 | 64 | 2 65 | 66 | 67 | 68 | 69 | True 70 | True 71 | 72 | y_adjustment 73 | 3 74 | True 75 | 76 | 77 | 78 | 3 79 | 80 | 81 | 82 | 83 | 0 84 | 85 | 86 | 87 | 88 | True 89 | 90 | 91 | True 92 | Width 93 | 94 | 95 | 0 96 | 97 | 98 | 99 | 100 | True 101 | True 102 | 103 | width_adjustment 104 | 3 105 | True 106 | 107 | 108 | 109 | 1 110 | 111 | 112 | 113 | 114 | 1 115 | 116 | 117 | 118 | 119 | Render 120 | True 121 | True 122 | True 123 | True 124 | True 125 | 126 | 127 | 128 | False 129 | False 130 | 2 131 | 132 | 133 | 134 | 135 | 1 136 | 137 | 138 | 139 | 140 | True 141 | 2 142 | False 143 | 144 | 145 | False 146 | 3 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | -100 156 | 100 157 | 0.01 158 | 10 159 | 10 160 | 161 | 162 | -100 163 | 100 164 | 0.01 165 | 10 166 | 10 167 | 168 | 169 | 100 170 | 0.01 171 | 10 172 | 10 173 | 174 | 175 | True 176 | gtk-save-as 177 | 178 | 179 | --------------------------------------------------------------------------------