/*    
	Palette.c	2.29
    	Copyright 1997 Willows Software, Inc. 

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

This library 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
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; see the file COPYING.LIB.  If
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
Cambridge, MA 02139, USA.


For more information about the Willows Twin Libraries.

	http://www.willows.com	

To send email to the maintainer of the Willows Twin Libraries.

	mailto:twin@willows.com 

 */

#include <string.h>

#include "windows.h"
#include "windowsx.h"

#include "GdiDC.h"
#include "GdiObjects.h"
#include "Kernel.h"
#include "Log.h"
#include "Driver.h"
#include "DeviceData.h"

	/* enable new palette code */
#define TWIN_PALETTE

HPALETTE32 TWIN_hPalRealized32;

COLORREF TWIN_PaletteRGB(HDC32,COLORREF);
LPRGBQUAD TWIN_MapPaletteColors(HDC,LPBITMAPINFO);
BOOL TWIN_IsColorInPalette(HPALETTE,COLORREF);

HPALETTE	WINAPI
CreatePalette(const LOGPALETTE *lpLogPalette)
{
	HPALETTE hPalette;
	LPPALETTE lpPalette;
	UINT u;

	APISTR((LF_API, "CreatePalette: (API) lpLogPalette %p\n",
		lpLogPalette));

	if (!lpLogPalette)
	{
		ERRSTR((LF_ERR, "CreatePalette: (ERR) null logical palette\n"));
		SetLastErrorEx(1, 0);
		return ((HPALETTE) 0);
	}

	APISTR((LF_API, "CreatePalette: palVersion %x palNumEntries %d\n",
		lpLogPalette->palVersion, lpLogPalette->palNumEntries));

	if (!(lpPalette = CREATEHPALETTE(hPalette)))
	{
		ERRSTR((LF_ERR, "CreatePalette: (ERR) create handle\n"));
		SetLastErrorEx(1, 0);
		return ((HPALETTE) 0);
	}

	if (!(lpPalette->lpLogPalette = (LPLOGPALETTE)
		WinMalloc(sizeof(LOGPALETTE)
		+ lpLogPalette->palNumEntries * sizeof(PALETTEENTRY))))
	{
#ifdef LATER
		/* DELETE uninitialized HPALETTE here */
#endif
		ERRSTR((LF_ERR, "CreatePalette: (ERR) create palette\n"));
		SetLastErrorEx(1, 0);
		return ((HPALETTE) 0);
	}

	lpPalette->lpLogPalette->palVersion = lpLogPalette->palVersion;
	lpPalette->lpLogPalette->palNumEntries = lpLogPalette->palNumEntries;
	for (u = 0; u < lpLogPalette->palNumEntries; u++)
	{
		LOGSTR((LF_LOG, "CreatePalette:"
			" palette entry [%d] rgb (%d, %d, %d) flags %d\n", u,
			lpLogPalette->palPalEntry[u].peRed,
			lpLogPalette->palPalEntry[u].peGreen,
			lpLogPalette->palPalEntry[u].peBlue,
			lpLogPalette->palPalEntry[u].peFlags));
		lpPalette->lpLogPalette->palPalEntry[u]
			= lpLogPalette->palPalEntry[u];
	}

	lpPalette->fIsRealized = FALSE;

	UNLOCK_PALETTE(hPalette);

	return (hPalette);

}

HPALETTE	WINAPI
SelectPalette(HDC hDC, HPALETTE hPalette, BOOL bForceBackground)
{
	LPDC lpDC;
	HPALETTE hOldPalette;

	APISTR((LF_API, "SelectPalette:"
		" hDC %x hPalette %x bForceBackground %d\n",
		hDC, hPalette, bForceBackground));

	ASSERT_DC(lpDC, hDC, (HPALETTE) 0);

	hOldPalette = (HPALETTE) DC_OUTPUT(LSD_SELECTPALETTE, lpDC, hPalette,
		bForceBackground);

	LOGSTR((LF_LOG, "SelectPalette: hOldPalette %x\n", hOldPalette));

	UNLOCK_DC(hDC);

	return (hOldPalette);

}

UINT	WINAPI
RealizePalette(HDC hDC)
{
	LPDC lpDC;
	UINT uNumRealizePalette;

	APISTR((LF_API, "RealizePalette: hDC %x\n", hDC));

	ASSERT_DC(lpDC, hDC, GDI_ERROR);

#ifdef TWIN_PALETTE
	uNumRealizePalette = (UINT) DC_OUTPUT(LSD_REALIZEPALETTE, lpDC, 0, 0);
#else
	uNumRealizePalette = 20;
#endif

	LOGSTR((LF_LOG, "RealizePalette: nNumRealizePalette %d\n",
		uNumRealizePalette));

	UNLOCK_DC(hDC);

	return (uNumRealizePalette);

}

BOOL	WINAPI
ResizePalette(HPALETTE hPalette, UINT uPaletteSize)
{
	LPPALETTE lpPalette;
	LPLOGPALETTE lpLogPalette;
	UINT u;

	APISTR((LF_API, "ResizePalette: hPalette %x uPaletteSize %d\n",
		hPalette, uPaletteSize));

	ASSERT_PALETTE(lpPalette, hPalette, FALSE);

	if (!(lpLogPalette = (LPLOGPALETTE) WinRealloc(lpPalette->lpLogPalette,
		sizeof(LOGPALETTE) + uPaletteSize * sizeof(PALETTEENTRY))))
	{
		UNLOCK_PALETTE(hPalette);
		ERRSTR((LF_ERR, "ResizePalette: (ERR) realloc\n"));
		SetLastErrorEx(1, 0);
		return (FALSE);
	}

	for (u = lpLogPalette->palNumEntries; u < uPaletteSize; u++)
	{
		LOGSTR((LF_LOG, "ResizePalette:"
			" (LOG) palette entry [%d] cleared\n", u));
		lpLogPalette->palPalEntry[u].peRed = 0;
		lpLogPalette->palPalEntry[u].peGreen = 0;
		lpLogPalette->palPalEntry[u].peBlue = 0;
		lpLogPalette->palPalEntry[u].peFlags = 0;
	}
	lpLogPalette->palNumEntries = uPaletteSize;
	lpPalette->lpLogPalette = lpLogPalette;

	UNLOCK_PALETTE(hPalette);

	return (TRUE);

}

UINT	WINAPI
SetPaletteEntries(HPALETTE hPalette,
	UINT uPaletteIndex, UINT uNumPaletteEntries,
	const PALETTEENTRY *lpPaletteEntries)
{
	LPPALETTE lpPalette;
	LPLOGPALETTE lpLogPalette;
	UINT u;

	APISTR((LF_API, "SetPaletteEntries: hPalette %x\n"
		" uPaletteIndex %d uNumPaletteEntries %d\n"
		" lpPaletteEntries %p\n",
		hPalette, uPaletteIndex, uNumPaletteEntries,
		lpPaletteEntries));

	ASSERT_PALETTE(lpPalette, hPalette, 0);

	if (!(lpLogPalette = lpPalette->lpLogPalette)
	 || (uPaletteIndex >= lpLogPalette->palNumEntries)
	 || (uPaletteIndex + uNumPaletteEntries > lpLogPalette->palNumEntries)
	 || !lpPaletteEntries)
	{
		UNLOCK_PALETTE(hPalette);
		ERRSTR((LF_ERR, "SetPaletteEntries:"
			" (ERR) invalid parameters\n"));
		SetLastErrorEx(1, 0);
		return (0);
	}

	for (u = 0; u < uNumPaletteEntries; u++)
	{
		LOGSTR((LF_LOG, "SetPaletteEntries:"
			" (LOG) palette entry [%04x]"
			" RGB(%02x,%02x,%02x) flags %x\n", u,
			lpPaletteEntries[u].peRed,
			lpPaletteEntries[u].peGreen,
			lpPaletteEntries[u].peBlue,
			lpPaletteEntries[u].peFlags));
		lpLogPalette->palPalEntry[uPaletteIndex + u]
			= lpPaletteEntries[u];
	}

	UNLOCK_PALETTE(hPalette);

	return (uNumPaletteEntries);

}

UINT	WINAPI
GetPaletteEntries(HPALETTE hPalette,
	UINT uPaletteIndex, UINT uNumPaletteEntries,
	LPPALETTEENTRY lpPaletteEntries)
{
	LPPALETTE lpPalette;
	LPLOGPALETTE lpLogPalette;
	UINT u;

	APISTR((LF_API, "GetPaletteEntries: hPalette %x\n"
		" uPaletteIndex %d uNumPaletteEntries %d\n"
		" lpPaletteEntries %p\n",
		hPalette, uPaletteIndex, uNumPaletteEntries,
		lpPaletteEntries));

	ASSERT_PALETTE(lpPalette, hPalette, 0);

	if (!(lpLogPalette = lpPalette->lpLogPalette))
	{
		UNLOCK_PALETTE(hPalette);
		ERRSTR((LF_ERR, "GetPaletteEntries: invalid palette\n"));
		SetLastErrorEx(1, 0);
		return (0);
	}

	if (!lpPaletteEntries)
	{
		uNumPaletteEntries = lpLogPalette->palNumEntries;
		UNLOCK_PALETTE(hPalette);
		return (uNumPaletteEntries);
	}

	if (uPaletteIndex >= lpLogPalette->palNumEntries)
	{
		UNLOCK_PALETTE(hPalette);
		ERRSTR((LF_ERR,
			"GetPaletteEntries: invalid palette index %d\n",
			uPaletteIndex));
		SetLastErrorEx(1, 0);
		return (0);
	}

	uNumPaletteEntries = min(uNumPaletteEntries,
		lpLogPalette->palNumEntries - uPaletteIndex);

	for (u = 0; u < uNumPaletteEntries; u++)
	{
		lpPaletteEntries[u]
			= lpLogPalette->palPalEntry[uPaletteIndex + u];
		LOGSTR((LF_LOG, "GetPaletteEntries:"
			" palette entry [%d] rgb (%d, %d, %d) flags %d\n", u,
			lpPaletteEntries[u].peRed,
			lpPaletteEntries[u].peGreen,
			lpPaletteEntries[u].peBlue,
			lpPaletteEntries[u].peFlags));
	}

	UNLOCK_PALETTE(hPalette);

	return (uNumPaletteEntries);

}

UINT	WINAPI
GetSystemPaletteEntries(HDC hDC,
	UINT uPaletteIndex, UINT uNumPaletteEntries, 
	LPPALETTEENTRY lpPaletteEntries)
{
	LPDC lpDC;
	HPALETTE hSystemPalette;
	LPPALETTE lpSystemPalette;
	UINT uNumSystemPaletteEntries;

	APISTR((LF_API, "GetSystemPaletteEntries: hDC %x\n"
		" uPaletteIndex %d uNumPaletteEntries %d\n"
		" lpPaletteEntries %p\n",
		hDC, uPaletteIndex, uNumPaletteEntries,
		lpPaletteEntries));

	ASSERT_DC(lpDC, hDC, 0);

#ifdef TWIN_PALETTE
	if (!(hSystemPalette = (HPALETTE)
		DC_OUTPUT(LSD_GETSYSTEMPALETTE, lpDC, 0, 0))
	 || !(lpSystemPalette = LOCK_PALETTE(hSystemPalette)))
	{
		ERRSTR((LF_ERR, "GetSystemPaletteEntries:"
			" ERROR: null system palette\n"));
		UNLOCK_DC(hDC);
		SetLastErrorEx(1, 0);
		return (0);
	}

	uNumSystemPaletteEntries = GetPaletteEntries(hSystemPalette,
		uPaletteIndex, uNumPaletteEntries, lpPaletteEntries);
#endif

	UNLOCK_PALETTE(hSystemPalette);
	UNLOCK_DC(hDC);

	return (uNumSystemPaletteEntries);

}

UINT	WINAPI
GetSystemPaletteUse(HDC hDC)
{
	LPDC lpDC;
	UINT uSystemPaletteUse;

	APISTR((LF_API, "GetSystemPaletteUse: hDC %x", hDC));

	ASSERT_DC(lpDC, hDC, SYSPAL_ERROR);

	uSystemPaletteUse = (UINT)
		DC_OUTPUT(LSD_SYSTEMPALETTEUSE, lpDC, SYSPAL_ERROR, FALSE);

	UNLOCK_DC(hDC);

	return (uSystemPaletteUse);

}

UINT	WINAPI
SetSystemPaletteUse(HDC hDC, UINT uSystemPaletteUse)
{
	LPDC lpDC;
	UINT uOldSystemPaletteUse;

	APISTR((LF_API, "SetSystemPaletteUse: hDC %x uSystemPaletteUse %d",
		hDC, uSystemPaletteUse));

	ASSERT_DC(lpDC, hDC, SYSPAL_ERROR);

	uOldSystemPaletteUse = (UINT)
		DC_OUTPUT(LSD_SYSTEMPALETTEUSE, lpDC, uSystemPaletteUse, TRUE);

	UNLOCK_DC(hDC);

	return (uOldSystemPaletteUse);

}

UINT	WINAPI
GetNearestPaletteIndex(HPALETTE hPalette, COLORREF crColor)
{
	LPPALETTE lpPalette;
	LPLOGPALETTE lpLogPalette;
	UINT u, uNearestPaletteIndex;
	long delta0, r0, g0, b0, delta, r, g, b;

	APISTR((LF_API, "GetNearestPaletteIndex: hPalette %x crColor %x\n",
		hPalette, crColor));

	ASSERT_PALETTE(lpPalette, hPalette, CLR_INVALID);

	if (!(lpLogPalette = lpPalette->lpLogPalette))
	{
		UNLOCK_PALETTE(hPalette);
		ERRSTR((LF_ERR, "GetNearestPaletteIndex: invalid palette\n"));
		SetLastErrorEx(1, 0);
		return (CLR_INVALID);
	}

	r0 = GetRValue(crColor);
	g0 = GetGValue(crColor);
	b0 = GetBValue(crColor);
	delta0 = 0x030000L;
	uNearestPaletteIndex = CLR_INVALID;
	LOGSTR((LF_LOG, "GetNearestPaletteIndex: match rgb (%d, %d, %d)\n",
		r0, g0, b0));
	for (u = 0; u < lpLogPalette->palNumEntries; u++)
	{
		LOGSTR((LF_LOG, "GetNearestPaletteIndex: palette index [%d]",
			u));
		if ((lpLogPalette->palPalEntry[u].peRed == r0)
		 && (lpLogPalette->palPalEntry[u].peGreen == g0)
		 && (lpLogPalette->palPalEntry[u].peBlue == b0))
		{
			LOGSTR((LF_LOG, " exact match\n"));
			uNearestPaletteIndex = u;
			break;
		}
		r = lpLogPalette->palPalEntry[u].peRed;
		g = lpLogPalette->palPalEntry[u].peGreen;
		b = lpLogPalette->palPalEntry[u].peBlue;
		delta = (r-r0)*(r-r0) + (g-g0)*(g-g0) + (b-b0)*(b-b0);
		if (delta < delta0)
		{
			LOGSTR((LF_LOG, " match rgb (%d, %d, %d) delta %d\n",
				r, g, b, delta));
			uNearestPaletteIndex = u;
			delta0 = delta;
		}
		else
		{
			LOGSTR((LF_LOG, " delta %d exceeds min delta %d\n",
				delta, delta0));
		}
	}

	UNLOCK_PALETTE(hPalette);

	return (uNearestPaletteIndex);

}

BOOL	WINAPI
AnimatePalette(HPALETTE hPalette,
	UINT uPaletteIndex, UINT uNumPaletteEntries,
	const PALETTEENTRY *lpPaletteEntries)
{
	LPPALETTE lpPalette;
	LPLOGPALETTE lpLogPalette;
	UINT u, uAnimatePaletteIndex;
	BYTE r, g;
	BOOL bSuccess = TRUE;

	APISTR((LF_API, "AnimatePalette: hPalette %x\n"
		" uPaletteIndex %d uNumPaletteEntries %d\n"
		" lpPaletteEntries %p\n",
		hPalette, uPaletteIndex, uNumPaletteEntries,
		lpPaletteEntries));

	/* (UNDOCUMENTED) null palette handle ==> animate system palette */
	if (!hPalette)
	{
		for (u = uAnimatePaletteIndex = 0; u < uNumPaletteEntries;
		     u++, uAnimatePaletteIndex++)
		{
			if (!(lpPaletteEntries[u].peFlags & PC_RESERVED))
				continue;
			DRVCALL_COLORS(PLH_ANIMATEPALETTE, 0,
				uAnimatePaletteIndex, lpPaletteEntries + u);
		}
		return (TRUE);
	}

	ASSERT_PALETTE(lpPalette, hPalette, FALSE);

	if (!(lpLogPalette = lpPalette->lpLogPalette)
	 || (uPaletteIndex >= lpLogPalette->palNumEntries)
	 || (uPaletteIndex + uNumPaletteEntries > lpLogPalette->palNumEntries)
	 || !lpPaletteEntries)
	{
		UNLOCK_PALETTE(hPalette);
		ERRSTR((LF_ERR, "AnimatePalette: invalid parameters\n"));
		SetLastErrorEx(1, 0);
		return (FALSE);
	}

	for (u = 0; u < uNumPaletteEntries; u++)
	{
		LOGSTR((LF_LOG, "AnimatePalette: palette entry [%04x]",
			uPaletteIndex + u));
		if (!(lpLogPalette->palPalEntry[uPaletteIndex+u].peFlags &
			PC_RESERVED))
		{
			LOGSTR((LF_LOG, " (not PC_RESERVED)\n"));
			continue;
		}
		if (!(lpLogPalette->palPalEntry[uPaletteIndex+u].peFlags &
			PC_EXPLICIT))
		{
			LOGSTR((LF_LOG, " (PC_RESERVED) (but unrealized)\n"));
			continue;
		}
		r = lpLogPalette->palPalEntry[uPaletteIndex+u].peRed;
		g = lpLogPalette->palPalEntry[uPaletteIndex+u].peGreen;
		uAnimatePaletteIndex = r | (g << 8);
		if (uAnimatePaletteIndex == 0)
		{
			LOGSTR((LF_LOG, " (PC_RESERVED) (but no index)\n"));
			continue;
		}
		else
		{
			LOGSTR((LF_LOG, " (PC_RESERVED) (index %04x)"
				" RGB(%02x,%02x,%02x)\n",
				uAnimatePaletteIndex,
				lpPaletteEntries[u].peRed,
				lpPaletteEntries[u].peGreen,
				lpPaletteEntries[u].peBlue));
			bSuccess = DRVCALL_COLORS(PLH_ANIMATEPALETTE, 0,
				uAnimatePaletteIndex, lpPaletteEntries + u);
		}
	}

	UNLOCK_PALETTE(hPalette);

	return (bSuccess);

}

BOOL	WINAPI
UpdateColors(HDC hDC)
{
	LPDC lpDC;
	BOOL bUpdateColors;

	APISTR((LF_API, "UpdateColors: hDC %x\n", hDC));

	ASSERT_DC(lpDC, hDC, FALSE);

	bUpdateColors = (BOOL) DC_OUTPUT(LSD_UPDATECOLORS, lpDC, 0, 0);

	UNLOCK_DC(hDC);

	return (bUpdateColors);

}

COLORREF
TWIN_PaletteRGB(HDC32 hDC32, COLORREF crColor)
{
    HPALETTE32 hPalette32;
    UINT uIndex;
    LPPALETTEENTRY pe;

    ASSERT_HPALETTE(hPalette32,hDC32->hPalette,0L);

    /* PALETTE_RGB */
    if (crColor & PALETTE_RGB)
    {
	RELEASEPALETTEINFO(hPalette32);
	return (crColor & ~PALETTE_MASK);
    }

    /* PALETTE_INDEX */
    uIndex = crColor & ~PALETTE_MASK;
    if (uIndex >= hPalette32->lpLogPalette->palNumEntries)
	uIndex = 0;
    pe = &(hPalette32->lpLogPalette->palPalEntry[uIndex]);
    crColor = RGB(pe->peRed, pe->peGreen, pe->peBlue);
    RELEASEPALETTEINFO(hPalette32);
    return crColor;

}

LPRGBQUAD
TWIN_MapPaletteColors(HDC hDC, LPBITMAPINFO lpbmi)
{
    HDC32 hDC32 = 0;
    HPALETTE32 hPalette32;
    LPRGBQUAD lpRGB;
    int nNumColors,i;
#ifdef TWIN32
    DWORD *lpIndex;
#else
    WORD *lpIndex;
#endif

    if (hDC == 0) {
	if (!(hPalette32 = TWIN_hPalRealized32)) {
    	    HPALETTE hPalette;

	    hPalette = GetStockObject(DEFAULT_PALETTE);
	    hPalette32 = GETPALETTEINFO(hDC32->hPalette);
	    TWIN_hPalRealized32 = hPalette32;
	}
    }
    else {
	ASSERT_HDC(hDC32,hDC,0);
	hPalette32 = GETPALETTEINFO(hDC32->hPalette);
    }
    if (hPalette32 == NULL) {
        RELEASEDCINFO(hDC32);
	return NULL;
    }

    nNumColors = 1 << lpbmi->bmiHeader.biBitCount;
    if (lpbmi->bmiHeader.biClrUsed)
	nNumColors = min(nNumColors,lpbmi->bmiHeader.biClrUsed);

    lpRGB = (LPRGBQUAD)WinMalloc(sizeof(RGBQUAD) * nNumColors);
#ifdef TWIN32
    lpIndex = (DWORD *)&lpbmi->bmiColors[0];
#else
    lpIndex = (WORD *)&lpbmi->bmiColors[0];
#endif

    for (i=0; i<nNumColors; i++) {
	lpRGB[i].rgbRed
		= hPalette32->lpLogPalette->palPalEntry[*lpIndex].peRed;
	lpRGB[i].rgbGreen
		= hPalette32->lpLogPalette->palPalEntry[*lpIndex].peGreen;
	lpRGB[i].rgbBlue
		 = hPalette32->lpLogPalette->palPalEntry[*lpIndex].peBlue;
	lpIndex++;
    }
    RELEASEDCINFO(hDC32);
    RELEASEPALETTEINFO(hPalette32);
    return lpRGB;
}

BOOL
TWIN_IsColorInPalette(HPALETTE hPalette, COLORREF cr)
{
    HPALETTE32 hPalette32;
    int i;

    ASSERT_HPALETTE(hPalette32,hPalette,FALSE);

    for (i=0; i < hPalette32->lpLogPalette->palNumEntries; i++) {
	if ((hPalette32->lpLogPalette->palPalEntry[i].peRed == GetRValue(cr))
	 || (hPalette32->lpLogPalette->palPalEntry[i].peGreen == GetBValue(cr))
	 || (hPalette32->lpLogPalette->palPalEntry[i].peBlue == GetBValue(cr)))
	{
	    RELEASEPALETTEINFO(hPalette32);
	    return TRUE;
	}
    }
    RELEASEPALETTEINFO(hPalette32);
    return FALSE;
}

/* (WIN32) GDI Halftone Palette ******************************************** */

HPALETTE	WINAPI
CreateHalftonePalette(HDC hDC)
{
	HPALETTE hPalette = 0;
	LPDC lpDC;

	APISTR((LF_API, "CreateHalftonePalette: hDC %x", hDC));

	ASSERT_DC(lpDC, hDC, (HPALETTE) 0);

#ifndef LATER
	LOGSTR((LF_LOG, "CreateHalftonePalette: (STUB)\n"));
#endif

	UNLOCK_DC(hDC);

	return (hPalette);

}

