/* ================================================== *
 *                GENERAL INFORMATION                 *
 * ================================================== *
 * This C program implements functions for handling   *
 * 24-bit images, drawing and parts of the bitmap     *
 * (.bmp) file format. Supported color models include *
 * rgb hsl. There are also functions for mandelbrot   *
 * set fractal rendering.                             *
 *                                                    *
 * Written by Jonathan Frech.                         *
 *                                                    *
 * Edit history:                                      *
 * 23rd, 24th, 27th, 28th, 29th, 30th of June,        *
 * 1st, 2nd, 3rd, 10th, 11th, 13th, 14th, 15th, 16th, *
 * 17th, 18th, 19th, 20th, 25th, 26th, 27th, 29th of  *
 * July 2017, 21st, 22nd, 23rd of March 2018          */

/* ================================================== *
 *                    COMPILATION                     *
 * ================================================== *
 * $ rm bmp; gcc bmp.c -lm -o bmp; ./bmp              */

/* ================================================== *
 *                 POSSIBLE PROBLEMS                  *
 * ================================================== *
 *                                                    *
 * I) Lost image reference                            *
 * Most image functions return an image pointer.      *
 *    Img = rrotate(img)                              *
 * In this case, both img and Img exist in memory.    *
 * A problem may arise when the function's output is  *
 * directly used in another funcion call.             *
 *    savebmp(rrotate(img), "out.bmp");               *
 * In this case, the memory for img is allocated,     *
 * though its reference is lost it thereby cannot be  *
 * freed anymore.                                     *
 *                                                    *
 * II) Bitmap bit depth                               *
 * The only supported bit depth is 24 bits. Bitmap    *
 * files with a different bit depth cannot be read or *
 * written.                                           *
 *                                                    *
 * III) Speed [TODO]                                  *
 * Some functions (mostly ellipse(), ...)             *
 * are inefficiently implemented and perform slowly.  */

// [TODO] color scheme generation functions

/* ======================================= *
 *  INCLUSION, CONSTANTS, TYPE DEFINITION  *
 * ======================================= */

// include header files
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>

// define the eight full-value color integers (0xrrggbb)
static const int BLACK   = 0x000000, RED   = 0xff0000,
                 GREEN   = 0x00ff00, BLUE  = 0x0000ff,
                 YELLOW  = 0xffff00, CYAN  = 0x00ffff,
                 MAGENTA = 0xff00ff, WHITE = 0xffffff;

// bitmap struct and type definition
typedef struct image { int width; int height; int *px; } image;

// byte type; value range [0, 255]
typedef unsigned char byte;

// color scheme struct used in mandelbrot set fractal rendering
typedef struct ClrSch { int Clr; int *Arr; } ClrSch;





/* ======================= *
 *  FUNCTION DELARACTIONS  *
 * ======================= */

/* I) color functions */
int color(byte r, byte g, byte b);
int hsl(double h, double s, double l);

/* II) image, draw functions */
image *newimage(int w, int h);
void freeimage(image *img);
int getpx(image *img, int x, int y);
void setpx(image *img, int x, int y, int c);
void fill(image *img, int c);

/* === DRAWING PRIMITIVES === */
void hline      (image *img, int x0, int x1, int y , int c               ); // draw horizontal line
void vline      (image *img, int x , int y0, int y1, int c               ); // draw vertical line
void line       (image *img, int x0, int y0, int x1, int y1, int c       ); // draw line
void fillrect   (image *img, int x0, int y0, int x1, int y1, int c       ); // draw filled rectangle
void rect       (image *img, int x0, int y0, int x1, int y1, int c       ); // draw rectangle
void fillcircle (image *img, int x , int y , int r , int c               ); // draw filled circle
void circle     (image *img, int x , int y , int r , int t , int c       ); // draw circle (with certain thickness)
void fillellipse(image *img, int x , int y , int rx, int ry, int c       ); // draw filled ellipse
void ellipse    (image *img, int x , int y , int rx, int ry, int t, int c); // draw ellipse (with certain thickness)

/* === TRANSFORMATION PRIMITIVES === */
image *resize   (image *img, int w , int h                                ); // resize an image
image *hflip    (image *img                                               ); // flip horizontally
image *vflip    (image *img                                               ); // flip vertically
image *rrotate  (image *img                                               ); // rotate clockwise
image *lrotate  (image *img                                               ); // rotate counter-clockwise
image *hrotate  (image *img                                               ); // rotate half a revolution
image *crop     (image *img, int x0, int y0, int x1, int y1               ); // crop an image
void   blit     (image *img, image*, int x , int y                        ); // blit an image onto another one

/* III) bitmap file functions */
image *readbmp(char fn[]);
void savebmp(image *img, char fn[]);

/* IV) utility functions */
int min (int a, int b); int max(int a, int b);
int mean(int a, int b); int abs(int v);

/* V) testing functions */
void graphic_test1();
void graphic_test2();
void graphic_test3();

/* ===== *
 *  MAIN *
 * ===== */
int main() {
	graphic_test1();
	graphic_test2();
	graphic_test3();
}





/* ==================== *
 *  I) COLOR FUNCTIONS  *
 * ==================== */

// concatenate three color bytes to one color integer
int color(byte r, byte g, byte b) { return (r<<16)|(g<<8)|b; }

// return color rgb integer based on hue, saturation and luminosity
// (0 <= h <= 360; 0 <= s, l <= 1)
int hsl(double h, double s, double l) {
	h /= 60;
	double c = s*(1-fabs(      l*2 -1));
	double x = c*(1-fabs(fmod(h, 2)-1));
	double m = l-c/2;
	double r = 0, g = 0, b = 0;
	if (0 <= h && h < 1) r = c, g = x; if (1 <= h && h < 2) r = x, g = c; if (2 <= h && h < 3) g = c, b = x;
	if (3 <= h && h < 4) g = x, b = c; if (4 <= h && h < 5) r = x, b = c; if (5 <= h && h < 6) r = c, b = x;
	return (byte)((r+m)*255)<<16
          |(byte)((g+m)*255)<< 8
          |(byte)((b+m)*255);
}





/* =========================== *
 *  II) IMAGE, DRAW FUNCTIONS  *
 * =========================== */

// return image struct pointer with allocated memory and cleared pixel data
image *newimage(int w, int h) {
	image *img = malloc(sizeof(image)); // image pointer
	img->width = w; img->height = h;    // dimensions
	img->px = malloc(w*h*sizeof(int));  // pixel data memory allocation
	fill(img, 0); return img;           // clear memory, return image pointer
}

// free image struct pointer's allocated memory
void freeimage(image *img) { free(img->px); free(img); }

// get pixel at coordinate (x, y)
int getpx(image *img, int x, int y) {
	if (x < 0 || x >= img->width || y < 0 || y > img->height) return -1; // value range
	return img->px[x+y*img->width];                                      // get pixels
}

// set pixel at cooridinate (x, y)
void setpx(image *img, int x, int y, int c) {
	if (x < 0 || x >= img->width || y < 0 || y > img->height) return; // value range
	img->px[x+y*img->width] = c;                                      // set pixel
}

// fill entire image with a single color
void fill(image *img, int c) {
	int N = img->width*img->height;                 // calculate total number of pixels
	for (int i = 0; i < N; i++) { img->px[i] = c; } // set pixels
}

// draw a horizontal line
void hline(image *img, int x0, int x1, int y, int c) {
	if (y < 0 || y >= img->height) return;                     // value range
	int N = min(max(x1, 0), img->width-1);                     // draw range
	for (int x = min(max(x0, 0), img->width-1); x <= N; x++) { // loop through x
		img->px[x+y*img->width] = c; }                         // set pixels
}

// draw a vertical line
void vline(image *img, int x, int y0, int y1, int c) {
	if (x < 0 || x >= img->width) return;                       // value range
	int N = min(max(y1, 0), img->height-1);                     // draw range
	for (int y = min(max(y0, 0), img->height-1); y <= N; y++) { // loop through y
		img->px[x+y*img->width] = c; }                          // set pixels
}

// draw a line
void line(image *img, int x0, int y0, int x1, int y1, int c) {
	double vx = x1-x0, vy = y1-y0, l = ceil(sqrt(vx*vx+vy*vy)) ?: 1;   // vector
	vx /= l; vy /= l; double px = x0, py = y0;                         // unify, point
	for (int i = 0; i <= l; i++) {                                     // loop
		if (0 <= px && px < img->width && 0 <= py && py < img->height) // draw range
			img->px[(int) round(px)+(int) round(py)*img->width] = c;   // set pixels
		px += vx; py += vy; }                                          // move along line
	setpx(img, x0, y0, c); setpx(img, x1, y1, c);                      // fix end points
}

// draw a filled rectangle
void fillrect(image *img, int x0, int y0, int x1, int y1, int c) {
	int N = min(max(y1, 0), img->height-1), M = min(max(x1, 0), img->width -1); // draw range
	for (int y = min(max(y0, 0), img->height-1); y <= N; y++) {                 // loop through y
	for (int x = min(max(x0, 0), img->width -1); x <= M; x++) {                 // loop through x
		img->px[x+y*img->width] = c; }}                                         // set pixels
}

// draw a rectangle
void rect(image *img, int x0, int y0, int x1, int y1, int c) {
	hline(img, x0, x1, y0, c); hline(img, x0, x1, y1, c); // horizontal lines
	vline(img, x0, y0, y1, c); vline(img, x1, y0, y1, c); // vertical lines
}

// draw a filled circle (inefficiently)
void _fillcircle(image *img, int x0, int y0, int r, int c) {
	int N = min(max(y0+r, 0), img->height-1), M = min(max(x0+r, 0), img->width -1); // draw range
	for (int y = min(max(y0-r, 0), img->height-1); y <= N; y++) {                   // loop through y
	for (int x = min(max(x0-r, 0), img->width -1); x <= M; x++) {                   // loop through x
		if (sqrt(pow(x0-x, 2)+pow(y0-y, 2)) <= r+1) img->px[x+y*img->width] = c; }} // set pixels
}

// draw a filled circle
void fillcircle(image *img, int x0, int y0, int r, int c) {
	int a = (int) ((r+1)/sqrt(2)), Lx = -x0, Rx = img->width-x0; // inner square's size
	int Ly = -y0, Ry = img->height-y0;                           // draw range
	fillrect(img, x0-a, y0-a, x0+a, y0+a, c);                    // fill inner square
	for (int y = r; y > a; y--) { for (int x = a; x >= 0; x--) { // loop through pixels
		if (sqrt(x*x+y*y) <= r+1) {                              // calculate distance
			if (Lx <= -x && -x < Rx && Ly <= -y && -y < Ry)      // draw all eight outer parts
				img->px[(x0-x)+(y0-y)*img->width] = c;           // of the circle to save
			if (Lx <=  x &&  x < Rx && Ly <= -y && -y < Ry)      // computational time
				img->px[(x0+x)+(y0-y)*img->width] = c;
			if (Lx <= -x && -x < Rx && Ly <=  y &&  y < Ry)
				img->px[(x0-x)+(y0+y)*img->width] = c;
			if (Lx <=  x &&  x < Rx && Ly <=  y &&  y < Ry)
				img->px[(x0+x)+(y0+y)*img->width] = c;
			if (Lx <= -y && -y < Rx && Ly <= -x && -x < Ry)
				img->px[(x0-y)+(y0-x)*img->width] = c;
			if (Lx <= -y && -y < Rx && Ly <=  x &&  x < Ry)
				img->px[(x0-y)+(y0+x)*img->width] = c;
			if (Lx <=  y &&  y < Rx && Ly <= -x && -x < Ry)
				img->px[(x0+y)+(y0-x)*img->width] = c;
			if (Lx <=  y &&  y < Rx && Ly <=  x &&  x < Ry)
				img->px[(x0+y)+(y0+x)*img->width] = c;
		}
	}}
}

// draw a circle
void circle(image *img, int x0, int y0, int r, int t, int c) {
	int a = (int) ((r+1)/sqrt(2)), Lx = -x0, Rx = img->width-x0; // inner square's size
	int Ly = -y0, Ry = img->height-y0;                           // draw range
	setpx(img, x0-a, y0-a, c); setpx(img, x0-a, y0+a, c);        // corner pixel fix (1)
	setpx(img, x0+a, y0-a, c); setpx(img, x0+a, y0+a, c);        // corner pixel fix (2)
	for (int y = r; y > a; y--) { for (int x = a; x >= 0; x--) { // loop through pixels
		int d = (int) sqrt(x*x+y*y);                             // calculate distance
		if (r-t/2 <= d && d <= r+t/2.) {                         // decide pixel placement
			if (Lx <= -x && -x < Rx && Ly <= -y && -y < Ry)      // draw all eight outer parts
				img->px[(x0-x)+(y0-y)*img->width] = c;           // of the circle to save
			if (Lx <=  x &&  x < Rx && Ly <= -y && -y < Ry)      // computational time
				img->px[(x0+x)+(y0-y)*img->width] = c;
			if (Lx <= -x && -x < Rx && Ly <=  y &&  y < Ry)
				img->px[(x0-x)+(y0+y)*img->width] = c;
			if (Lx <=  x &&  x < Rx && Ly <=  y &&  y < Ry)
				img->px[(x0+x)+(y0+y)*img->width] = c;
			if (Lx <= -y && -y < Rx && Ly <= -x && -x < Ry)
				img->px[(x0-y)+(y0-x)*img->width] = c;
			if (Lx <= -y && -y < Rx && Ly <=  x &&  x < Ry)
				img->px[(x0-y)+(y0+x)*img->width] = c;
			if (Lx <=  y &&  y < Rx && Ly <= -x && -x < Ry)
				img->px[(x0+y)+(y0-x)*img->width] = c;
			if (Lx <=  y &&  y < Rx && Ly <=  x &&  x < Ry)
				img->px[(x0+y)+(y0+x)*img->width] = c;
		}
	}}
}

// draw a circle (inefficiently)
void _circle(image *img, int x0, int y0, int r, int t, int c) {
	int N = min(max(y0+r+t/2, 0), img->height-1), M = min(max(x0+r+t/2, 0), img->width -1); // draw range
	for (int y = min(max(y0-r-t/2, 0), img->height-1); y <= N; y++) {                       // loop through y
	for (int x = min(max(x0-r-t/2, 0), img->width -1); x <= M; x++) {                       // loop through x
		int d = (int) sqrt(pow(x0-x, 2)+pow(y0-y, 2));                                      // calculate distance
		if (r-t/2 <= d && d <= r+t/2) img->px[x+y*img->width] = c; }}                       // set pixels
}

// draw a filled ellipse
void fillellipse(image *img, int x0, int y0, int rx, int ry, int c) {
	int N = min(max(y0+ry, 0), img->height-1), M = min(max(x0+rx, 0), img->width -1);           // draw range
	for (int y = min(max(y0-ry, 0), img->height-1); y <= N; y++) {                              // loop through y
	for (int x = min(max(x0-rx, 0), img->width -1); x <= M; x++) {                              // loop through x
		if (pow((x0-x)/(rx+1.), 2)+pow((y0-y)/(ry+1.), 2) <= 1) img->px[x+y*img->width] = c; }} // set pixels
}

// draw an ellipse
void ellipse(image *img, int x0, int y0, int rx, int ry, int t, int c) {
	int N = min(max(y0+ry+t/2, 0), img->height-1), M = min(max(x0+rx+t/2, 0), img->width -1);    // draw range
	for (int y = min(max(y0-ry-t/2, 0), img->height-1); y <= N; y++) {                           // loop through y
	for (int x = min(max(x0-rx-t/2, 0), img->width -1); x <= M; x++) {                           // loop through x
		if (!(pow((x0-x)/(rx-t/2.), 2)+pow((y0-y)/(ry-t/2.), 2) <= 1 ||                          // calculate distance
		1 <= pow((x0-x)/(rx+t/2.), 2)+pow((y0-y)/(ry+t/2.), 2))) img->px[x+y*img->width] = c; }} // set pixels
}

// return resized image
image *resize(image *img, int w, int h) {
	if (w < 1 || h < 1) return NULL;                                      // value range
	image *Img = newimage(w, h);                                          // new image pointer
	double kx = (double) img->width/w, ky = (double) img->height/h;       // resize factors
	for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) {           // loop through new pixels
		Img->px[x+y*w] = img->px[((int)(x*kx))+((int)(y*ky))*img->width]; // map pixels
	}} return Img;                                                        // return resized image
}

// return horizontally flipped image
image * hflip(image *img) {
	image *Img = newimage(img->width, img->height);                                // new image pointer
	for (int y = 0; y < img->height; y++) { for (int x = 0; x < img->width; x++) { // loop through pixels
		Img->px[x+(img->height-1-y)*img->width] = img->px[x+y*img->width];         // assign pixels (flipped)
	}} return Img;                                                                 // return flipped image
}

// return vertically flipped image
image *vflip(image *img) {
	image *Img = newimage(img->width, img->height);                                // new image pointer
	for (int y = 0; y < img->height; y++) { for (int x = 0; x < img->width; x++) { // loop through pixels
		Img->px[(img->width-1-x)+y*img->width] = img->px[x+y*img->width];          // assign pixels (flipped)
	}} return Img;                                                                 // return flipped image
}

// return right (90 degrees) rotated image
image *rrotate(image *img) {
	image *Img = newimage(img->height, img->width);                                // new image pointer
	for (int y = 0; y < img->height; y++) { for (int x = 0; x < img->width; x++) { // loop through pixels
		Img->px[(img->height-1-y)+x*img->height] = img->px[x+y*img->width];        // assign pixels (rotated)
	}} return Img;                                                                 // return rotated image
}

// return left (90 degrees) rotated image
image *lrotate(image *img) {
	image *Img = newimage(img->height, img->width);                                // new image pointer
	for (int y = 0; y < img->height; y++) { for (int x = 0; x < img->width; x++) { // loop through pixels
		Img->px[y+(img->width-1-x)*img->height] = img->px[x+y*img->width];         // assign pixels (rotated)
	}} return Img;                                                                 // return rotated image
}

// return half a revolution (180 degrees) rotated image
image *hrotate(image *img) {
	image *Img = newimage(img->width, img->height);                                       // new image pointer
	for (int y = 0; y < img->height; y++) { for (int x = 0; x < img->width; x++) {        // loop through pixels
		Img->px[(img->width-1-x)+(img->height-1-y)*img->width] = img->px[x+y*img->width]; // assign pixels (rotated)
	}} return Img;                                                                        // return rotated image
}

// cut out an image's portion
image *crop(image *img, int x0, int y0, int x1, int y1) {
	int w = x1-x0+1, h = y1-y0+1; if (w <= 0 || h <= 0) return NULL;     // dimension checking
	image *Img = newimage(w, h);                                         // new image pointer
	for (int y = y0; y <= y1; y++) { for (int x = x0; x <= x1; x++) {    // loop through pixels
		if (x >= 0 && x < img->width && y >= 0 && y < img->height)       // value bounds
			Img->px[(x-x0)+(y-y0)*Img->width] = img->px[x+y*img->width]; // set pixel
	}} return Img;                                                       // return image
}

// blit an image onto another one
void blit(image *img, image *Img, int x0, int y0) {
	int N = y0+Img->height, M = x0+Img->width;                              // Img dimensions in img
	for (int y = y0; y < N; y++) { for (int x = x0; x < M; x++) {           // loop through pixels
		if (x >= 0 && x < img->width && y >= 0 && y < img->height)          // value bounds
			img->px[x+y*img->width] = Img->px[(x-x0)+(y-y0)*Img->width]; }} // set pixel
}





/* ============================ *
 *  III) BITMAP FILE FUNCTIONS  *
 * ============================ */

// read a bitmap file
image *readbmp(char fn[]) {
	FILE *f = fopen(fn, "r"); if (f == NULL) return NULL;            // file pointer, file read error
	fseek(f, 0, SEEK_END); int s = ftell(f); byte *data = malloc(s); // seek file end, get file size, allocate memory
	fseek(f, 0, SEEK_SET); fread(data, 1, s, f); fclose(f);          // read file contents

	int k = 54;                                                                                       // header size
	if (s < k)                                                                           return NULL; // file size missmatch
	int w = (data[21]<<24)|(data[20]<<16)|(data[19]<<8)|data[18];                                     // image pixel width
	int h = (data[25]<<24)|(data[24]<<16)|(data[23]<<8)|data[22];                                     // image pixel height
	int b = (data[37]<<24)|(data[36]<<16)|(data[35]<<8)|data[34];                                     // bitmap data size
	if (!(data[ 0] == 0x42 && data[ 1] == 0x4D                                        )) return NULL; // magic number missmatch
	if (!(data[10] ==    k && data[11] == 0x00 && data[12] == 0x00 && data[13] == 0x00)) return NULL; // bitmap data offset missmatch
	if (!(data[14] == 0x28 && data[15] == 0x00 && data[16] == 0x00 && data[17] == 0x00)) return NULL; // DIB size missmatch
	if (!(data[26] == 0x01 && data[27] == 0x00                                        )) return NULL; // not one color plane
	if (!(data[28] == 0x18 && data[29] == 0x00                                        )) return NULL; // not 24 bit color depth
	if (!(data[30] == 0x00 && data[31] == 0x00 && data[32] == 0x00 && data[33] == 0x00)) return NULL; // compression missmatch
	                                                                                                  // DPI (h and v, 8 bytes) get ignored
	if (!(data[46] == 0x00 && data[47] == 0x00 && data[48] == 0x00 && data[49] == 0x00)) return NULL; // palette missmatch
	if (!(data[50] == 0x00 && data[51] == 0x00 && data[52] == 0x00 && data[53] == 0x00)) return NULL; // color importance missmatch
	if (s != k+b)                                                                        return NULL; // file size missmatch

	image *img = newimage(w, h); int i = 0;                            // image pointer, bitmap data pointer
	int pad = (4-(3*w)%4)*((3*w)%4!=0);                                // number of bytes to pad each pixel line to a multiple of four bytes
	for (int y = h-1; y >= 0; y--) { for (int x = 0; x < w; x++) {     // loop through data, read pixel values
		img->px[x+y*w] = data[k+i]|(data[k+i+1]<<8)|(data[k+i+2]<<16); // read three color bytes
		i += 3; } i += pad; }                                          // advance counter, skip padded bytes

	free(data); return img; // free data, return image pointer
}

// save a bitmap file
void savebmp(image *img, char fn[]) {
	int w = img->width, h = img->height; // one-letter variables for image dimensions
	int pad = (4-(3*w)%4)*((3*w)%4!=0);  // number of bytes to pad each pixel line to a multiple of four bytes
	int k = 54;                          // header size (bitmap header size + DIB header size)
	int b = (3*w+pad)*h, s = k+b;        // bitmap data size, file size (complete)
	byte *data = malloc(s);              // data array pointer

	int i = 0; // data index, fill bitmap data portion
	for (int y = h-1; y >= 0; y--) { for (int x = 0; x < w; x++) {
			int p = img->px[x+y*w];         // get pixel integer
			data[k+i  ] = (p&0xff    )    ; // blue byte
			data[k+i+1] = (p&0xff00  )>> 8; // green byte
			data[k+i+2] = (p&0xff0000)>>16; // red byte
			i += 3; }                       // advance counter

		// pad to a multiple of four bytes
		for (int pd = 0; pd < pad; ++pd) { data[k+i] = 0; i++; }
	}

	// file header bytes
	char S[] = {s&0xff, (s&0xff00)>>8, (s&0xff0000)>>16, (s&0xff000000)>>24}; // file size
	char K[] = {k&0xff, (k&0xff00)>>8, (k&0xff0000)>>16, (k&0xff000000)>>24}; // header size (also bitmap data offset)
	char W[] = {w&0xff, (w&0xff00)>>8, (w&0xff0000)>>16, (w&0xff000000)>>24}; // pixel height
	char H[] = {h&0xff, (h&0xff00)>>8, (h&0xff0000)>>16, (h&0xff000000)>>24}; // pixel width
	char B[] = {b&0xff, (b&0xff00)>>8, (b&0xff0000)>>16, (b&0xff000000)>>24}; // bitmap byte size

	// file header
	char header[] = {
		/*  bitmap file header  */
		0x42, 0x4D,             // BM
		S[0], S[1], S[2], S[3], // file size
		0x00, 0x00, 0x00, 0x00, // unused
		K[0], K[1], K[2], K[3], // bitmap data offset
		/*      DIB header      */
		0x28, 0x00, 0x00, 0x00, // DIB size
		W[0], W[1], W[2], W[3], // pixel width
		H[0], H[1], H[2], H[3], // pixel height
		0x01, 0x00,             // one color plane
		0x18, 0x00,             // 24 bit color depth
		0x00, 0x00, 0x00, 0x00, // no compression
		B[0], B[1], B[2], B[3], // bitmap data size
		0x23, 0x2E, 0x00, 0x00, // 300 DPI (horizontal)
		0x23, 0x2E, 0x00, 0x00, // 300 DPI (vertical)
		0x00, 0x00, 0x00, 0x00, // no palette
		0x00, 0x00, 0x00, 0x00  // color importance
	};
	for (int i = 0; i < k; ++i) { data[i] = header[i]; } // put header into file
	FILE *f = fopen(fn, "w"); if (f == NULL) return;     // file pointer, file cannot be written
	fwrite(data, 1, s, f); fclose(f); free(data);        // write file, close file, free memory
}





/* ======================= *
 *  IV) UTILITY FUNCTIONS  *
 * ======================= */

int min (int a, int b) { return a < b ?  a : b; } // return minimum of a, b
int max (int a, int b) { return a > b ?  a : b; } // return maximum of a, b
int mean(int a, int b) { return (a+b)/2       ; } // return mean of a, b
int abs (int a       ) { return a < 0 ? -a : a; } // return abs of a





/* ====================== *
 *  V) TESTING FUNCTIONS  *
 * ====================== */

void graphic_test1() {
	image *i = newimage(64, 48);
	fill(i, 0x3a0b5e);

	setpx(i, 1, 1, RED);

	hline(i, 3, 62, 1, BLACK);
	vline(i, 1, 3, 46, BLACK);
	line(i, 3, 3, 10, 46, BLACK);
	line(i, 3, 3, 62, 10, BLACK);

	rect(i, 9, 7, 19, 17, 0x550000);
	fillrect(i, 11, 9, 17, 15, 0xff0000);
	rect(i, 21, 7, 23, 17, 0x110000);
	rect(i, 9, 19, 23, 21, 0x330000);

	fillcircle(i, 45, 29, 13, 0x330000);
	fillcircle(i, 45, 29, 11, 0x550000);
	fillcircle(i, 45, 29,  9, 0x770000);
	fillcircle(i, 45, 29,  7, 0x990000);
	fillcircle(i, 45, 29,  5, 0xbb0000);
	fillcircle(i, 45, 29,  3, 0xdd0000);
	fillcircle(i, 45, 29,  1, 0xff0000);
	circle(i, 45, 29, 17, 1, BLACK);

	line(i, 18-3, 35, 18+3, 35, 0x18003d);
	line(i, 18, 35-7, 18, 35+7, 0x18003d);
	ellipse(i, 18, 35, 3, 7, 1, RED);
	ellipse(i, 18, 35, 7, 11, 1, BLACK);
	line(i, 18, 35, 18, 35, 0xff0000);

	image *I = resize(i, i->width*10, i->height*10);
	freeimage(i);
	savebmp(I, "drw.bmp");
	freeimage(I);
}

void graphic_test2() {
	image *i = newimage(7, 11);
	fill(i, 0x002953);
	line(i, 3, 0, 0, 3, WHITE);
	line(i, 3, 0, 6, 3, WHITE);
	line(i, 0, 7, 3, 10, WHITE);
	line(i, 3, 10, 6, 7, WHITE);
	line(i, 2, 4, 4, 6, WHITE);
	setpx(i, 1, 5, RED);
	setpx(i, 5, 5, RED);

	image *ri = rrotate(i);
	image *li = lrotate(i);
	image *hi = hrotate(i);

	image *I = newimage(7+1+11, 1+11+1+7);
	fill(I, 0x6aca30);
	blit(I, i, 0, 1+0);
	blit(I, ri, 8, 1+0);
	blit(I, hi, 12, 1+8);
	blit(I, li, 0, 1+12);
	freeimage(i); freeimage(ri); freeimage(li); freeimage(hi);

	image *II = newimage(I->width*2+1, I->height*2+1);
	fill(II, 0xaaaaaa);
	image *vI = vflip(I);
	blit(II, I, 0, 0);
	blit(II, vI, 20, 0);
	image *hI = hflip(I);
	image *hvI = hflip(vI);
	blit(II, hI, 0, 21);
	blit(II, hvI, 20, 21);
	freeimage(I); freeimage(vI); freeimage(hI); freeimage(hvI);

	image *resized = resize(II, II->width*10, II->height*10);
	freeimage(II);
	savebmp(resized, "flp.bmp");

	setpx(resized, 47, 50, YELLOW);
	setpx(resized, 183, 291, YELLOW);
	image *cropped = crop(resized, 47, 50, 183, 291);
	freeimage(resized);
	savebmp(cropped, "crp.bmp");
	freeimage(cropped);
}

void graphic_test3_line(image *img, int x0, int y0, int x1, int y1) {
	line(img, x0, y0, x1, y1, WHITE);
	setpx(img, x0, y0, RED); setpx(img, x1, y1, RED);
}

void graphic_test3() {
	image *img = newimage(1000, 1000);
	graphic_test3_line(img, 362, 211, 644, 724);
	graphic_test3_line(img, 457, 221, 249, 622);
	graphic_test3_line(img, 831, 132, 899, 576);
	graphic_test3_line(img, 544, 914, 608, 760);
	graphic_test3_line(img, 579, 206, 534, 234);
	graphic_test3_line(img, 993, 676, 131, 565);
	graphic_test3_line(img, 111, 792, 183, 470);
	graphic_test3_line(img, 496, 731, 679, 409);
	graphic_test3_line(img, 589, 446, 778, 676);
	graphic_test3_line(img, 852, 592, 819, 780);
	graphic_test3_line(img, 661, 855, 466, 880);
	graphic_test3_line(img, 193, 9, 561, 783);
	graphic_test3_line(img, 589, 259, 461, 827);
	graphic_test3_line(img, 9, 180, 674, 950);
	graphic_test3_line(img, 713, 809, 404, 379);
	graphic_test3_line(img, 504, 301, 104, 928);
	graphic_test3_line(img, 666, 404, 539, 146);
	graphic_test3_line(img, 301, 855, 84, 475);
	graphic_test3_line(img, 15, 677, 805, 96);
	graphic_test3_line(img, 209, 337, 412, 896);
	graphic_test3_line(img, 968, 577, 402, 875);
	graphic_test3_line(img, 284, 654, 813, 628);
	graphic_test3_line(img, 178, 532, 557, 470);
	graphic_test3_line(img, 925, 250, 218, 161);
	graphic_test3_line(img, 494, 455, 199, 995);
	graphic_test3_line(img, 792, 297, 751, 493);
	graphic_test3_line(img, 505, 774, 932, 60);
	graphic_test3_line(img, 879, 825, 515, 279);
	graphic_test3_line(img, 242, 775, 423, 178);
	graphic_test3_line(img, 383, 957, 422, 925);

	//image *resized = resize(img, img->width*10, img->height*10);
	//freeimage(img);
	savebmp(img, "lin.bmp");
	freeimage(img);
}

/* END OF FILE */
