/*
 * Build colour map and order dither matrix based on display parameters.
 * 
 */

#include <stdio.h>
#include <math.h>


/*
 * MAKEDTAB  --  Create a 16 by 16 ordered dither table, a gamma corrected
 * colour map compatible with it, and lookup tables for efficiently mapping
 * RGB values through it.
 * 
 * LEVELS     Number of intensity levels per primary colour.  LEVELS^3 must be
 * less than 256, hence LEVELS is usually set to 6, using 216 colour map
 * slots.
 * 
 * GAMMA      Display gamma.  1 means no correction. Try values between 1 and 2
 * for most monitors.
 * 
 * RGBMAP     Colour map, addressed rgbmap[index][3] where the second subscript
 * is 0 for red, 1 for green, and 2 for blue.  LEVELS^3 entries will be
 * filled.
 * 
 * DIVTAB     Quotient table for fast access to dither matrix.
 * 
 * MODTAB     Remainder table for fast access to dither matrix.
 * 
 * DITH16     16 by 16 ordered dither matrix.  Generated by squaring a 4 by 4
 * compiled-in matrix.
 */

void makedtab(int levels, double gamma, int rgbmap[][3],
	       int divtab[256], int modtab[256], int dith16[16][16])
{
	int i, j, k, l, levels2, levels3;
	double n, dithfact;
	int gammamap[256];
	static int dither4[4][4] = {
		0, 14, 3, 13,
		11, 5, 8, 6,
		12, 2, 15, 1,
		7, 9, 4, 10
	};

	for (i = 0; i < 256; i++)
	{
		gammamap[i] = 0.5 + 255 * pow(i / 255.0, 1.0 / gamma);
	}

	levels2 = levels * levels;
	levels3 = levels * levels2;

	n = 255.0 / (levels - 1);	/* Get size of each step */

	/*
	 * Now build the gamma corrected colour map entries for the level^3
	 * items indexed by the ordered dither matrix.
	 */

	for (i = 0; i < levels3; i++)
	{
		rgbmap[i][0] = gammamap[(int) (0.5 + (i % levels) * n)];
		rgbmap[i][1] = gammamap[(int) (0.5 + ((i / levels) % levels) * n)];
		rgbmap[i][2] = gammamap[(int) (0.5 + ((i / levels2) % levels) * n)];
	}

	/*
	 * Initialise the lookup tables used to perform fast divide and
	 * modulo operations when we're accessing the dither matrix.
	 */

	for (i = 0; i < 256; i++)
	{
		divtab[i] = i / n;
		modtab[i] = i - ((int) (n * divtab[i]));
	}

	/*
	 * Construct the 16 by 16 ordered dither matrix by squaring the basic
	 * 4 x 4 matrix.  Love those nested FOR loops!
	 */

	dithfact = (n - 2) / 16.0;
	for (i = 0; i < 4; i++)
		for (j = 0; j < 4; j++)
			for (k = 0; k < 4; k++)
				for (l = 0; l < 4; l++)
					dith16[4 * k + i][4 * l + j] =
						0.5 + dither4[i][j] * dithfact +
						(dither4[k][l] / 16.0) * dithfact;

}
