+#include "SDL.h"
+#include <math.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <zlib.h>
+
+/* Algorithmic variables */
+int maxiters = 256;
+#define BAILOUT 2.0
+int xres = 600;
+int yres = 600;
+double xmin = -2, ymin = -2, xmax = 2, ymax = 2;
+
+/* Display variables */
+SDL_Surface* screen;
+SDL_Color palette[256],origpalette[256];
+int ppm = 0;
+int sdl = 1;
+int cycling = 0;
+
+int iterate(double x, double y)
+{
+ int i;
+ double cplxr, cplxi;
+ cplxr = y;
+ cplxi = x;
+ i = 0;
+ while ((i < maxiters) && (hypot(cplxr, cplxi) < BAILOUT))
+ {
+ double a=cplxr, b=cplxi, c=cplxr, d=cplxi;
+ i++;
+ cplxr = a*c-b*d+y;
+ cplxi = a*d+b*c+x;
+ }
+ return i;
+}
+
+void set_palette()
+{
+ if (sdl)
+ SDL_SetColors(screen, palette, 0, 256);
+}
+
+void cycle_colors()
+{
+ SDL_Color x;
+ int i;
+ x = palette[0];
+ for (i=0; i<255; i++)
+ palette[i] = palette[i+1];
+ palette[255] = x;
+ set_palette();
+}
+
+Uint32 cycle_colors_cbk(Uint32 interval)
+{
+ cycle_colors();
+ return interval;
+}
+
+void init_palette()
+{
+ int i;
+ for (i=0; i<256; i++)
+ {
+ palette[i].r = sin(((double)i)/256.0*2.0*M_PI*5.0f)*256.0;
+ palette[i].g = sin(((double)(i+64))/256.0*2.0*M_PI*4.0f)*256.0;
+ palette[i].b = sin(((double)(i+128))/256.0*2.0*M_PI*3.0f)*256.0;
+ origpalette[i] = palette[i];
+ }
+}
+
+int videoflags = SDL_SWSURFACE | SDL_RESIZABLE;
+
+void start_sdl()
+{
+ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0)
+ {
+ printf("SDL init failed: %s\n", SDL_GetError());
+ exit(1);
+ }
+ atexit(SDL_Quit);
+
+ screen = SDL_SetVideoMode(xres, yres, 8, videoflags);
+ if (!screen)
+ {
+ printf("SDL video init failed: %s\n", SDL_GetError());
+ exit(1);
+ }
+
+ SDL_WM_SetCaption("Mandelbrot Set", "Mandelbrot");
+}
+
+double pxltofltx(int x)
+{
+ return ((double)x)*((double)(xmax-xmin))/((double)xres)+xmin;
+}
+double pxltoflty(int y)
+{
+ return ((double)y)*((double)(ymax-ymin))/((double)yres)+ymin;
+}
+
+void render()
+{
+ int x,y;
+ Uint8* buffer;
+ if (ppm)
+ printf("P6\n%d %d\n255\n", xres, yres);
+ if (sdl)
+ {
+ cycling = 0;
+ SDL_WM_SetCaption("Mandelbrot Set [working]", "Mandelbrot");
+ SDL_SetTimer(0, NULL);
+ SDL_LockSurface(screen);
+ buffer = (Uint8*)screen->pixels;
+ }
+ for (y=0;y<yres;y++)
+ {
+ for (x=0;x<xres;x++)
+ {
+ int val = iterate(pxltofltx(x), pxltoflty(y));
+ if (ppm)
+ printf("%c%c%c", palette[val].r, palette[val].g, palette[val].b);
+ if (sdl)
+ buffer[x] = val;
+ }
+ if (sdl)
+ buffer += screen->pitch;
+ if (!(y%10))
+ {
+ if (sdl)
+ {
+ for (x=0;x<xres;x++)
+ buffer[x]=0;
+ SDL_UpdateRect(screen,0,0,0,0);
+ } else
+ fprintf(stderr, "%d\n", y);
+ }
+ }
+ if (sdl)
+ {
+ SDL_UpdateRect(screen, 0, 0, 0, 0);
+ SDL_UnlockSurface(screen);
+ SDL_WM_SetCaption("Mandelbrot Set", "Mandelbrot");
+ }
+}
+
+int load_palette(char* name)
+{
+ gzFile file;
+ char c[1024];
+ int idx;
+ file = gzopen(name, "rb");
+ if (file == NULL)
+ {
+ perror("gzopen");
+ return 0;
+ }
+ for (idx = 0; idx < 256; idx++)
+ {
+ char* cidx = c;
+ int accum;
+ int element;
+ gzgets(file, c, 1024);
+
+ if ((c[0] == '\r') || (c[0] == '\n'))
+ {
+ idx--;
+ continue;
+ }
+ if (c[0] == 0x1A /* EOF */)
+ {
+ gzclose(file);
+ return 1;
+ }
+ for (element = 0; element < 3; element++)
+ {
+ accum = 0;
+ while (isblank(*cidx))
+ cidx++;
+ if (!isdigit(*cidx))
+ {
+ fprintf(stderr, "%s: palette file format fault on color %d -- expected digit, got `%c'\n", name, idx, *cidx);
+ gzclose(file);
+ return;
+ }
+ while (isdigit(*cidx))
+ {
+ accum *= 10;
+ accum += *cidx - '0';
+ cidx++;
+ }
+ switch(element)
+ {
+ case 0: origpalette[idx].r = palette[idx].r = accum; break;
+ case 1: origpalette[idx].g = palette[idx].g = accum; break;
+ case 2: origpalette[idx].b = palette[idx].b = accum; break;
+ default: abort();
+ }
+ }
+ }
+ gzclose(file);
+ set_palette();
+ return 1;
+}
+
+void usage(char* me)
+{
+ printf(
+ "Usage: %s [OPTION]...\n"
+ "Renders the Mandelbrot set.\n"
+ "\n"
+ "Mandatory arguments to long options are mandatory for short options too.\n"
+ " -x, --width=PIXELS sets the output width\n"
+ " -y, --height=PIXELS sets the output height\n"
+ " -m, --maxiters=ITERATIONS sets the maximum number of iterations\n"
+ " -s, --sdl enables SDL as an output device (default)\n"
+ " -S, --no-sdl disables SDL as an output device (implies -q)\n"
+ " -p, --ppm enables .ppm to stdout as an output device (implies -q)\n"
+ " -P, --no-ppm disables .ppm as an output device (default)\n"
+ " -q, --quit-immediately exits immediately after rendering\n"
+ " -l, --location=XMIN,XMAX,YMIN,YMAX\n"
+ " sets the starting location\n"
+ " -t, --palette=FILENAME sets the palette\n"
+ " --help shows this help\n"
+ "\n"
+ "Written by Joshua Wise <joshua@joshuawise.com>.\n"
+ , me
+ );
+ exit(2);
+}
+
+struct option longopts[] = {
+ {"x", required_argument, NULL, 'x'},
+ {"width", required_argument, NULL, 'x'},
+ {"y", required_argument, NULL, 'y'},
+ {"height", required_argument, NULL, 'y'},
+ {"maxiters", required_argument, NULL, 'm'},
+ {"sdl", no_argument, NULL, 's'},
+ {"ppm", no_argument, NULL, 'p'},
+ {"no-sdl", no_argument, NULL, 'S'},
+ {"no-ppm", no_argument, NULL, 'P'},
+ {"help", no_argument, NULL, 'h'},
+ {"location", required_argument, NULL, 'l'},
+ {"palette", required_argument, NULL, 't'},
+ {"quit-immediately", no_argument, NULL, 'q'},
+ {0,0,0,0}
+};
+
+int main(int argc, char** argv)
+{
+ SDL_Event event;
+ int startx, starty;
+ int buttondown = 0;
+ int dragged = 0;
+ int arg;
+ int custompalette = 0;
+ int quitimmediately = 0;
+ double yctr, ysz;
+
+ while ((arg = getopt_long(argc, argv, "x:y:m:l:t:sSpPq", longopts, NULL)) != -1)
+ {
+ switch(arg)
+ {
+ case 'x':
+ xres = atoi(optarg);
+ if (!xres)
+ {
+ printf("%s: option `%c' requires an integer argument\n", argv[0], arg);
+ printf("Try `%s --help' for more information.\n", argv[0]);
+ exit(2);
+ }
+ break;
+ case 'y':
+ yres = atoi(optarg);
+ if (!yres)
+ {
+ printf("%s: option `%c' requires an integer argument\n", argv[0], arg);
+ printf("Try `%s --help' for more information.\n", argv[0]);
+ exit(2);
+ }
+ break;
+ case 'm':
+ maxiters = atoi(optarg);
+ if (!maxiters)
+ {
+ printf("%s: option `%c' requires an integer argument\n", argv[0], arg);
+ printf("Try `%s --help' for more information.\n", argv[0]);
+ exit(2);
+ }
+ break;
+ case 's':
+ sdl = 1;
+ break;
+ case 'p':
+ ppm = 1;
+ quitimmediately = 1;
+ break;
+ case 'S':
+ sdl = 0;
+ break;
+ case 'P':
+ ppm = 0;
+ break;
+ case 'h':
+ usage(argv[0]);
+ break;
+ case 'l':
+ {
+ float f1,f2,f3,f4;
+ if (sscanf(optarg, "%f,%f,%f,%f", &f1, &f2, &f3, &f4) != 4)
+ {
+ printf("%s: incorrect syntax for option `%c'\n", argv[0], arg);
+ printf("Try %s --help' for more information.\n", argv[0]);
+ exit(2);
+ }
+ xmin = f1; xmax = f2; ymin = f3; ymax = f4;
+ }
+ break;
+ case 't':
+ custompalette = load_palette(optarg);
+ break;
+ case 'q':
+ quitimmediately = 1;
+ break;
+ case '?':
+ case ':':
+ printf("Try `%s --help' for more information.\n", argv[0]);
+ exit(2);
+ break;
+ default:
+ printf("Oh God I am not good with computers how did this get here?\n");
+ exit(127);
+ }
+ }
+
+ if (sdl)
+ start_sdl();
+ yctr = ymin + (ymax - ymin) / 2.0;
+ // get y in proportion
+ ysz = (xmax-xmin)*((double)yres)/((double)xres);
+ ymax = yctr+ysz/2; ymin = yctr-ysz/2;
+
+ if (!custompalette)
+ init_palette();
+ set_palette();
+ render();
+ if (quitimmediately)
+ exit(0);
+ while ( SDL_WaitEvent(&event) ) {
+ switch (event.type) {
+ case SDL_KEYDOWN:
+ if (event.key.keysym.sym == SDLK_c)
+ {
+ if (!cycling)
+ {
+ SDL_WM_SetCaption("Mandelbrot Set [color cycling]", "Mandelbrot");
+ SDL_SetTimer(50, cycle_colors_cbk);
+ } else {
+ SDL_WM_SetCaption("Mandelbrot Set", "Mandelbrot");
+ SDL_SetTimer(0, NULL);
+ }
+ cycling = !cycling;
+ } else if (event.key.keysym.sym == SDLK_r) {
+ int i;
+ for (i=0;i<256;i++)
+ {
+ palette[i].r = origpalette[i].r;
+ palette[i].g = origpalette[i].g;
+ palette[i].b = origpalette[i].b;
+ }
+ set_palette();
+ } else if (event.key.keysym.sym == SDLK_ESCAPE ||
+ event.key.keysym.sym == SDLK_q) {
+ exit(0);
+ } else if (event.key.keysym.sym == SDLK_p) {
+ ppm = 1;
+ render();
+ ppm = 0;
+ } else if (event.key.keysym.sym == SDLK_RETURN) {
+ render();
+ } else if (event.key.keysym.sym == SDLK_i) {
+ printf("mandelbrot by Joshua Wise\n");
+ printf("2005-06-19, Little Cayman\n");
+ printf("\n");
+ printf("Current settings:\n");
+ printf(" xmin: %f, xmax: %f\n", xmin, xmax);
+ printf(" ymin: %f, ymax: %f\n", ymin, ymax);
+ printf(" maxiters: %d, bailout: %f\n", maxiters, BAILOUT);
+ printf(" xres: %d, yres: %d\n", xres, yres);
+ printf("Recreate with:\n");
+ printf(" %s -x%d -y%d -l%f,%f,%f,%f -m%d\n", argv[0], xres,yres,xmin,xmax,ymin,ymax,maxiters);
+ }
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ if (event.button.button == 1)
+ buttondown = 1;
+ startx = event.button.x;
+ starty = event.button.y;
+ break;
+ case SDL_MOUSEMOTION:
+ if (buttondown)
+ dragged = 1;
+ break;
+ case SDL_MOUSEBUTTONUP:
+ if (event.button.button == 1)
+ buttondown = 0;
+ if (!dragged)
+ {
+ if (event.button.button == 1 || event.button.button == 3)
+ {
+ double xsz = xmax-xmin, ysz = ymax-ymin;
+ double x,y;
+ double scalar;
+ if (event.button.button == 1)
+ scalar = 1.0/2.0;
+ else
+ scalar = 2.0/1.0;
+ xsz *= scalar;
+ ysz *= scalar;
+ x = pxltofltx(event.button.x);
+ y = pxltoflty(event.button.y);
+ xmax = x + xsz / 2;
+ xmin = x - xsz / 2;
+ ymax = y + ysz / 2;
+ ymin = y - ysz / 2;
+ render();
+ } else if (event.button.button == 2) {
+ xmax = ymax = 2;
+ xmin = ymin = -2;
+ render();
+ }
+ dragged = 0;
+ break;
+ } else {
+ double newmaxx, newminx, newmaxy, newminy;
+ if (event.button.x > startx)
+ {
+ newmaxx = pxltofltx(event.button.x);
+ newminx = pxltofltx(startx);
+ } else {
+ newminx = pxltofltx(event.button.x);
+ newmaxx = pxltofltx(startx);
+ }
+ if (event.button.y > starty)
+ {
+ newmaxy = pxltoflty(event.button.y);
+ newminy = pxltoflty(starty);
+ } else {
+ newminy = pxltoflty(event.button.y);
+ newmaxy = pxltoflty(starty);
+ }
+ yctr = newminy + (newmaxy - newminy) / 2.0;
+ // get y in proportion
+ ysz = (newmaxx-newminx)*((double)yres)/((double)xres);
+ ymax = yctr+ysz/2; ymin = yctr-ysz/2;
+ xmax = newmaxx; xmin = newminx;
+ render();
+ dragged = 0;
+ break;
+ }
+ case SDL_VIDEORESIZE:
+ {
+ screen = SDL_SetVideoMode(event.resize.w, event.resize.h,
+ screen->format->BitsPerPixel,
+ videoflags);
+ if (!screen)
+ {
+ fprintf(stderr, "Couldn't resize screen\n");
+ exit(1);
+ }
+ SDL_SetColors(screen, palette, 0, 256);
+ xres = event.resize.w;
+ yres = event.resize.h;
+ yctr = ymin + (ymax - ymin) / 2.0;
+ // get y in proportion
+ ysz = (xmax-xmin)*((double)yres)/((double)xres);
+ ymax = yctr+ysz/2; ymin = yctr-ysz/2;
+ render();
+ break;
+ }
+ case SDL_QUIT:
+ return;
+ }
+ }
+ return 1;
+}