/*#autogcc
#This comment is a bash script to self-compile
#this .c file. Just run ./filename.c
cd /mnt/data/projects/src/bifurcate_png
echo "compiling .c file"
gcc main.c -o bifur -lpng
exit
*/

/*
BIFURCATION TREE PLOTTER
by Chris Merck
navaburo@gmail.com
This software may be freely redistributed under the terms
 of the GNU public license.
*/ 

//includes and basic definitions
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#define PNG_DEBUG 3
#include <png.h>
#include <stdio.h>
#include <stdlib.h>
#define FONT_HEIGHT  8
#define FONT_WIDTH   8


 //globals
//this is the value of a for the zeroth iteration
#define ASEED .159357
//#define SILENTI 100
//#define VISABLEI YMAX

//this is QSXGA resolution! WOW!
#define XMAX 2560
#define YMAX 2048

//this is XGA
//#define XMAX 1024
//#define YMAX 768

int bifur[XMAX][YMAX]; //this is the work array

int x, y;

double brightness;

int width, height;
png_byte color_type;
png_byte bit_depth;

png_structp png_ptr;
png_infop info_ptr;
int number_of_passes;
png_bytep * row_pointers;



int main(int argc, char *argv[])
{
	int i=0;
	
	int SILENTI, VISABLEI;
	
	double C=0, a=0, cmin=1, cmax=4, amin=0, amax=1;
	
	//initialize variables from command-line arguments
	if (argc<7)
	{
		printf("usage: pix silenti visablei cmin cmax amin amax brightness\n");
		return(1);
	}
	SILENTI = atoi(argv[1]);
	VISABLEI = atoi(argv[2]);
	cmin = atof(argv[3]);
	cmax = atof(argv[4]);
	amin = atof(argv[5]);
	amax = atof(argv[6]);
	brightness = atof(argv[7]);
	
	printf("initializing bifur work array...\n");
	for (x=0;x<XMAX;x++)
		for (y=0;y<YMAX;y++)
			bifur[x][y]=0;
			
	printf("plotting bifurcation tree to work array...\n");
	
	/*plot the Bifurcation Tree
	each x value corresponds to a coefficient of C in 
	 the sequence a = Ca(1-a) Given sufficient time, for
	 some given value of C (and an appropriate initial a)
	 this sequence will settle into an orbit.
	This code plots the orbits of increasing values of C
	 on the interval [1,4] or some subset thereof. */
	 
	//each pixel column represents the orbit of a for some C
	for (x=0; x<XMAX; x++)
	{	
		a=ASEED; //initialize sequence
		C=(cmax-cmin)*x/XMAX + cmin; //set coefficient
		
		//iterate silently to allow orbit to stablize
		for (i=0;i<=SILENTI;i++)
			a=C*a*(1-a);
		
		//draw orbit
		for (i=0;i<=VISABLEI;i++)
		{
			a=C*a*(1-a);
			if (a>=amin && a<=amax) //only draw if within window
			{
				//calculate where to plot a on the screen
				y=YMAX-(a-amin)/(amax-amin)*YMAX; //y is an integer so this auto-rounds
				bifur[x][y]++;
			}
		}
	}
	
	printf("reading blank.png\n");
	read_png_file("blank.png");
	printf("manipulating png\n");
	process_file(); //this writes the screen to the png
	printf("writing to out.png\n");
	write_png_file("out.png");
	printf("DONE: Image saved as out.png\n");
	
	//all done!
	return 0;
}


/* The following PNG access code was written by:
 * Guillaume Cottenceau (gc at mandrakesoft.com)
 *
 * Copyright 2002 MandrakeSoft
 *
 * This software may be freely redistributed under the terms of the GNU
 * public license.
 *
 */



void abort_(const char * s, ...)
{
	va_list args;
	va_start(args, s);
	vfprintf(stderr, s, args);
	fprintf(stderr, "\n");
	va_end(args);
	abort();
}

void read_png_file(char* file_name)
{
	char header[8];	// 8 is the maximum size that can be checked

	/* open file and test for it being a png */
	FILE *fp = fopen(file_name, "rb");
	if (!fp)
		abort_("[read_png_file] File %s could not be opened for reading", file_name);
	fread(header, 1, 8, fp);
	if (png_sig_cmp(header, 0, 8))
		abort_("[read_png_file] File %s is not recognized as a PNG file", file_name);


	/* initialize stuff */
	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	
	if (!png_ptr)
		abort_("[read_png_file] png_create_read_struct failed");

	info_ptr = png_create_info_struct(png_ptr);
	if (!info_ptr)
		abort_("[read_png_file] png_create_info_struct failed");

	if (setjmp(png_jmpbuf(png_ptr)))
		abort_("[read_png_file] Error during init_io");

	png_init_io(png_ptr, fp);
	png_set_sig_bytes(png_ptr, 8);

	png_read_info(png_ptr, info_ptr);

	width = info_ptr->width;
	height = info_ptr->height;
	color_type = info_ptr->color_type;
	bit_depth = info_ptr->bit_depth;

	number_of_passes = png_set_interlace_handling(png_ptr);
	png_read_update_info(png_ptr, info_ptr);


	/* read file */
	if (setjmp(png_jmpbuf(png_ptr)))
		abort_("[read_png_file] Error during read_image");

	row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height);
	for (y=0; y<height; y++)
		row_pointers[y] = (png_byte*) malloc(info_ptr->rowbytes);

	png_read_image(png_ptr, row_pointers);

        fclose(fp);
}


void write_png_file(char* file_name)
{
	/* create file */
	FILE *fp = fopen(file_name, "wb");
	if (!fp)
		abort_("[write_png_file] File %s could not be opened for writing", file_name);


	/* initialize stuff */
	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	
	if (!png_ptr)
		abort_("[write_png_file] png_create_write_struct failed");

	info_ptr = png_create_info_struct(png_ptr);
	if (!info_ptr)
		abort_("[write_png_file] png_create_info_struct failed");

	if (setjmp(png_jmpbuf(png_ptr)))
		abort_("[write_png_file] Error during init_io");

	png_init_io(png_ptr, fp);


	/* write header */
	if (setjmp(png_jmpbuf(png_ptr)))
		abort_("[write_png_file] Error during writing header");

	png_set_IHDR(png_ptr, info_ptr, width, height,
		     bit_depth, color_type, PNG_INTERLACE_NONE,
		     PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

	png_write_info(png_ptr, info_ptr);


	/* write bytes */
	if (setjmp(png_jmpbuf(png_ptr)))
		abort_("[write_png_file] Error during writing bytes");

	png_write_image(png_ptr, row_pointers);


	/* end write */
	if (setjmp(png_jmpbuf(png_ptr)))
		abort_("[write_png_file] Error during end of write");

	png_write_end(png_ptr, NULL);

        /* cleanup heap allocation */
	for (y=0; y<height; y++)
		free(row_pointers[y]);
	free(row_pointers);

        fclose(fp);
}


void process_file(void)
{
	double gray;
	
	if (info_ptr->color_type != PNG_COLOR_TYPE_RGBA)
		abort_("[process_file] color_type of input file must be PNG_COLOR_TYPE_RGBA (is %d)", info_ptr->color_type);

	for (y=0; y<height; y++) {
		png_byte* row = row_pointers[y];
		for (x=0; x<width; x++) {
			png_byte* ptr = &(row[x*4]);
			/*printf("Pixel at position [ %d - %d ] has the following RGBA values: %d - %d - %d - %d\n",
			       x, y, ptr[0], ptr[1], ptr[2], ptr[3]); */
			       
			/* write the pixel */
			//calculate grayscale value
			gray=bifur[x][y];
			gray*=brightness;
			if (gray>255) gray=255;
			gray=255-gray; //invert colors (black on white)
			//printf("%d",bifur[x,y]);
			ptr[0] = gray;
			ptr[1] = gray;
			ptr[2] = gray;
			//if (gray==0) ptr[1]=255; //color the overloaded pixels
			
		}
	}

}