/***************************************************************************
 * Colours! - A colourful demonstration of Turbo C's VGA graphics.         *
 * Copyright (C) October 2004 Neil Fraser                                  *
 * http://neil.fraser.name/                                                *
 *                                                                         *
 * This program is free software; you can redistribute it and/or           *
 * modify it under the terms of the GNU General Public License             *
 * as published by the Free Software Foundation.                           *
 *                                                                         *
 * This program is distributed in the hope that it will be useful,         *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
 * GNU General Public License (www.gnu.org) for more details.              *
 *                                                                         *
 * Compile with:  tcc colour~1.c graphics.lib                              *
 * Executed with: colours-vga [x[, y]]  (where x and y both default to 129)*
 ***************************************************************************/

#include <stdio.h>
#include <graphics.h>
#include <conio.h>

int rows;
int cols;
int *grid;


/* Pick a random integer between two end values (inclusive). */
int midpoint(int end1, int end2) {
    int swap;
    if (end1 > end2) {
        swap = end1;
        end1 = end2;
        end2 = swap;
    }
    return rand()%(end2 - end1 + 1) + end1;
}


/* Recall the value of a pixel */
int getpoint(int x, int y) {
    return *(grid + x*rows + y);
}


/* Set one pixel and record its value */
void setpoint(int x, int y, int value) {
    *(grid + x*rows + y) = value;
}


/* fillrow(row, endleft, endright) */
/* Recursively fill one row. */
/* Used to bootstrap the recursive area fill. */
/* Also used to fudge our way out of ?x2 rectangles. */
void fillrow(row, endleft, endright) {
    int middleX;
    if (endleft + 1 == endright)
      return;
    /* Set the dot in the middle. */
    middleX = (int)((endright - endleft) / 2 + endleft);
    setpoint(middleX, row, midpoint(getpoint(endleft, row), getpoint(endright, row)));
    /* Go fill the two new sub-rows */
    fillrow(row, endleft, middleX);
    fillrow(row, middleX, endright);
}


/* fillcol(col, endtop, endbottom) */
/* Recursively fill one column. */
/* Used to bootstrap the recursive area fill. */
/* Also used to fudge our way out of ?x2 rectangles. */
void fillcol(col, endtop, endbottom) {
    int middleY;
    if (endtop + 1 == endbottom)
      return;
    /* Set the dot in the middle. */
    middleY = (int)((endbottom - endtop) / 2 + endtop);
    setpoint(col, middleY, midpoint(getpoint(col, endtop), getpoint(col, endbottom)));
    /* Go fill the two new sub-columns */
    fillcol(col, endtop, middleY);
    fillcol(col, middleY, endbottom);
}


/* fillarea(endtop, endbottom, endleft, endright) */
/* Recursively fill the area. */
void fillarea(endtop, endbottom, endleft, endright) {
    int middleX, middleY;
    int centre1, centre2;
    if ((endleft + 1 == endright) && (endtop + 1 == endbottom)) {
        /* This is just a 2x2 square.  Nothing to do. */
    } else if (endleft + 1 == endright) {
        /* This is just a 2x? rectangle.  Fill in some vertical space. */
        fillcol(endright, endtop, endbottom);
    } else if (endtop + 1 == endbottom) {
        /* This is just a ?x2 rectangle.  Fill in some horizontal space. */
        fillrow(endbottom, endleft, endright);
    } else {
        /* Wide open space; something to sink our recursive teeth into... */
        /* Set the dot half way along the bottom row. */
        middleX = (int)((endright - endleft) / 2 + endleft);
        setpoint(middleX, endbottom, midpoint(getpoint(endleft, endbottom), getpoint(endright, endbottom)));

        /* Set the dot half way along the right column. */
        middleY = (int)((endbottom - endtop) / 2 + endtop);
        setpoint(endright, middleY, midpoint(getpoint(endright, endtop), getpoint(endright, endbottom)));

        /* Set the dot in the middle (midpoint of two midpoints). */
        centre1 = midpoint(getpoint(middleX, endtop), getpoint(middleX, endbottom));
        centre2 = midpoint(getpoint(endright, middleY), getpoint(endleft, middleY));
        setpoint(middleX, middleY, midpoint(centre1, centre2));

        /* Go fill the four new quadrants. */
        fillarea(endtop, middleY, endleft, middleX);
        fillarea(middleY, endbottom, endleft, middleX);
        fillarea(endtop, middleY, middleX, endright);
        fillarea(middleY, endbottom, middleX, endright);
    }
}


/* Display the image on screen. */
/* Graphics.lib in Turbo C 2.01 only supports 16 colours. */
void render_image() {
    int graphdriver = DETECT;
    int graphmode;
    int x, y;
    int colour1=0, colour2=0, colour3=0;

    /* Initialize graphics system */
    initgraph(&graphdriver, &graphmode, "");
    cleardevice();

    /* Pick three random colours */
    while (colour1 == colour2 || colour2 == colour3) {
        colour1 = rand()%16;
        colour2 = rand()%16;
        colour3 = rand()%16;
    }

    /* Draw the pixel for each grid element */
    /* Dither each pixel between two known points */
    for (y=0; y<rows; y++)
        for (x=0; x<cols; x++)
            if (getpoint(x, y) > 127)
              if (rand()%128 + 128 < getpoint(x, y))
                  putpixel(x, y, colour1);
              else
                  putpixel(x, y, colour2);
            else
              if (rand()%128 < getpoint(x, y))
                  putpixel(x, y, colour2);
              else
                  putpixel(x, y, colour3);
}


int main(int argc, char *argv[]) {
    int x;
    int y;

    /* Height and width of the image. */
    /* All values work, but 2^n+1 squares are ideal (65x65, 129x129, 257x257, etc). */
    rows = 129;
    cols = 129;
    if (argc > 1)
        cols = atoi(argv[1]);
    if (argc > 2)
        rows = atoi(argv[2]);
    printf("Size = %d*%d.\n", cols, rows);

    /* The grid of points that make up the image. */
    if ((grid = (int *) malloc(cols * rows * sizeof(int)) ) == NULL) {
        printf("Unable to allocate memory for image.");
        return;
    }
    printf("Memory allocated.\n");

    /* Initialise the grid (not needed)
    for (y=0; y<rows; y++)
        for (x=0; x<cols; x++)
            setpoint(x, y, 0);
    */

    srand (time (0)); /* Seed the random number generator with the time */
    srand (rand());   /* Make the generator less linear */

    /* Flip a coin to determine which diagonally opposite corners get the master colours. */
    if (rand()%2==1) {
        setpoint(0, 0, 0);
        setpoint(cols-1, rows-1, 255);
        setpoint(cols-1, 0, midpoint(0, 255));
        setpoint(0, rows-1, midpoint(0, 255));
    } else {
        setpoint(0, rows-1, 0);
        setpoint(cols-1, 0, 255);
        setpoint(0, 0, midpoint(0, 255));
        setpoint(cols-1, rows-1, midpoint(0, 255));
    }

    /* Draw the top row and left column. */
    fillrow(0, 0, cols-1);
    fillcol(0, 0, rows-1);

    /* Now we have the top edge, the left edge, and the bottom/right dot. */
    /* We are all setup to fill the remaining area. */
    fillarea(0, rows-1, 0, cols-1);

    /* Print the grid of numbers */
    /*
    for (y=0; y<rows; y++) {
        for (x=0; x<cols; x++) {
            printf("%d ", getpoint(x, y));
        }
        printf("\n");
    }
    */

    render_image();

    free(grid);
    grid = NULL;
    
    getch(); /* wait until key pressed */
	restorecrtmode(); /* restores the screen to the original screen */
    return;
}
