/********************************************************************
 *																	*
 *	Color icon manipulation and drawing routines					*
 *	by Markus Gutschke, modified by Ken Hollis 						*
 *																	*
 *	Copyright (C) 1994, Bitgate Software, Clever Bits and Markus	*
 *						Gutschke									*
 *	All rights reserved.											*
 *																	*
 *	Note: These routines were documented, and are EXTREMELY			*
 *	difficult to understand without having extensive knowledge of	*
 *	VDI.  Do NOT attempt to modify these routines unless you		*
 *	ABSOLUTELY know what you're doing.								*
 *																	*
 ********************************************************************
 *																	*
 *	Update log:														*
 *		*NONE*														*
 *																	*
 ********************************************************************/

#include <stddef.h>
#include <string.h>
#include "winlib.h"

LOCAL int intersect(int  x1,int  y1,int  w1,int  h1,
		            int  x2,int  y2,int  w2,int  h2,
		            int *x3,int *y3,int *w3,int *h3)
{
	*x3	= max(x1,x2);
	*y3	= max(y1,y2);
	*w3	= min(x1+w1-1,x2+w2-1) - *x3 + 1;
	*h3	= min(y1+h1-1,y2+h2-1) - *y3 + 1;
	return ((*w3 > 0) && (*h3 > 0));
}

void FixColIcon(ICONBLK *iconBlk,int planes,short *sData,
                short *sMask,short *dData,short *dMask,
                short *dtData,short *dtMask,short *cnvTab)
{
	static int colcnv1[]	= {0,1};
	static int colcnv2[]	= {0,2,3,1};
	static int colcnv3[]	= {0,2,3,6,4,7,5,1};
	static int colcnv4[]	= {0,2,3,6,4,7,5,8,9,10,11,14,12,15,13,1};
	static int *colcnv[4]	= {colcnv1,colcnv2,colcnv3,colcnv4};
	static MFDB colIndex	= {0,0,0,0,0,0,0,0,0};
	static int oldPlanes	= 0,xferMode = 0;
	static short *oldCnvTabPtr = NULL,oldCnvTab[16];
	long		bitPlaneSize = 2L * ((iconBlk->ib_wicon + 15) / 16L)*
	                           (long)iconBlk->ib_hicon;
	MFDB		pixel,source,dest,tmpIndex;
	unsigned	x,y,b,p,i,j,tPlanes;
	int			vdiHandle,pxy[8],col[2];
	short		ditherBits,curDither,*sPtr,*dPtr,*mskPtr,*tmpBuf;
	int			bitMapSize,colTabSize;

	if (planes == 1) {
		memmove(dMask,sMask,bitPlaneSize);
		memmove(dData,sData,bitPlaneSize);
		if (dtMask) {
			memmove(dtMask,sMask,bitPlaneSize);
			mskPtr = dMask;
			sPtr   = dData;
			dPtr   = dtData;
			for (y = 0; y < iconBlk->ib_hicon; y++) {
				ditherBits = 0x5555 << (y & 1);
				for (x = (iconBlk->ib_wicon + 15) / 16; x--; ) {
					curDither = ditherBits & *mskPtr++;
					*dPtr++ = *sPtr++ | curDither;
				}
			}
		}
		return;
	}

	vdiHandle = VDIhandle;
	memmove(dMask,sMask,bitPlaneSize);

	tPlanes				= planes < 8 && cnvTab != NULL ? 8 : planes;
	source.fd_addr		= sData;
	source.fd_w			= iconBlk->ib_wicon;
	source.fd_h			= iconBlk->ib_hicon;
	source.fd_wdwidth	= (iconBlk->ib_wicon + 15) / 16;
	source.fd_stand		= 1;
	source.fd_nplanes	= num_planes /* planes */ /***/;
	source.fd_r1		=
	source.fd_r2		=
	source.fd_r3		= 0;
	dest				= source;
	dest.fd_addr		= dData;
	dest.fd_stand		= 0;
	dest.fd_nplanes		= num_planes;
	bitMapSize			= num_planes * (((1 << tPlanes) + 15) & ~15) / 8;
	colTabSize			= (int)sizeof(short) * (1 << tPlanes);

	tmpBuf = malloc(num_planes * bitPlaneSize * (dtMask ? 2 : 1));
	if (colIndex.fd_addr == NULL || oldPlanes != tPlanes ||
	    oldCnvTabPtr != cnvTab ||
	    (cnvTab != NULL && memcmp(oldCnvTab,cnvTab,sizeof(oldCnvTab)))) {
		if (colIndex.fd_addr != 0)
			free(colIndex.fd_addr);
	retry:
		if ((colIndex.fd_addr = malloc(2 * max(bitMapSize,colTabSize)))==NULL) {
		    if (tmpBuf) {
		    	free(tmpBuf);
				tmpBuf = NULL;
				goto retry;
			}

		    memset(dData,0,(long)num_planes * bitPlaneSize);
			memmove(dData,dMask,(long)bitPlaneSize);
			vr_trnfm(vdiHandle,&source,&dest);
			return;
		}

		if (cnvTab != NULL)
			memmove(oldCnvTab,cnvTab,sizeof(oldCnvTab));

		oldPlanes			= tPlanes;
		oldCnvTabPtr		= cnvTab;
		pixel.fd_addr		= "\xFF";
		pixel.fd_w			=
		pixel.fd_h			=
		pixel.fd_wdwidth	=
		pixel.fd_stand		=
		pixel.fd_nplanes	= 1;
		pixel.fd_r1			=
		pixel.fd_r1			=
		pixel.fd_r1			= 0;
		colIndex.fd_w		= 1L << tPlanes;
		colIndex.fd_h		= 1;
		colIndex.fd_wdwidth	= (colIndex.fd_w+15)/16;
		colIndex.fd_stand	= 0;
		colIndex.fd_nplanes	= num_planes;
		colIndex.fd_r1		=
		colIndex.fd_r2		=
		colIndex.fd_r3		= 0;

		for (i = (unsigned)(1L << tPlanes); i--;) {
			pxy[0] = pxy[1] = pxy[2] = pxy[3] = pxy[5] = pxy[7] = 0;
			pxy[4] = pxy[6] = (int)i;
			col[0] = i; col[1] = 0;
			vrt_cpyfm(vdiHandle,MD_REPLACE,pxy,&pixel,&colIndex,col);
		}

		tmpIndex = colIndex;
		memmove((char *)tmpIndex.fd_addr += bitMapSize,
		        (char *)colIndex.fd_addr,bitMapSize);
		colIndex.fd_stand = 1;
		vr_trnfm(vdiHandle,&tmpIndex,&colIndex);

		for (i = 0, 
		    dPtr = (short *)colIndex.fd_addr +
		           max(bitMapSize,colTabSize) / 2;
		    i < (unsigned)(1L << tPlanes); i++, dPtr++) {
		    *dPtr = 0;
			for (j = num_planes, sPtr = (short*)colIndex.fd_addr + i / 16;
			     j--; sPtr = (short *)((char *)sPtr+
			                           (((1L << tPlanes) + 15) & ~15) / 8))
				*dPtr |= !!((*sPtr << (i % 16)) & 0x8000) << (num_planes - 1 - j);
		}

		dPtr = (short *)colIndex.fd_addr;
		sPtr = (short *)colIndex.fd_addr + max(bitMapSize,colTabSize) / 2;
		p = planes >= 4 ? 4 : planes;
		for (i = 1 << planes; i--; ) {
			int j;
			if (i < (1 << p) - 1)
				j = colcnv[p-1][i];
			else if (i == (1 << planes) - 1)
				j = 1;
			else
				j = i-1;

			dPtr[i] = cnvTab && j < 16 ? sPtr[cnvTab[j]] : sPtr[j];
		}

		for (i = 1 << planes, xferMode = 0; i--;)
			if (dPtr[i] != i) 
				if (xferMode != 1 && i == (1 << planes)-1 &&
				    dPtr[i] == (1 << num_planes)-1)
					xferMode = 2;
				else
					xferMode = 1;
	}

	switch (xferMode) {
	case 0:
		if (num_planes > planes)
			memset((tmpBuf ? tmpBuf : dData) + planes * bitPlaneSize / 2L,0,
			       (num_planes-planes) * bitPlaneSize);
		memmove(tmpBuf ? tmpBuf : dData,sData,planes * bitPlaneSize);
		break;

	case 1:
		for (y = 0, sPtr = sData, dPtr = tmpBuf ? tmpBuf : dData; y < iconBlk->ib_hicon; y++) {
			for (x = 0; x < iconBlk->ib_wicon; x += 16, sPtr++, dPtr++) {
				for (p = num_planes; p--; dPtr += bitPlaneSize / sizeof(short))
					*dPtr = 0;

				dPtr -= (long)num_planes * (long)bitPlaneSize / sizeof(short);

				for (b = 1; b != 0; b <<= 1) {
					sPtr += (long)planes * (long)bitPlaneSize / sizeof(short);

					for (p = 0, i = 0; p < planes; p++) {
						sPtr -= bitPlaneSize/sizeof(short);
						i = (i << 1) | !!(*sPtr & b);
					}

					i = ((short *) colIndex.fd_addr)[i];
					for (p = 0; p < num_planes; p++, dPtr += bitPlaneSize / sizeof(short))
						if (i & (1 << p))
							*dPtr |= b;

					dPtr -= (long)num_planes * (long)bitPlaneSize / sizeof(short);
				}
			}
		}
		break;

	case 2:
		dPtr = tmpBuf ? tmpBuf : dData;
		sPtr = sData;
		for (y = 0; y < iconBlk->ib_hicon; y++) {
			for (x = 0; x < iconBlk->ib_wicon; x += 16) {
				i = 0xFFFF;
				for (p = planes; p--; sPtr += bitPlaneSize / sizeof(short), dPtr += bitPlaneSize / sizeof(short))
					i &= *dPtr = *sPtr;

				sPtr -= (long)planes * bitPlaneSize / sizeof(short) - 1;
				for (p = num_planes - planes; p--; dPtr += bitPlaneSize / sizeof(short))
					*dPtr = i;

				dPtr -= (long)num_planes * bitPlaneSize / sizeof(short)-1;
			}
		}
		break;
	}

	if (dtMask) {
		memmove(dtMask,sMask,bitPlaneSize);
		mskPtr = dMask;
		sPtr   = tmpBuf ? tmpBuf : dData;
		dPtr   = tmpBuf ? tmpBuf + num_planes * bitPlaneSize / 2L : dtData;

		for (y = 0; y < iconBlk->ib_hicon; y++) {
			ditherBits = 0x5555 << (y & 1);
			for (x = (iconBlk->ib_wicon + 15) / 16; x--; ) {
				curDither = ditherBits & *mskPtr++;
				for (p = num_planes; p--; sPtr += bitPlaneSize/sizeof(short), dPtr += bitPlaneSize/sizeof(short))
						*dPtr = *sPtr | curDither;

				sPtr -= (long)num_planes * (long)bitPlaneSize / sizeof(short)-1;
				dPtr -= (long)num_planes * (long)bitPlaneSize / sizeof(short)-1;
			}
		}

		source.fd_addr	= tmpBuf ? tmpBuf + num_planes * bitPlaneSize / 2L : dtData;
		source.fd_nplanes = num_planes;
		dest.fd_addr	= dtData;

		vr_trnfm(vdiHandle,&source,&dest);
	}

	source.fd_addr		= tmpBuf ? tmpBuf : dData;
	source.fd_nplanes	= num_planes;
	dest.fd_addr		= dData;

	vr_trnfm(vdiHandle,&source,&dest);
	if (tmpBuf)
		free(tmpBuf);

	vsl_color(vdiHandle, 1);

	return;
}