/**
 *
 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
 *
 * 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; either version 2
 * of the License, or (at your option) any later version. The Blender
 * Foundation also sells licenses for use in proprietary software under
 * the Blender License.  See http://www.blender.org/BL/ for information
 * about this.
 *
 * 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 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
 * ham.c
 *
 * $Id: ham.c,v 1.5 2003/12/04 18:18:05 sirdude Exp $
 */

#include "BLI_blenlib.h"

#include "imbuf.h"
#include "imbuf_patch.h"
#include "IMB_imbuf_types.h"
#include "IMB_imbuf.h"
#include "IMB_cmap.h"
#include "IMB_hamx.h"
#include "IMB_ham.h"

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

extern short alpha_col0;

#define HAMB	0x0100
#define HAMG	0x0400
#define HAMR	0x0200
#define HAMC	0x1000
#define HAMFREE	0x2000

static void addhamdither(short x, unsigned char *dit,
				  short dmax, unsigned char *rgb,
				  unsigned short *ham,
				  short type, short round, short shift)
{
	short dx = 0;
	short c1, c2;

	for (;x>0;x--){
		if (ham[0] & (HAMFREE | type)){
			c2 = c1 = *rgb;
			
			/* wrap dither */
			while (dx >= dmax) dx -= dmax;
			
			c1 += dit[dx];
			if (c1 > 255) c1 = 255;
			c2 += round;
			if (c2 > 255) c2 = 255;
			
			if (c1 != c2){
				c1 >>= shift; c2 >>= shift;
				if (ham[1] & HAMFREE){
					ham[0] = type + c1;
					ham[1] = type + c2;
				} else if (ham[1] & type){
					ham[0] = type + c1;
				} else if ((ham[2] & (type | HAMFREE)) == type){
					ham[0] = type + c1;
				} else if ((ham[1] & HAMC) | (ham[2] & HAMC)){
					ham[0] = type + c1;
				}
			}
		}
		rgb += 4;
		ham ++;
		dx ++;
	}
}

static void convhamscanl(short x, short y,
				  unsigned char *rgbbase,
				  unsigned char coltab[][4],
				  short *deltab,
				  short bits)
{
	int a, r, g, b, lr, lg, lb, dr, dg, db, col, fout, type, x2;
	int round, shift;
	uchar *rgb, dit[2];
	unsigned short *ham, *hambase;

	/* Concept:
		first we check the entire image, where color transitions are coded: FGRB XXXX XXXX
		F			- free color value, can be changed by anyone
		G/R/B		- green/red/blue ham transition, only to be changed by this color
		XXXX XXXX	- N bits value.
	
		0000 XXXX XXXX is palette color.
	
		after that first the green dither is added, then the red dither, and finally blue dither
	*/

	if ((hambase = (unsigned short *) malloc((x+4) * sizeof(unsigned short)))==0) return;

	lb = coltab[0][1];
	lg = coltab[0][2];
	lr = coltab[0][3];
	type = col = 0;

	ham = hambase;
	rgb = rgbbase;
	
	shift = 8 - bits;
	round = 1 << (shift - 1);
	
	/* to prevent 'noise' at the end of the line */
	for (x2 = 3; x2 >= 0; x2 --) hambase[x + x2] = HAMFREE;
	
	for (x2 = x ;x2 > 0; x2--){
		r = rgb[0] + round;
		g = rgb[1] + round;
		b = rgb[2] + round;
		a = rgb[3];
		
		if (a < 128 && alpha_col0) {
			a = 1;
		} else a = 0;
		
		if (b > 255) b = 255;
		if (g > 255) {
			g = 255;
		}
		if (r > 255) r = 255;

		r >>= shift;
		g >>= shift;
		b >>= shift;

		if ((b-lb) | (g-lg) | (r-lr) | a){
			if (a) {
				col = 0;
				type = HAMC;
			} else {
				col = ((b << (2 * bits)) + (g << bits) + r) << 1;
				fout = deltab[col + 1];
				col = deltab[col];
				type = HAMC;
				
				dr = quadr[lr-r];
				dg = quadr[lg-g];
				db = quadr[lb-b];
	
				if ((dr+dg) <= fout){
					fout = dr+dg;
					col = b;
					type = HAMB;
				}
				if ((dg+db) <= fout){
					fout = dg+db;
					col = r;
					type = HAMR;
				}
				if ((dr+db) <= fout){
					fout = dr+db;
					col = g;
					type = HAMG;
				}
			}
			
			switch(type){
			case HAMG:
				lg = g;
				break;
			case HAMR:
				lr = r;
				break;
			case HAMB:
				lb = b;
				break;
			default:
				lb = coltab[col][1];
				lg = coltab[col][2];
				lr = coltab[col][3];
			}
			*ham = type + col;
		} else *ham = HAMG + HAMFREE + g;

		rgb += 4;
		ham ++;
	}


	if (y & 1){
		dit[0] = 0 << (shift - 2);
		dit[1] = 3 << (shift - 2);
	} else {
		dit[0] = 2 << (shift - 2);
		dit[1] = 1 << (shift - 2);
	}

	addhamdither(x,dit,2,rgbbase+2,hambase,HAMG, round, shift);

	if ((y & 1)==0){
		dit[0] = 3 << (shift - 2);
		dit[1] = 0 << (shift - 2);
	} else {
		dit[0] = 1 << (shift - 2);
		dit[1] = 2 << (shift - 2);
	}
	
	addhamdither(x,dit,2,rgbbase+3,hambase,HAMR, round, shift);
	addhamdither(x,dit,2,rgbbase+1,hambase,HAMB, round, shift);


	ham = hambase;
	rgb = rgbbase;
	rgb += 3;

	for (x2=x;x2>0;x2--){
		type = *(ham++);
		if (type & HAMG) type |= HAMR | HAMB;

		*rgb = (type & 0xff) | ((type & (HAMR | HAMB)) >> shift);
		rgb += 4;
	}

	free (hambase);
}


short imb_converttoham(struct ImBuf *ibuf)
{
	unsigned int coltab[256],*rect;
	short x,y,* deltab;
	int mincol;
	
	memcpy(coltab,ibuf->cmap,4 * ibuf->maxcol);

	mincol = ibuf->mincol;	
	if (alpha_col0 && mincol == 0) mincol = 1;

	if (ibuf->ftype == AN_hamx) {
		deltab = imb_coldeltatab((uchar *) coltab, 0, ibuf->maxcol, 4);
	} else {
		ibuf->cbits = ibuf->depth - 2;
		imb_losecmapbits(ibuf, coltab);
		deltab = imb_coldeltatab((uchar *) coltab, mincol, ibuf->maxcol, ibuf->cbits);
	}
	
	rect = ibuf->rect;
	x=ibuf->x;
	y=ibuf->y;

	if (ibuf->ftype == AN_hamx){
		IMB_dit2(ibuf, 2, 4);
		IMB_dit2(ibuf, 1, 4);
		IMB_dit2(ibuf, 0, 4);
		imb_convhamx(ibuf, coltab, deltab);
	} else {
		for(;y > 0; y--){
			convhamscanl(x, y, (uchar *)rect, coltab, deltab, ibuf->cbits);
			rect += x;
		}
	}

	return (TRUE);
}
