#include <allegro.h>

/*
  Welcome to my color mapping code. This isn't a "real" source file; you're
  expected to cut 'n' paste the appropriate sections into your own file.
  Here's how it all works: the function make_map_slice() fills in a slice
  of the color map by applying a color, an opacity, and a blending mode
  to all the colors in the palette. By using a few loops, you can create
  a color map with various special effects. For instance, the example code
  included below generates a color map that does additive darkening for
  pallete indeces 0-63, additive brightening for 64-127, "fire" (red-yellow-
  white gradient with increasing opacity) for 128-191, and "electricity" for
  192-256. If I drew an explosion using colors 128 through 191, I could
  draw it using draw_trans_sprite and have a cool-looking explosion.

  Down to the nuts and bolts: Add the section marked "prototypes" to the top
  of your source file. Add the section marked "color map routines" to the
  bottom of your source file. Somewhere in between, write a function to
  generate a color map.
*/

/*
  Begin Prototypes
*/

typedef struct
{
	int r;
    int g;
    int b;
} RGBI;

//Blending modes
#define M_NULL      0   //Don't do anything
#define M_ADD       1   //Add the supplied color to the colors in the palette
#define M_MERGE     2   //Average the colors, using o as the opacity
#define M_MULT      3   //Multiply colors (0 = 0.0, 255 = 1.0). Can use opacity.
#define M_CROSS     4   //Take cross product of colors. Can use opacity.
#define M_TINT      5   //Combine the color (hue & sat) from src with lum. from dest
#define M_GRAY      6   //Make all colors greyscale
#define M_GRAD      7   //Explanation follows

/*
  What M_GRAD Does:
  The M_GRAD mode differs from the other modes in that r,g, & b do not
  specify a color, but instead are treated as pallete indices.
  R and G specify a range of colors in the palette. A color is selected
  from this range based on the luminostiy of dest. Black will result in palette
  index R being returned, White results in G. Inbetween colors return values
  in between R and G. B is added to the index selected, to give you a chance to
  "brighten" the color. The opacity works as you'd expect.
*/


/*
  Fill in map[src][0...256] with the result of combining the color
  {r,g,b} (given in 8 bit color depth) with the palette p, using opacity o,
  and the specified blending mode.
*/
void make_map_slice(COLOR_MAP &map, PALETTE p, int src, int r, int g, int b, int o, int mode);

/* End Prototypes */


/*
  Example Code
*/
PALETTE pal;
COLOR_MAP my_map;

/*
  This routine fills in the color map as noted above, with 4 different special
  effects.
*/
void setup_colormap(void)
{
	int i,t;
    int r,g,b;

    /* Create darken section */
    for(i = 0; i < 64; i++)
    	make_map_slice(my_map,(PALETTE)fix_dat[FD_DEFAULT_PAL].dat,i,-i*4,-i*4,-i*4,255,M_ADD); //darken

    /* Create lighten section */
    for(i = 64; i < 128; i++)
    {
    	t = i - 64;
    	make_map_slice(my_map,(PALETTE)fix_dat[FD_DEFAULT_PAL].dat,i,t*4,t*4,t*4,255,M_ADD); //lighten
    }

    /* Create "Fire" */
    for(i = 128; i < 192; i++)
    {
    	t = i - 128;
        if(t < 20)
        {
        	r = 12*t; g = 0; b = 0;
        }
        else if(t >= 20 && t < 40)
        {
        	r = 255; g = (t-20)*12; b = 0;
        }
        else
        {
        	r = 255; g = 255; b = (int)((t-40)*10.5);
        }

    	make_map_slice(my_map,(PALETTE)fix_dat[FD_DEFAULT_PAL].dat,i,r,g,b,t*(t/12),M_ADD); //Fire
    }

    /* Create "electricity" */
    for(i = 192; i < 256; i++)
    {
    	t = i - 192;
    	make_map_slice(my_map,(PALETTE)fix_dat[FD_DEFAULT_PAL].dat,i,t*(t/16),t*3,t*4,t,M_ADD); //lighten
    }

    //Tell Allegro to use the color map
    color_map = &my_map;

}

/* End Example */

/*
  Begin Color Map Routines
*/

#define RGB_DIST2(x,y) ( (((x).r-(y).r)*((x).r-(y).r)) + (((x).g-(y).g)*((x).g-(y).g)) + (((x).b-(y).b)*((x).b-(y).b)) )
RGBI RGB_ADD(RGBI src, RGBI dest);
RGBI RGB_MERGE(RGBI src, RGBI dest, int o);
RGBI RGB_GRAY(RGBI src, RGBI dest, int o);
RGBI RGB_TINT(RGBI src, RGBI dest, int o);
RGBI RGB_CROSS(RGBI src, RGBI dest, int o);
RGBI scale_rgb(RGBI v);
int find_in_pal(PALETTE p, RGBI c, char usage[]);

RGBI RGB_ADD(RGBI src, RGBI dest)
{
	RGBI ret;
    ret.r = src.r + dest.r;
    ret.g = src.g + dest.g;
    ret.b = src.b + dest.b;

    return ret;
}


RGBI RGB_MERGE(RGBI src, RGBI dest, int o)
{
	RGBI ret;
    ret.r = o*src.r/255 + (255-o)*dest.r/255;
    ret.g = o*src.g/255 + (255-o)*dest.g/255;
    ret.b = o*src.b/255 + (255-o)*dest.b/255;

    return ret;
}

RGBI RGB_TINT(RGBI src, RGBI dest, int o)
{
	RGBI ret,temp;
    int lum;

    lum = (dest.r + dest.g + dest.b)/3;
    temp.r = (src.r*lum)/255;
    temp.g = (src.g*lum)/255;
    temp.g = (src.b*lum)/255;

    ret = RGB_MERGE(src,temp,o);
    return ret;
}

RGBI RGB_GRAY(RGBI src, RGBI dest, int o)
{
	RGBI temp,ret;
    int lum;
    lum = (dest.r + dest.g + dest.b)/255;
    temp.r = temp.g = temp.b = lum;

    ret = RGB_MERGE(dest,temp,o);
    return ret;
}


RGBI RGB_MULT(RGBI src, RGBI dest, int o)
{
	RGBI ret,temp;
    temp.r = (src.r * dest.r)/255;
    temp.g = (src.g * dest.g)/255;
    temp.b = (src.b * dest.b)/255;

    ret = RGB_MERGE(src,temp,o);
    return ret;
}

RGBI RGB_CROSS(RGBI src, RGBI dest, int o)
{
	RGBI ret, temp;
    temp.r = src.g*dest.b - src.b*dest.g;
    temp.g = -(src.r*dest.b - src.b*dest.r);
    temp.b = src.r*dest.g - src.g*dest.r;

    ret = RGB_MERGE(src,temp,o);
    return ret;
}

//Make sure elements are in range
RGBI scale_rgb(RGBI v)
{
	RGBI ret;
    ret.r = v.r > 255 ? 255 : (v.r < 0 ? 0 : v.r);
    ret.g = v.g > 255 ? 255 : (v.g < 0 ? 0 : v.g);
    ret.b = v.b > 255 ? 255 : (v.b < 0 ? 0 : v.b);

    return ret;
}

//Searches the palette for the appropriate color
int find_in_pal(PALETTE p, RGBI c, char usage[])
{
	int ret, i, min, dist;
    RGB temp;

    min = 1000000;
    ret = 0;

    c = scale_rgb(c);

    temp.r = c.r/4; temp.g = c.g/4; temp.b = c.b/4;

    for(i = 0; i < 256; i++)
    {
    	dist = RGB_DIST2(temp,p[i]);
        if(dist < min)
        	if(!usage || usage[i])
            {
        		min = dist;
            	ret = i;
            }
    }

    return ret;
}

void make_map_slice(COLOR_MAP &map, PALETTE p, int src, int r, int g, int b, int o, int mode)
{
	int i;
    RGBI sr, dest, mix;
    int t;
    int lum;

    sr.r = r; sr.g = g; sr.b = b;

    for(i = 0; i < 256; i++)
    {
    	dest.r = p[i].r; dest.g = p[i].g; dest.b = p[i].b;
        dest.r *= 4; dest.g *= 4; dest.b *= 4;

        switch(mode)
        {
        	case M_NULL:
    			t = i;
                break;
            case M_ADD:
            	mix = RGB_ADD(dest,sr);
                t = find_in_pal(p,mix,NULL);
                break;
            case M_MERGE:
            	mix = RGB_MERGE(sr,dest,o);
                t = find_in_pal(p,mix,NULL);
                break;
            case M_MULT:
            	mix = RGB_MULT(sr,dest,o);
                t = find_in_pal(p,mix,NULL);
                break;
            case M_CROSS:
            	mix = RGB_CROSS(sr,dest,o);
                t = find_in_pal(p,mix,NULL);
                break;
            case M_TINT:
            	mix = RGB_TINT(sr,dest,o);
                t = find_in_pal(p,mix,NULL);
                break;
            case M_GRAY:
            	mix = RGB_GRAY(sr,dest,o);
                t = find_in_pal(p,mix,NULL);
                break;
            case M_GRAD:
            	lum = (dest.r + dest.g + dest.b)/3;
                t = lum*(g - r)/255 + r + b*(g - r)/255;
                t = t > g ? g : t;
                sr.r = p[t].r * 4;
                sr.g = p[t].g * 4;
                sr.b = p[t].b * 4;
                mix = RGB_MERGE(sr,dest,o);
                t = find_in_pal(p,mix,NULL);
                break;
        }

        map.data[src][i] = t;
    }
}

/* End Color Map Routines */

/*
  Final Notes:
  Obviously, this isn't a finished product. For one thing, it runs really
  slowly. You'll probably want to save your color map to a file after you
  generate it, and then just load it. More blending mode might be useful,
  although the only other one I can think of that I would need would be
  "tinting" the pallete.

  I can be contacted at:
  andyclifton@cs.com
  http://ourworld.cs.com/andyclifton/

  This code may be considered public domain.
*/
