/*
  Gas Giant Generator.  Copyright 1999, Dave Turner.
  Released under the GNU GPL.  For details see www.gnu.org/copyleft/gpl.html.

  You may need to compile with -lm
  Last updates (minor) September 18, 2001: Direct jpeg output, code clean-up

  TTD:
  Splotches.

 */
#include <stdio.h>
#include <stdint.h>
#include <math.h>
#include <stdlib.h>
#include <sys/timeb.h>

#define PI 3.14159265358979

#define YSIZE 256
#define XSIZE 512
/*
#define YSIZE 512
#define XSIZE 1024
*/
#define HALF_YSIZE (YSIZE / 2)
#define debug 0

/*tuning:*/
#define x_bubble_pow 2.2
#define y_bubble_pow 1.6


typedef struct {

  int32_t bmp_data [YSIZE][XSIZE];

} BITMAP_D ;

/* The next 5 functions are from the Allegro Game Programming Library: */

uint32_t makecol32 (int r, int g, int b) {
   return ((r << 0) | (g << 8) | (b << 16));
}
int getr32 (uint32_t c) {
  return c & 255;
}

int getg32 (uint32_t c) {
  return (c >> 8) & 255;
}

int getb32 (uint32_t c) {
  return (c >> 16) & 255;
}

void hsv_to_rgb (float h, float s, float v, int *r, int *g, int *b) {
  float f, x, y, z;
  int i;
  
  v *= 255.0;
  
  if (s == 0.0) {
    *r = *g = *b = (int)v;
  } else {

    while (h < 0) {
      h += 360;
    }

    h = fmod(h, 360) / 60.0;
    i = (int)h;
    f = h - i;
    x = v * (1.0 - s);
    y = v * (1.0 - (s * f));
    z = v * (1.0 - (s * (1.0 - f)));
    
    switch (i) {

    case 0: *r = v; *g = z; *b = x; break;
    case 1: *r = y; *g = v; *b = x; break;
    case 2: *r = x; *g = v; *b = z; break;
    case 3: *r = x; *g = y; *b = v; break;
    case 4: *r = z; *g = x; *b = v; break;
    case 5: *r = v; *g = x; *b = y; break;

    }
  }
}

/*
This function is a perfect example of the wrong way to do things.
The header was taken from a bitmap file made in GIMP.  When cjpeg refused 
to read the output file (unexpected end of input file), I had to make it 
output a bunch of zeros at the end.  This is horrible wrong: I should 
have tried to figure out the problem. 
*/

int save_bmp (char *filename, BITMAP_D *bmp, int xsize, int ysize) {

  int x, y, i;

  FILE *fp;
  
  unsigned char bmp_header [54] = { 0x42, 0x4d, 0x36, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 
0x0, 0x36, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x80, 0x0,
0x0, 0x0, 0x1, 0x0, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0, 0x0, 0x0, 0x1, 
0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};

  /* insert sizes into headers */
  bmp_header [18] = xsize & 255;
  bmp_header [19] = (xsize >> 8) & 255;
  bmp_header [22] = ysize & 255;
  bmp_header [23] = (ysize >> 8) & 255;

  fp = fopen (filename, "wb");

  if (fp == NULL) return 1;

  for (i = 0; i < 54; i ++) {
    fputc (bmp_header [i], fp);
  }


  for (y = 0; y < YSIZE; y ++) {
    for (x = 0; x < YSIZE /*not a typo*/; x ++) {

      fputc (bmp->bmp_data [y][x] & 255, fp);
      fputc ((bmp->bmp_data [y][x] >> 8) & 255, fp);
      fputc ((bmp->bmp_data [y][x] >> 16)& 255, fp);

    }
  }

  /* make cjpeg happy */
  for (i = 0; i < xsize * ysize * 0.02; i ++) {
    fputc (0, fp);
  }

  fclose (fp);

  return 0;

}

/*************************************************************************/
/**                                                                     **/
/**     getcgivars.c-- routine to read CGI input variables into an      **/
/**         array of strings.                                           **/
/**                                                                     **/
/**     The x2c() and unescape_url() routines were lifted directly      **/
/**     from NCSA's sample program util.c, packaged with their HTTPD.   **/
/**                                                                     **/
/*************************************************************************/


/** Convert a two-char hex string into the char it represents **/
char x2c(char *what) {
    char digit;

   digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
   digit *= 16;
   digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));

   if (!isalnum (digit) && !ispunct (digit) && digit != 10) digit = ' ';

   return(digit);
}

/** Reduce any %xx escape sequences to the characters they represent **/
void unescape_url(char *url) {
    int i,j;

    for(i=0,j=0; url[j]; ++i,++j) {
        if((url[i] = url[j]) == '%') {
            url[i] = x2c(&url[j+1]) ;
            j+= 2 ;
        }
    }
    url[i] = '\0' ;
}


/** Read the CGI input and place all name/val pairs into list.        **/
/** Returns list containing name1, value1, name2, value2, ... , NULL  **/
char **getcgivars() {
    int i ;
    char *request_method ;
    int content_length;
    char *cgiinput ;
    char **cgivars ;
    char **pairlist ;
    int paircount ;
    char *nvpair ;
    char *eqpos ;

    /** Depending on the request method, read all CGI input into cgiinput **/
    /** (really should produce HTML error messages, instead of exit()ing) **/
    request_method = getenv("REQUEST_METHOD");
    if (request_method == NULL) exit(1);

    if (!strcmp(request_method, "GET") || !strcmp(request_method, "HEAD") ) {
        cgiinput= strdup(getenv("QUERY_STRING")) ;
    }
    else if (!strcmp(request_method, "POST")) {
        /* strcasecmp() is not supported in Windows-- use strcmpi() instead */
        if ( strcasecmp(getenv("CONTENT_TYPE"), "application/x-www-form-urlencoded")) {
            printf("getcgivars(): Unsupported Content-Type.\n") ;
            exit(1) ;
        }
        if ( !(content_length = atoi(getenv("CONTENT_LENGTH"))) ) {
            printf("getcgivars(): No Content-Length was sent with the POST request.\n") ;
            exit(1) ;
        }
        if ( !(cgiinput= (char *) malloc(content_length+1)) ) {
            printf("getcgivars(): Could not malloc for cgiinput.\n") ;
            exit(1) ;
        }
        if (!fread(cgiinput, content_length, 1, stdin)) {
            printf("Couldn't read CGI input from STDIN.\n") ;
            exit(1) ;
        }
        cgiinput[content_length]='\0' ;
    }
    else {
        printf("getcgivars(): unsupported REQUEST_METHOD\n") ;
        exit(1) ;
    }

    /** Change all plusses back to spaces **/
    for(i=0; cgiinput[i]; i++) if (cgiinput[i] == '+') cgiinput[i] = ' ' ;

    /** First, split on "&" to extract the name-value pairs into pairlist **/
    pairlist= (char **) malloc(256*sizeof(char **)) ;
    paircount= 0 ;
    nvpair= strtok(cgiinput, "&") ;
    
    while (nvpair) {
        pairlist[paircount++]= strdup(nvpair) ;

        if (!(paircount%256))
	  /*deals with the situation if there are more than n (starts at 256) 
	    pairs, and increases n */
	  pairlist= (char **) realloc(pairlist,(paircount+256)*sizeof(char **)) ;
        nvpair= strtok(NULL, "&") ;
    }
    pairlist[paircount]= 0 ;    /* terminate the list with NULL */

    /** Then, from the list of pairs, extract the names and values **/
    cgivars= (char **) malloc((paircount*2+1)*sizeof(char **)) ;
    for (i= 0; i<paircount; i++) {
        if (eqpos=strchr(pairlist[i], '=')) {
            *eqpos= '\0' ;
            unescape_url(cgivars[i*2+1] = strdup(eqpos+1)) ;
        } else {
            unescape_url(cgivars[i*2+1] = strdup("")) ;
        }
        unescape_url(cgivars[i*2] = strdup(pairlist[i])) ;
    }
    cgivars[paircount*2]= 0 ;   /* terminate the list with NULL */
    
    /** Free anything that needs to be freed **/
    free(cgiinput) ;
    for (i=0; pairlist[i]; i++) free(pairlist[i]) ;
    free(pairlist) ;

    /** Return the list of name-value strings **/
    return cgivars ;
    
}


/*

A planet gradient is: a solid color, with some lighter (and less saturated) 
bands and some darker bands, with some hue, saturation, and value noise. 

*/

void do_gradient3 (BITMAP_D *bmp) {

  float hue [YSIZE];
  float val [YSIZE];
  float sat [YSIZE];

  float val2 [YSIZE];
  float sat2 [YSIZE];

  float main_h, main_s, main_v;

  int x, y, ymin, ymax, i, n = 0;

  int r, g, b;
  uint32_t c;

  main_h = rand () % 360;

  main_s = .55 + .3 * ((float) rand()) / RAND_MAX;
  main_v = .45 + .3 * ((float) rand()) / RAND_MAX;

  /*background color*/

  for (y = 0; y < YSIZE; y ++) {

    hue [y] = main_h;
    sat [y] = main_s;
    val [y] = main_v;

  }

  /*
    are there any light bands? (saturn doesn't have any)
  */
  if (rand () & 3) {
    n = 2 + (rand () & 3);
    
    /* 
       n largish light bands
    */
	
    for (i = 0; i < n; i ++) {
      
      ymin = rand () % (YSIZE - 22);
      ymax = ymin + 15 + (rand () & 7);
      
      main_s = .20 + .2 * ((float) rand()) / RAND_MAX;
      main_v = .65 + .2 * ((float) rand()) / RAND_MAX;
      
      for (y = ymin; y < ymax; y ++) {
	
	sat [y] = main_s;
	val [y] = main_v;
	
      }
      
    }
    n = 2 + (rand () & 3);
    
    /* 
    n smallish light bands
    */

    for (i = 0; i < n; i ++) {
      
      ymin = rand () % (YSIZE - 8
			);
      ymax = ymin + 1 + (rand () & 7);
      
      main_s = .20 + .2 * ((float) rand()) / RAND_MAX;
      main_v = .65 + .2 * ((float) rand()) / RAND_MAX;
      
      for (y = ymin; y < ymax; y ++) {
	
	sat [y] = main_s;
	val [y] = main_v;
	
      }
      
    }
    n = 0;

  } else {
    /*if there aren't any light bands, there's probably a bunch of dark ones.*/
    n += 4;
  }

  n += 2 + (rand () % 5);

  /* n small dark bands */

  for (i = 0; i < n; i ++) {

    ymin = rand () % (YSIZE - 7);
    ymax = ymin + (rand () & 7);

    main_s = .65 + .2 * ((float) rand()) / RAND_MAX;
    main_v = .2 + .2 * ((float) rand()) / RAND_MAX;

    for (y = ymin; y < ymax; y ++) {
      
      sat [y] = main_s;
      val [y] = main_v;

    }

  }

  /* smoothing (makes transitions nicer) */

  sat2 [0] = (sat [0] + sat [1]) / 2;
  sat2 [YSIZE] = (sat [YSIZE] + sat [YSIZE - 1]) / 2;
  val2 [0] = (val [0] + val [1]) / 2;
  val2 [YSIZE] = (val [YSIZE] + val [YSIZE - 1]) / 2;

  for (y = 1; y < YSIZE - 1; y ++) {
    
    sat2 [y] = (sat [y - 1] + sat [y] + sat [y + 1]) / 3;
    val2 [y] = (val [y - 1] + val [y] + val [y + 1]) / 3;
    
  }

  for (y = 1; y < YSIZE - 1; y ++) {
    
    sat [y] = sat2 [y];
    val [y] = val2 [y];
    
  }

  main_s = 0;
  main_v = 0;
  main_h = 0;

  for (y = 0; y < YSIZE; y ++) {
    
    main_s += 0.01 * ((float) rand ()) / RAND_MAX - 0.005;

    if (main_s > 0.15) main_s = 0.15;
    if (main_s < -0.15) main_s = -0.15;

    main_v += 0.01 * ((float) rand ()) / RAND_MAX - 0.005;

    if (main_v > 0.15) main_v = 0.15;
    if (main_v < -0.15) main_v = -0.15;

    main_h += 2 * ((float) rand ()) / RAND_MAX - 1;

    if ( main_h > 30) main_h = 30;
    if ( main_h < -30) main_h = -30;

    sat [y] += main_s;
    val [y] += main_v;
    hue [y] += main_h;

    if (sat [y] > 1) sat [y] = 1;
    if (sat [y] < 0) sat [y] = 0;
    if (val [y] > 1) val [y] = 1;
    if (val [y] < 0) val [y] = 0;

    hsv_to_rgb (hue [y], sat [y], val [y], &r, &g, &b);

    c = makecol32 (r, g, b);

    for (x = 0; x < XSIZE; x ++) {
      bmp->bmp_data [y][x] = c;
    }
    
  }

  
}

void do_bubbles (BITMAP_D *bmp) {

  int x, y, x2, y2, i, n;

  uint32_t * curline;

  /* new_x and new_y contain the new X and Y coords of the point [y][x] when
     seen through a "lens."
  */
  float new_x [32][32], new_y [32][32];
    
  BITMAP_D * bmp2;

  /* sets up the first quadrant of new_x and new_y, the rest are symmetrical */
  for (y = 0; y < 16; y ++) {
    for (x = 0; x < 16; x ++) {

      if (((x - 16) * (x - 16)) + ((y - 16) * (y - 16)) <= XSIZE) {
	
	new_x [y][x] = 15 - (15 * pow (fabs (x - 15) / 16, x_bubble_pow));
	new_y [y][x] = 15 - (15 * pow (fabs (y - 15) / 16, y_bubble_pow));
	
      } else {
	new_x [y][x] = x;
	new_y [y][x] = y;
      }
    }
  }

  /*sets up the last 3 quadrants of the lens*/
  for (y = 0; y < 16; y ++) {
    for (x = 0; x < 16; x ++) {
      
      new_x [y + 16][x + 16] = 31 - new_x [15 - y][15 - x];
      new_y [y + 16][x + 16] = 31 - new_y [15 - y][15 - x];
      
      new_x [y + 16][x] = new_x [15 - y][x];
      new_y [y + 16][x] = 31 - new_y [15 - y][x];
      
      new_x [y][x + 16] = 31 - new_x [y][15 - x];
      new_y [y][x + 16] = new_y [y][15 - x];
    }
  }

  bmp2 = (BITMAP_D *) malloc(sizeof (BITMAP_D));


  for (y = 0; y < YSIZE; y ++) {
    for (x = 0; x < XSIZE; x ++) {
      
      bmp2->bmp_data [y][x] = bmp->bmp_data [y][x];
      
    }
  }


  /*number of lenses*/
  n = 5 + (rand () & 7);

  /*applies the lens n times at random locations*/

  for (i = 0; i < n; i ++) {

    x2 = rand () % (XSIZE - 32);
    y2 = rand () % (YSIZE - 32);

    for (y = 0; y < 32; y ++) {
      for (x = 0; x < 32; x ++) {

	curline = (uint32_t *) bmp->bmp_data [(int)(new_y [y][x] + y2)];
	bmp2->bmp_data [y + y2][x + x2] = curline [(int) (new_x [y][x]) + x2];
	
      }
    }
  }
  for (y = 0; y < YSIZE; y ++) {
    for (x = 0; x < XSIZE; x ++) {
      
      bmp2->bmp_data [y][x] = bmp->bmp_data [y][x];
      
    }
  }

  free (bmp2);

}



void do_swirls (BITMAP_D *bmp) {

  int x, y, x2, y2, i, big_x, big_y, turb;

  uint32_t * curline;

  /*
    new_x [i] and new_y [i] compose a single swirling filter.
   */
  float new_x [8][32][32], new_y [8][32][32];
    
  float dist, ang;

  BITMAP_D * bmp2;

  bmp2 = (BITMAP_D * ) malloc (sizeof (BITMAP_D));

  /* amount of turbulence in the swirls overall */
  turb = (rand () & 7) + 2;

  /*
    the filters rotate each pixel less as radius -> max_radius (=XSIZE).  
    This means that the edges of the swirled region  are not distorted, 
    so that it tiles smoothly.
    */

  for (i = 0; i < 8; i ++) {
    for (y = 0; y < 32; y ++) {
      for (x = 0; x < 32; x ++) {
	
	dist = ((x - 16) * (x - 16)) + ((y - 16) * (y - 16));
	
	if (dist <= XSIZE) {
	  
	  ang = (i - 4) * (20 + (rand () % turb)) / (2 * dist);
	  
	  new_x [i][y][x] = 16 + (x - 16) * cos (ang) - (y - 16) * sin (ang);
	  new_y [i][y][x] = 16 + (x - 16) * sin (ang) + (y - 16) * cos (ang);
	  
	} else {
	  
	  new_x [i][y][x] = x;
	  new_y [i][y][x] = y;

	}
      }
    }
  }
  


  /*
    applies the swirl filter at each point on a jittered, 
    overlapping grid.
  */

  for (big_y = 0; big_y < YSIZE; big_y += 24) {
    for (big_x = 0; big_x < XSIZE; big_x += 24) {

      x2 = big_x + (rand () & 7);
      y2 = big_y + (rand () & 7);

      i = rand () & 7;

 
      for (y = 0; y < 32; y ++) {
	for (x = 0; x < 32; x ++) {
	  
	  curline = (uint32_t *) bmp->bmp_data [((int)(new_y [i][y][x] + y2)) % YSIZE];
	  bmp2->bmp_data [y][x] = curline [((int)(new_x [i][y][x]) + x2) % XSIZE];
	  
	}
      }
 
      for (y = 0; y < 32; y ++) {
	for (x = 0; x < 32; x ++) {
	  
	  bmp->bmp_data [y + y2][x + x2] = bmp2->bmp_data [y][x];
	  
	}
      }

    }
  }

  for (big_y = YSIZE; big_y > 0; big_y -= 24) {
    for (big_x = XSIZE; big_x > 0; big_x -= 24) {

      x2 = big_x + (rand () & 7);
      y2 = big_y + (rand () & 7);

      i = rand () & 7;


      for (y = 0; y < 32; y ++) {
	for (x = 0; x < 32; x ++) {
	  
	  curline = (uint32_t *) bmp->bmp_data [((int)(new_y [i][y][x] + y2)) % YSIZE];
	  bmp2->bmp_data [y][x] = curline [((int)(new_x [i][y][x]) + x2) % XSIZE];
	}
      }
      
      
      for (y = 0; y < 32; y ++) {
	for (x = 0; x < 32; x ++) {
	  
	  bmp->bmp_data [y + y2][x + x2] = bmp2->bmp_data [y][x];	  
	}
      }
      
    }
  }

  /*free (bmp2);*/

}

/*same as do_swirls, but on a 1/4 scale.*/
void do_swirls_small (BITMAP_D *bmp) {

  int x, y, x2, y2, i, big_x, big_y, turb;

  uint32_t * curline;

  /*
    new_x [i] and new_y [i] compose a single swirling filter.
   */

  float new_x [8][16][16], new_y [8][16][16];
    
  float dist, ang;

  BITMAP_D * bmp2;

  bmp2 = (BITMAP_D * ) malloc (sizeof (BITMAP_D));

  /*amount of turbulence in the swirls overall*/
  turb = (rand () & 3) + 1;

  /*
    the filters rotate each pixel less as radius -> max_radius (=XSIZE).  
    This means that the edges of the swirled region are not distorted, 
    so that it tiles smoothly.
    */

  for (i = 0; i < 8; i ++) {
    for (y = 0; y < 16; y ++) {
      for (x = 0; x < 16; x ++) {
	
	dist = ((x - 8) * (x - 8)) + ((y - 8) * (y - 8));
	
	if (dist <= YSIZE/2) {
	  
	  ang = (i - 4) * (7 + (rand () % turb)) / (2 * dist);
	  
	  new_x [i][y][x] = 8 + (x - 8) * cos (ang) - (y - 8) * sin (ang);
	  new_y [i][y][x] = 8 + (x - 8) * sin (ang) + (y - 8) * cos (ang);
	  
	} else {
	  
	  new_x [i][y][x] = x;
	  new_y [i][y][x] = y;

	}
      }
    }
  }

  /*
    applies the swirl filter at each point on a jittered, 
    overlapping grid.
  */

  for (big_y = 0; big_y < YSIZE; big_y += 12) {
    for (big_x = 0; big_x < XSIZE; big_x += 12) {

      x2 = big_x + (rand () & 3);
      y2 = big_y + (rand () & 3);

      i = rand () & 7;

 
      for (y = 0; y < 16; y ++) {
	for (x = 0; x < 16; x ++) {
	  
	  curline = (uint32_t *) bmp->bmp_data [((int)(new_y [i][y][x] + y2)) & (YSIZE - 1)];
	  bmp2->bmp_data [y][x] = curline [((int)(new_x [i][y][x]) + x2) & (XSIZE - 1)];
	  
	}
      }
 
      for (y = 0; y < 16; y ++) {
	for (x = 0; x < 16; x ++) {
	  
	  bmp->bmp_data [y + y2][x + x2] = bmp2->bmp_data [y][x];
	  
	}
      }

    }
  }

  for (big_y = YSIZE; big_y > 0; big_y -= 12) {
    for (big_x = XSIZE; big_x > 0; big_x -= 12) {

      x2 = big_x + (rand () & 3);
      y2 = big_y + (rand () & 3);

      i = rand () & 7;


      for (y = 0; y < 16; y ++) {
	for (x = 0; x < 16; x ++) {
	  
	  curline = (uint32_t *) bmp->bmp_data [((int)(new_y [i][y][x] + y2)) & (YSIZE - 1)];
	  bmp2->bmp_data [y][x] = curline [((int)(new_x [i][y][x]) + x2) & (XSIZE - 1)];
	}
      }
      
      
      for (y = 0; y < 16; y ++) {
	for (x = 0; x < 16; x ++) {
	  
	  bmp->bmp_data [y + y2][x + x2] = bmp2->bmp_data [y][x];	  
	}
      }
      
    }
  }

  free (bmp2);

}

/*
  creates Perlin noise and multiplies the value of each pixel 
  in the bitmap by it.
*/

void do_plasma (BITMAP_D * bmp) {

  int i, x, y, offsx, offsy;

  int r, g, b;
  uint32_t c;

  uint32_t bmp2 [YSIZE][XSIZE];

  for (y = 0; y < YSIZE; y ++) {
    for (x = 0; x < XSIZE; x ++) {
      bmp2 [y][x] = 0;
    }
  }

  for (i = YSIZE/2; i > 0; i = i >> 1) {

    offsx = rand () % YSIZE;
    offsy = rand () % XSIZE;

    for (y = 0; y < YSIZE; y ++) {
      for (x = 0; x < XSIZE; x ++) {
	
	bmp2 [y][x] += i * ((sin ((offsy + y) * YSIZE * PI / (i * YSIZE)) + 1) +
	                    (cos ((offsx + x) * YSIZE * PI / (i * XSIZE)) + 1));

      }
    }
  }
  for (y = 0; y < YSIZE; y ++) {
    for (x = 0; x < XSIZE; x ++) {
      
      c = bmp->bmp_data [y][x];
      
      r = getr32 (c);
      g = getg32 (c);
      b = getb32 (c);
      
      if (bmp2 [y][x] > 255) bmp2 [y][x] = 255;

      c = makecol32 ((r * bmp2 [y][x]) >> 8, (g * bmp2 [y][x]) >> 8, (b * bmp2 [y][x]) >> 8);

      bmp->bmp_data [y][x] = c;
            
    }
  }
  
}


void do_wind (BITMAP_D * bmp) {
  
  int x, y, i, r, g, b;
  uint32_t c;
  
  BITMAP_D * bmp2;
  
  bmp2 = (BITMAP_D *) malloc (sizeof (BITMAP_D));
  
  
  for (y = 0; y < YSIZE; y ++) {
    for (x = 0; x < XSIZE; x ++) {
	  
      bmp2->bmp_data [y][x] = bmp->bmp_data [y][x];	  
    }
  }
  
  for (y = 0; y < YSIZE; y ++) {
    for (x = 0; x < XSIZE; x += (rand () & 3)) {
      
      bmp2->bmp_data [y][x] = bmp->bmp_data [rand () & (YSIZE - 1)][rand () & (XSIZE - 1)];
    }
  }
  
  for (y = 0; y < YSIZE; y ++) {
    for (x = 0; x < XSIZE; x ++) {
      r = 0;
      b = 0;
      g = 0;
      
      for (i = 0; i < 16; i ++) {
	c = bmp2->bmp_data [y][(x + i) & (XSIZE - 1)];
	r += getr32 (c);
	g += getg32 (c);
	b += getb32 (c);
      }
      
      c = bmp->bmp_data [y][x];
      
      bmp->bmp_data [y][x] = makecol32 (((r >> 4) + getr32 (c) * 1) >> 1, 
					((g >> 4) + getg32 (c) * 1) >> 1,
					((b >> 4) + getb32 (c) * 1) >> 1);
      
    }
  }

  free (bmp2);

}


/*
  not used, but grabs a new value for each pixel from one of the 
  9 squares that surrounds it.
*/

void do_diffuse (BITMAP_D *bmp) {

  int x, y;

  BITMAP_D * bmp2;

  bmp2 = (BITMAP_D *) malloc (sizeof (BITMAP_D));

  for (y = 0; y < YSIZE; y ++) {
    for (x = 0; x < XSIZE; x ++) {
      
      bmp2->bmp_data [y][x] = bmp->bmp_data [(y + (rand () % 3) - 1) & (YSIZE - 1)][(x + (rand () % 3) - 1) & (XSIZE - 1)];
    }
  }

  for (y = 0; y < YSIZE; y ++) {
    for (x = 0; x < XSIZE; x ++) {
	  
      bmp->bmp_data [y][x] = bmp2->bmp_data [y][x];	  
    }
  }

  free (bmp2);

}

int getseed () {

  char ** cgivars;
  int numvars;
  int i;

  char *rport_str;
  int rport;

  cgivars = getcgivars ();
  i = 0;
  while (cgivars [i] != NULL) {
    i ++;
  }
  numvars = i / 2;
  for (i = 0; i < numvars; i += 2) {
    if (!strcasecmp (cgivars [i], "seed")) return atoi (cgivars [i + 1]);
  }

  /*
    On Netscape Navigator, the remote port changes with every hit
    on MSIE, it doesn't. 
  */
  rport_str = getenv ("REMOTE_PORT");
  rport = atoi (rport_str);
  return time (NULL) ^ rport;

}

int main (int argc, char ** argv) {
  
  BITMAP_D *dbl_buf;
  BITMAP_D *cylmap;

  float spherewidth [YSIZE];
  
  float tx [YSIZE];
    
  int x, y, i, j = 0, dist;
  int xcen, ycen;

  int r, g, b;

  uint32_t c;

  int randseed = 0;

  char * bmpfile;
  char * command_str;

  dbl_buf = (BITMAP_D *) malloc (sizeof (BITMAP_D));

  memset (dbl_buf->bmp_data, 0, sizeof (dbl_buf));

  randseed = getseed ();

  srand (randseed);

  cylmap = (BITMAP_D *) malloc (sizeof (BITMAP_D));

  for (i = 0; i < YSIZE; i ++) {
    
    spherewidth [i] = (YSIZE * 6.28) - sqrt (HALF_YSIZE * HALF_YSIZE - (abs (HALF_YSIZE - i)) * (abs (HALF_YSIZE - i))) * 6.28;
    
    tx [i] = (-1.57 + acos (((float)i - HALF_YSIZE) / HALF_YSIZE)) / (2 * PI);

  }

      
  do_gradient3 (cylmap);
  //do_diffuse (cylmap);
  do_bubbles (cylmap);
  do_swirls (cylmap);
  do_bubbles (cylmap);
  do_swirls_small (cylmap);
  do_plasma (cylmap);
  do_wind (cylmap);
  
  
  for (y = 0; y < YSIZE; y ++) {
    for (x = 0; x < YSIZE; x ++) {
      xcen = (x - HALF_YSIZE);
      ycen = (y - HALF_YSIZE);
      dist = (xcen * xcen + ycen * ycen) / (HALF_YSIZE / 4);

      if (dist < 256) {
	dist = 256 - dist;	

	c = cylmap->bmp_data [y][((int) (j + tx [x] * spherewidth [y])) & (XSIZE - 1)];
	r = ((c & 255) * dist) >> 8;
	c = c >> 8;
	g = ((c & 255) * dist) >> 8;
	c = c >> 8;
	b = ((c & 255) * dist) >> 8;
	c = r + (g << 8) + (b << 16);
	dbl_buf->bmp_data [y][x] = c;
      }
    }
  }
    
  command_str = malloc (1000);
  bmpfile = malloc (1000);

  if (debug) 
    printf ("Content-type:text/html\n\n");
  else 
    printf ("Content-type:image/jpeg\n\n");

  sprintf (bmpfile, "/tmp/%i.bmp", rand () % 100000000);
  if (debug) printf ("bmpfile = %s (len = %i)\n", bmpfile, strlen (bmpfile));

  fflush (stdout);

  save_bmp (bmpfile, dbl_buf, YSIZE, YSIZE);
  sprintf (command_str, "/usr/bin/cjpeg %s", bmpfile);
  system (command_str);  
  unlink (bmpfile);
  return 0;

}




