COLORWP.C

/*************************************************************************** 
* *
* MODULE : ColorWP.c *
* *
* DESCRIPTION : Window function for the colors window and related fns. *
* *
* FUNCTIONS : ColorWP () - Window function for colors *
* window. *
* *
* ComputeInverseColor() - Gets the inverse RGB of a given *
* RGB value *
* *
* HISTORY : 6/21/89 - adapted from pBrush - LR *
* *
***************************************************************************/

#include "imagedit.h"
#include "dialogs.h"

#include <windowsx.h>
#include <commdlg.h>


STATICFN VOID NEAR ColorInit(HWND hwnd);
STATICFN VOID NEAR ColorProcessCommand(HWND hwnd, INT idCtrl, INT NotifyCode);
STATICFN VOID NEAR ColorBoxPaint(HDC hdc);
STATICFN VOID NEAR DrawColorRect(HDC hdc, DWORD rgb, INT x, INT y,
INT cx, INT cy, HDC hdcMem, BOOL fMonoOK);
STATICFN VOID NEAR MyRectangle(HDC hdc, INT left, INT top, INT right,
INT bottom, HDC hdcMem, BOOL fMonoOK);
STATICFN VOID NEAR ColorBoxClicked(UINT msg, PPOINT ppt);
STATICFN BOOL NEAR ColorBoxHitTest(PPOINT ppt, PINT piColor, PINT pfMode);
STATICFN VOID NEAR ColorLRPaint(HWND hwnd, HDC hdc);
STATICFN VOID NEAR ColorLRDrawSamples(HDC hdc, PRECT prc, BOOL fLeft);
STATICFN VOID NEAR ColorLRUpdate(BOOL fLeft);
STATICFN VOID NEAR ColorEdit(VOID);
STATICFN VOID NEAR SetLeftColor(INT iColor, INT iMode);
STATICFN VOID NEAR SetRightColor(INT iColor, INT iMode);
STATICFN HBRUSH NEAR MyCreateSolidBrush(DWORD rgb);
STATICFN DWORD NEAR MyGetNearestColor(DWORD rgb, BOOL fMonoOK);
STATICFN DWORD NEAR ComputeInverseColor(DWORD rgb);

/*
* Width/height of a single color square.
*/
static INT gcxColorBox;

/*
* Vertical offset within the color box control to where to start the
* top row of color squares (the color squares are vertically centered
* within the color box control).
*/
static INT gyColorBoxStart;

/*
* Number of colors and image type. These globals are used by the
* the color palette routines to know what mode the color palette
* is in.
*/
static INT gnColorPalColors;
static INT giColorPalType;



/****************************************************************************
* ColorShow
*
* This function shows or hides the color palette.
*
* History:
*
****************************************************************************/

VOID ColorShow(
BOOL fShow)
{
if (fShow)
ShowWindow(ghwndColor, SW_SHOWNA);
else
ShowWindow(ghwndColor, SW_HIDE);
}



/************************************************************************
* ColorDlgProc
*
*
*
* Arguments:
*
* History:
*
************************************************************************/

DIALOGPROC ColorDlgProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
ColorInit(hwnd);

/*
* Return TRUE so that the dialog manager does NOT set the focus
* for me. This prevents the status window from initially having
* the focus when the editor is started.
*/
return TRUE;

case WM_ACTIVATE:
if (GET_WM_ACTIVATE_STATE(wParam, lParam))
gidCurrentDlg = DID_COLOR;

break;

case WM_CTLCOLORBTN:
case WM_CTLCOLORDLG:
case WM_CTLCOLORSTATIC:
switch (GET_WM_CTLCOLOR_TYPE(wParam, lParam, msg)) {
case CTLCOLOR_BTN:
case CTLCOLOR_DLG:
return (BOOL)GetStockObject(LTGRAY_BRUSH);

case CTLCOLOR_STATIC:
SetBkColor(GET_WM_CTLCOLOR_HDC(wParam, lParam, msg),
RGB_LIGHTGRAY);
return (BOOL)GetStockObject(LTGRAY_BRUSH);
}

return (BOOL)NULL;

case WM_PAINT:
{
HDC hdc;
PAINTSTRUCT ps;

hdc = BeginPaint(hwnd, &ps);
DrawMarginBorder(hwnd, hdc);
EndPaint(hwnd, &ps);
}

break;

case WM_COMMAND:
ColorProcessCommand(hwnd,
GET_WM_COMMAND_ID(wParam, lParam),
GET_WM_COMMAND_CMD(wParam, lParam));
break;

case WM_CLOSE:
/*
* The user closed the color palette from the system menu.
* Hide the window (we don't actually destroy it so
* that it will appear in the same spot when they show
* it again).
*/
ColorShow(FALSE);
gfShowColor = FALSE;
break;

case WM_DESTROY:
{
RECT rc;

/*
* Save the position of the color palette.
*/
GetWindowRect(hwnd, &rc);
WriteWindowPos(&rc, FALSE, szColorPos);

/*
* Null out the global window handle for the color palette
* for safety's sake.
*/
ghwndColor = NULL;
}

break;

default:
return FALSE;
}

return FALSE;
}



/************************************************************************
* ColorInit
*
*
*
* Arguments:
*
* History:
*
************************************************************************/
STATICFN VOID NEAR ColorInit(
HWND hwnd)
{
RECT rc;

/*
* Get the dimension of a single color square, and the vertical
* offset to where the top of the squares are.
*/
GetWindowRect(GetDlgItem(hwnd, DID_COLORBOX), &rc);
gcxColorBox = (rc.right - rc.left) / COLORCOLS;
gyColorBoxStart = ((rc.right - rc.left) - (gcxColorBox * COLORCOLS)) / 2;
}



/************************************************************************
* ColorProcessCommand
*
*
* Arguments:
* HWND hwnd - The window handle.
* INT idCtrl - The id of the control the WM_COMMAND is for.
* INT NotifyCode - The control's notification code.
*
* History:
*
************************************************************************/

STATICFN VOID NEAR ColorProcessCommand(
HWND hwnd,
INT idCtrl,
INT NotifyCode)
{
switch (idCtrl) {
case DID_COLOREDIT:
ColorEdit();
break;

case DID_COLORDEFAULT:
if (gfModeLeft == MODE_COLOR) {
gargbColor[giColorLeft] = gargbDefaultColor[giColorLeft];
InvalidateRect(GetDlgItem(ghwndColor, DID_COLORBOX),
NULL, TRUE);
SetLeftColor(giColorLeft, gfModeLeft);
}

break;
}
}



/************************************************************************
* ColorBoxWndProc
*
*
*
* Arguments:
*
* History:
*
************************************************************************/

WINDOWPROC ColorBoxWndProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
POINT pt;
HDC hdc;
PAINTSTRUCT ps;
INT iColor;
INT iMode;

switch (msg) {
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
ColorBoxPaint(hdc);
EndPaint(hwnd, &ps);
break;

case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
((pt).x = ((*((POINTS *)&(lParam)))).x, (pt).y = ((*((POINTS *)&(lParam)))).y);
ColorBoxClicked(msg, &pt);
break;

case WM_LBUTTONDBLCLK:
((pt).x = ((*((POINTS *)&(lParam)))).x, (pt).y = ((*((POINTS *)&(lParam)))).y);
if (ColorBoxHitTest(&pt, &iColor, &iMode))
ColorEdit();

break;

default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}

return 0;
}



/************************************************************************
* ColorBoxPaint
*
*
*
* Arguments:
*
* History:
*
************************************************************************/

STATICFN VOID NEAR ColorBoxPaint(
HDC hdc)
{
HDC hdcMem;
HBITMAP hbmMem;
INT i;
INT x;
INT y;
INT cx = gcxColorBox + 1;
INT cy = gcxColorBox + 1;

if (giColorPalType != FT_BITMAP) {
x = 0;
y = gyColorBoxStart;
DrawColorRect(hdc, grgbScreen, x, y, cx, cy, NULL, FALSE);
y += gcxColorBox;
DrawColorRect(hdc, grgbInverse, x, y, cx, cy, NULL, FALSE);
}

if (!(hdcMem = CreateCompatibleDC(hdc)))
return;

/*
* Create a bitmap. It will have the same number of colors as the
* current image.
*/
if (!(hbmMem = MyCreateBitmap(hdc, cx, cy, gnColorPalColors))) {
DeleteDC(hdcMem);
return;
}

SelectObject(hdcMem, hbmMem);

x = gcxColorBox * 2;
y = gyColorBoxStart;

for (i = 1; i <= COLORSMAX; i++) {
DrawColorRect(hdc, gargbCurrent[i - 1], x, y, cx, cy, hdcMem, TRUE);

if (i % COLORROWS) {
y += gcxColorBox;
}
else {
x += gcxColorBox;
y = gyColorBoxStart;
}
}

DeleteDC(hdcMem);
DeleteObject(hbmMem);
}



/************************************************************************
* DrawColorRect
*
*
*
* Arguments:
*
* History:
*
************************************************************************/

STATICFN VOID NEAR DrawColorRect(
HDC hdc,
DWORD rgb,
INT x,
INT y,
INT cx,
INT cy,
HDC hdcMem,
BOOL fMonoOK)
{
HBRUSH hbr;
HBRUSH hbrOld;

hbr = CreateSolidBrush(rgb);
hbrOld = SelectObject(hdc, hbr);
MyRectangle(hdc, x, y, x + cx, y + cy, hdcMem, fMonoOK);
SelectObject(hdc, hbrOld);
DeleteObject(hbr);
}



/************************************************************************
* MyRectangle
*
*
*
* Arguments:
*
* History:
*
************************************************************************/

STATICFN VOID NEAR MyRectangle(
HDC hdc,
INT left,
INT top,
INT right,
INT bottom,
HDC hdcMem,
BOOL fMonoOK)
{
HBITMAP hbmMem;
HBRUSH hbr;
HPEN hpen;
HBRUSH hbrOld;
HPEN hpenOld;
BOOL fDCCreated = FALSE;
INT cx = right - left;
INT cy = bottom - top;
INT nColors;

/*
* Do they want us to create the memory DC and bitmap for them?
*/
if (!hdcMem) {
if (!(hdcMem = CreateCompatibleDC(hdc)))
return;

/*
* Create a bitmap. It will be monochrome if in 2 color mode
* and monochrome is ok, otherwise it will be 16 color.
*/
nColors = gnColorPalColors;
if (!fMonoOK)
nColors = 16;

if (!(hbmMem = MyCreateBitmap(hdc, cx, cy, nColors))) {
DeleteDC(hdcMem);
return;
}

SelectObject(hdcMem, hbmMem);
fDCCreated = TRUE;
}

/*
* Extract the current pen and brush out of the passed in DC.
*/
hbr = SelectObject(hdc, GetStockObject(NULL_BRUSH));
hpen = SelectObject(hdc, GetStockObject(NULL_PEN));

/*
* Select them into the memory DC.
*/
hbrOld = SelectObject(hdcMem, hbr);
hpenOld = SelectObject(hdcMem, hpen);

/*
* Draw the rectangle in the memory bitmap.
*/
Rectangle(hdcMem, 0, 0, cx, cy);

/*
* Unselect the pen and brush from the memory DC.
*/
SelectObject(hdcMem, hbrOld);
SelectObject(hdcMem, hpenOld);

/*
* Restore the pen and brush to the original DC.
*/
SelectObject(hdc, hbr);
SelectObject(hdc, hpen);

/*
* Blit the memory image to the passed in DC.
*/
BitBlt(hdc, left, top, cx, cy, hdcMem, 0, 0, SRCCOPY);

if (fDCCreated) {
DeleteDC(hdcMem);
DeleteObject(hbmMem);
}
}



/************************************************************************
* ColorBoxClicked
*
*
*
* Arguments:
*
* History:
*
************************************************************************/

STATICFN VOID NEAR ColorBoxClicked(
UINT msg,
PPOINT ppt)
{
INT iColor;
INT iMode;

if (ColorBoxHitTest(ppt, &iColor, &iMode)) {
switch (msg) {
case WM_LBUTTONDOWN:
SetLeftColor(iColor, iMode);
break;

case WM_RBUTTONDOWN:
SetRightColor(iColor, iMode);
break;
}
}
}



/************************************************************************
* ColorBoxHitTest
*
*
*
* Arguments:
*
* History:
*
************************************************************************/

STATICFN BOOL NEAR ColorBoxHitTest(
PPOINT ppt,
PINT piColor,
PINT pfMode)
{
INT iCol;
INT iRow;
INT iBox;

if (ppt->y < gyColorBoxStart)
return FALSE;

iCol = ppt->x / gcxColorBox;
iRow = (ppt->y - gyColorBoxStart) / gcxColorBox;

if (iCol >= (COLORSMAX / COLORROWS) + 2 || iRow >= COLORROWS)
return FALSE;

iBox = iRow + (iCol * COLORROWS);

switch (iBox) {
case 0:
if (giColorPalType == FT_BITMAP)
return FALSE;

*piColor = 0;
*pfMode = MODE_SCREEN;
return TRUE;

case 1:
if (giColorPalType == FT_BITMAP)
return FALSE;

*piColor = 0;
*pfMode = MODE_INVERSE;
return TRUE;

case 2:
case 3:
return FALSE;

default:
*piColor = iBox - (COLORROWS * 2);
*pfMode = MODE_COLOR;
return TRUE;
}
}



/************************************************************************
* ColorLRWndProc
*
*
*
* Arguments:
*
* History:
*
************************************************************************/

WINDOWPROC ColorLRWndProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;

switch (msg) {
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
ColorLRPaint(hwnd, hdc);
EndPaint(hwnd, &ps);
break;

default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}

return 0;
}



/************************************************************************
* ColorLRPaint
*
*
*
* Arguments:
*
* History:
*
************************************************************************/

STATICFN VOID NEAR ColorLRPaint(
HWND hwnd,
HDC hdc)
{
RECT rc;

GetClientRect(hwnd, &rc);
DrawSunkenRect(&rc, hdc);
ColorLRDrawSamples(hdc, &rc, TRUE);
ColorLRDrawSamples(hdc, &rc, FALSE);
}



/************************************************************************
* ColorLRDrawSamples
*
* Draws the sample colors in the Color Left-Right control.
*
* Arguments:
* HDC hdc - DC to draw into.
* PRECT prc - Rectangle of color sample control. The samples will
* be centered within this with an appropriate margin.
* BOOL fLeft - TRUE if the left sample is to be drawn, FALSE for the right.
*
* History:
*
************************************************************************/

STATICFN VOID NEAR ColorLRDrawSamples(
HDC hdc,
PRECT prc,
BOOL fLeft)
{
INT xLeftStart;
INT xRightStart;
INT ySolidStart;
INT yDitherStart;
INT cx;
INT cy;
HBRUSH hbrOld;
HPEN hpenOld;
BOOL fMonoOK;

/*
* The width and height of each square includes the border.
*/
cx = ((prc->right - prc->left) - (6 * PALETTEMARGIN)) / 2;
cy = ((prc->bottom - prc->top) - (4 * PALETTEMARGIN)) / 2;

xLeftStart = prc->left + (PALETTEMARGIN * 2) + 1;
xRightStart = xLeftStart + cx + (PALETTEMARGIN * 2);

ySolidStart = prc->top + (PALETTEMARGIN * 2) + 1;
yDitherStart = ySolidStart - 1 + cy;

/*
* Draw either the left or the right color sample.
*/
if (fLeft) {
fMonoOK = (gfModeLeft == MODE_COLOR) ? TRUE : FALSE;

/*
* Draw the solid color.
*/
hbrOld = SelectObject(hdc, ghbrLeftSolid);
hpenOld = SelectObject(hdc, GetStockObject(NULL_PEN));
MyRectangle(hdc, xLeftStart, ySolidStart,
xLeftStart + cx, yDitherStart + 1, NULL, fMonoOK);

/*
* Draw the true color (may be dithered).
*/
SelectObject(hdc, ghbrLeft);
MyRectangle(hdc, xLeftStart, yDitherStart,
xLeftStart + cx, yDitherStart + cy, NULL, fMonoOK);
}
else {
fMonoOK = (gfModeRight == MODE_COLOR) ? TRUE : FALSE;

hbrOld = SelectObject(hdc, ghbrRightSolid);
hpenOld = SelectObject(hdc, GetStockObject(NULL_PEN));
MyRectangle(hdc, xRightStart, ySolidStart,
xRightStart + cx, yDitherStart + 1, NULL, fMonoOK);

SelectObject(hdc, ghbrRight);
MyRectangle(hdc, xRightStart, yDitherStart,
xRightStart + cx, yDitherStart + cy, NULL, fMonoOK);
}

/*
* Now draw the outline rectangle.
*/
SelectObject(hdc, GetStockObject(BLACK_PEN));
SelectObject(hdc, GetStockObject(NULL_BRUSH));

if (fLeft) {
Rectangle(hdc, xLeftStart - 1, ySolidStart - 1,
xLeftStart + cx, yDitherStart + cy);
}
else {
Rectangle(hdc, xRightStart - 1, ySolidStart - 1,
xRightStart + cx, yDitherStart + cy);
}

/*
* Clean up.
*/
SelectObject(hdc, hpenOld);
SelectObject(hdc, hbrOld);
}



/************************************************************************
* ColorLRUpdate
*
* Called when the left or right color has been changed. This function
* will cause the specified color sample to be updated in the color palette.
*
* History:
*
************************************************************************/

STATICFN VOID NEAR ColorLRUpdate(
BOOL fLeft)
{
RECT rc;
HWND hwndLR;
HDC hdc;

hwndLR = GetDlgItem(ghwndColor, DID_COLORLR);
GetClientRect(hwndLR, &rc);
hdc = GetDC(hwndLR);
ColorLRDrawSamples(hdc, &rc, fLeft);
ReleaseDC(hwndLR, hdc);
}



/************************************************************************
* ColorEdit
*
* This function calls the standard color chooser dialog to get a
* new color for the selected palette entry.
*
* History:
*
************************************************************************/

STATICFN VOID NEAR ColorEdit(VOID)
{
/*
* This array of custom colors is initialized to all white colors.
* The custom colors will be remembered between calls, but not
* between sessions.
*/
static DWORD argbCust[16] = {
RGB(255, 255, 255), RGB(255, 255, 255),
RGB(255, 255, 255), RGB(255, 255, 255),
RGB(255, 255, 255), RGB(255, 255, 255),
RGB(255, 255, 255), RGB(255, 255, 255),
RGB(255, 255, 255), RGB(255, 255, 255),
RGB(255, 255, 255), RGB(255, 255, 255),
RGB(255, 255, 255), RGB(255, 255, 255),
RGB(255, 255, 255), RGB(255, 255, 255)
};
CHOOSECOLOR cc;
DWORD rgbOld;
BOOL fResult;
INT idPrevDlg;

switch (gfModeLeft) {
case MODE_COLOR:
/*
* The monochrome palette cannot be edited.
*/
if (gnColorPalColors == 2)
return;

rgbOld = gargbCurrent[giColorLeft];
break;

case MODE_SCREEN:
rgbOld = grgbScreen;
break;

case MODE_INVERSE:
rgbOld = grgbInverse;
break;
}

cc.lStructSize = sizeof(CHOOSECOLOR);
cc.hwndOwner = ghwndMain;
cc.hInstance = ghInst;
cc.rgbResult = rgbOld;
cc.lpCustColors = argbCust;
cc.Flags = CC_RGBINIT | CC_SHOWHELP;
cc.lCustData = 0;
cc.lpfnHook = NULL;
cc.lpTemplateName = NULL;

EnteringDialog(DID_COMMONFILECHOOSECOLOR, &idPrevDlg, TRUE);
fResult = ChooseColor(&cc);
EnteringDialog(idPrevDlg, NULL, FALSE);

if (fResult && rgbOld != cc.rgbResult) {
switch (gfModeLeft) {
case MODE_COLOR:
gargbCurrent[giColorLeft] = cc.rgbResult;
break;

case MODE_SCREEN:
SetScreenColor(cc.rgbResult);
break;

case MODE_INVERSE:
SetScreenColor(ComputeInverseColor(cc.rgbResult));
break;
}

SetLeftColor(giColorLeft, gfModeLeft);
InvalidateRect(GetDlgItem(ghwndColor, DID_COLORBOX), NULL, TRUE);
}
}



/************************************************************************
* SetLeftColor
*
*
* History:
*
************************************************************************/

STATICFN VOID NEAR SetLeftColor(
INT iColor,
INT iMode)
{
DWORD rgbSolid;
BOOL fEnableDefault = FALSE;
BOOL fEnableEdit = FALSE;

if (ghbrLeft)
DeleteObject(ghbrLeft);

if (ghbrLeftSolid)
DeleteObject(ghbrLeftSolid);

if (ghpenLeft)
DeleteObject(ghpenLeft);

switch (iMode) {
case MODE_COLOR:
ghbrLeft = MyCreateSolidBrush(gargbCurrent[iColor]);
rgbSolid = MyGetNearestColor(gargbCurrent[iColor], TRUE);
ghbrLeftSolid = CreateSolidBrush(rgbSolid);
ghpenLeft = CreatePen(PS_INSIDEFRAME, 1, rgbSolid);
giColorLeft = iColor;

/*
* We will enable the "Default" button if the current color
* on the left button is not the default color, and we are
* not in monochrome mode.
*/
if (gargbColor[giColorLeft] != gargbDefaultColor[giColorLeft] &&
gnColorPalColors > 2)
fEnableDefault = TRUE;

/*
* For non-screen colors, the Edit button will be enabled
* if we are not in monochrome mode.
*/
if (gnColorPalColors > 2)
fEnableEdit = TRUE;

break;

case MODE_SCREEN:
ghbrLeft = CreateSolidBrush(grgbScreen);
ghbrLeftSolid = CreateSolidBrush(grgbScreen);
ghpenLeft = CreatePen(PS_INSIDEFRAME, 1, grgbScreen);
giColorLeft = 0;
fEnableEdit = TRUE;
break;

case MODE_INVERSE:
ghbrLeft = CreateSolidBrush(grgbInverse);
ghbrLeftSolid = CreateSolidBrush(grgbInverse);
ghpenLeft = CreatePen(PS_INSIDEFRAME, 1, grgbInverse);
giColorLeft = 0;
fEnableEdit = TRUE;
break;
}

EnableWindow(GetDlgItem(ghwndColor, DID_COLORDEFAULT), fEnableDefault);
EnableWindow(GetDlgItem(ghwndColor, DID_COLOREDIT), fEnableEdit);

gfModeLeft = iMode;
ColorLRUpdate(TRUE);
}



/************************************************************************
* SetRightColor
*
*
* History:
*
************************************************************************/

STATICFN VOID NEAR SetRightColor(
INT iColor,
INT iMode)
{
DWORD rgbSolid;

if (ghbrRight)
DeleteObject(ghbrRight);

if (ghbrRightSolid)
DeleteObject(ghbrRightSolid);

if (ghpenRight)
DeleteObject(ghpenRight);

switch (iMode) {
case MODE_COLOR:
ghbrRight = MyCreateSolidBrush(gargbCurrent[iColor]);
rgbSolid = MyGetNearestColor(gargbCurrent[iColor], TRUE);
ghbrRightSolid = CreateSolidBrush(rgbSolid);
ghpenRight = CreatePen(PS_INSIDEFRAME, 1, rgbSolid);
giColorRight = iColor;
break;

case MODE_SCREEN:
ghbrRight = CreateSolidBrush(grgbScreen);

ghbrRightSolid = CreateSolidBrush(grgbScreen); 
ghpenRight = CreatePen(PS_INSIDEFRAME, 1, grgbScreen);
giColorRight = 0;
break;

case MODE_INVERSE:
ghbrRight = CreateSolidBrush(grgbInverse);
ghbrRightSolid = CreateSolidBrush(grgbInverse);
ghpenRight = CreatePen(PS_INSIDEFRAME, 1, grgbInverse);
giColorRight = 0;
break;
}

gfModeRight = iMode;
ColorLRUpdate(FALSE);
}



/************************************************************************
* SetScreenColor
*
*
* History:
*
************************************************************************/

VOID SetScreenColor(
DWORD rgb)
{
DWORD rgbInverse;
HDC hdcTemp;
HBITMAP hbmOld;
HDC hdcANDTemp;
HBITMAP hbmANDOld;

rgb = MyGetNearestColor(rgb, FALSE);

/*
* Because we are about to change the screen color, separate
* out the XOR mask (but only for icons/cursors).
*/
if (giColorPalType != FT_BITMAP) {
if (gpImageCur) {
ImageDCSeparate(ghdcImage, gcxImage, gcyImage, ghdcANDMask,
grgbScreen);

/*
* Is there a pending undo buffer? If so, it must be
* changed as well or an undo that is done after a screen
* color change will restore the wrong colors!
*/
if (ghbmUndo) {
/*
* Create some temporary DC's to use when separating
* out the undo buffer's masks. These will be deleted
* a little later.
*/
hdcTemp = CreateCompatibleDC(ghdcImage);
hbmOld = SelectObject(hdcTemp, ghbmUndo);
hdcANDTemp = CreateCompatibleDC(ghdcANDMask);
hbmANDOld = SelectObject(hdcANDTemp, ghbmUndoMask);

/*
* Separate out the undo buffer's colors, before
* changing the screen color. It will be combined
* later.
*/
ImageDCSeparate(hdcTemp, gcxImage, gcyImage, hdcANDTemp,
grgbScreen);
}
}
}

if (ghbrScreen)
DeleteObject(ghbrScreen);

ghbrScreen = CreateSolidBrush(rgb);
grgbScreen = rgb;

if (ghbrInverse)
DeleteObject(ghbrInverse);

rgbInverse = ComputeInverseColor(rgb);
ghbrInverse = CreateSolidBrush(rgbInverse);
grgbInverse = rgbInverse;

/*
* For icons and cursors, we might need to update a few more things.
*/
if (giColorPalType != FT_BITMAP) {
/*
* Recombine the XOR and AND images now that there is a new screen
* color. This updates the image DC with the new color properly.
*/
if (gpImageCur) {
ImageDCCombine(ghdcImage, gcxImage, gcyImage, ghdcANDMask);

/*
* Is there a pending undo buffer? If so, it has to be
* recombined with the new screen color.
*/
if (ghbmUndo) {
ImageDCCombine(hdcTemp, gcxImage, gcyImage, hdcANDTemp);

/*
* Clean up the DC's that were allocated a little earlier.
*/
SelectObject(hdcANDTemp, hbmANDOld);
DeleteDC(hdcANDTemp);
SelectObject(hdcTemp, hbmOld);
DeleteDC(hdcTemp);
}
}

/*
* Reset the colors on the mouse buttons, just in case a screen
* or inverse screen color was assigned to either of them.
*/
SetLeftColor(giColorLeft, gfModeLeft);
SetRightColor(giColorRight, gfModeRight);

InvalidateRect(GetDlgItem(ghwndColor, DID_COLORBOX), NULL, TRUE);
}

ViewUpdate();
}



/************************************************************************
* MyCreateSolidBrush
*
*
* History:
*
************************************************************************/

STATICFN HBRUSH NEAR MyCreateSolidBrush(
DWORD rgb)
{
HDC hdc;
HDC hdcMem;
HBRUSH hbr;
HBRUSH hbrOld;
HBITMAP hbmPat;
HBITMAP hbmOld;

/*
* First, create a brush for the given RGB value.
*/
hbr = CreateSolidBrush(rgb);

/*
* Create a temporary memory DC.
*/
hdc = GetDC(ghwndMain);
hdcMem = CreateCompatibleDC(hdc);

/*
* Create a temporary bitmap.
*/
hbmPat = MyCreateBitmap(hdc, 8, 8, gnColorPalColors);
ReleaseDC(ghwndMain, hdc);

/*
* Draw the (possibly) dithered pattern on the temporary bitmap.
*/
hbmOld = SelectObject(hdcMem, hbmPat);
hbrOld = SelectObject(hdcMem, hbr);
PatBlt(hdcMem, 0, 0, 8, 8, PATCOPY);
SelectObject(hdcMem, hbrOld);
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);

/*
* Delete the first brush.
*/
DeleteObject(hbr);

/*
* Now create a pattern brush out of the (dithered) bitmap.
*/
hbr = CreatePatternBrush(hbmPat);

DeleteObject(hbmPat);

/*
* Return the pattern brush.
*/
return hbr;
}



/************************************************************************
* MyGetNearestColor
*
* This function returns the RGB value of the nearest color to the
* specified RGB value. If fMonoOK is TRUE, it takes into account
* the number of colors of the current image being edited. In other
* words, it will return the nearest solid color for a device that
* has the number of colors of the current image.
*
* Arguments:
* DWORD rgb - RGB value of the color.
* BOOL fMonoOK - TRUE if the returned color should be mapped to a
* color in a monochrome palette, if the current image
* is monochrome. A value of FALSE will return a
* color mapped to the closest color in a 16 color
* palette.
*
* History:
*
************************************************************************/

STATICFN DWORD NEAR MyGetNearestColor(
DWORD rgb,
BOOL fMonoOK)
{
HDC hdc;
HDC hdcMem;
DWORD rgbNearest;
HBITMAP hbmMem;
HBITMAP hbmOld;

hdc = GetDC(ghwndMain);
hdcMem = CreateCompatibleDC(hdc);
hbmMem = MyCreateBitmap(hdc, 1, 1, (fMonoOK) ? gnColorPalColors : 16);
hbmOld = SelectObject(hdcMem, hbmMem);
rgbNearest = GetNearestColor(hdcMem, rgb);
SelectObject(hdcMem, hbmOld);
DeleteObject(hbmMem);
DeleteDC(hdcMem);
ReleaseDC(ghwndMain, hdc);

return rgbNearest;
}



/************************************************************************
* ComputeInverseColor
*
* Computes the inverse value of a given rgb color.
*
* Arguments:
*
* History:
*
************************************************************************/

STATICFN DWORD NEAR ComputeInverseColor(
DWORD rgb)
{
HBITMAP hTempBit1;
HBITMAP hTempBit2;
HDC hTempDC1;
HDC hTempDC2;
HDC hdc;
HANDLE hOldObj1;
HANDLE hOldObj2;
DWORD rgbInv;

hdc = GetDC(ghwndMain);
hTempDC1 = CreateCompatibleDC(hdc);
hTempDC2 = CreateCompatibleDC(hdc);

/* create two temporary 1x1, 16 color bitmaps */
hTempBit1 = MyCreateBitmap(hdc, 1, 1, 16);
hTempBit2 = MyCreateBitmap(hdc, 1, 1, 16);

ReleaseDC(ghwndMain, hdc);

hOldObj1 = SelectObject(hTempDC1, hTempBit1);
hOldObj2 = SelectObject(hTempDC2, hTempBit2);

/* method for getting inverse color : set the given pixel (rgb) on
* one DC. Now blt it to the other DC using a SRCINVERT rop.
* This yields a pixel of the inverse color on the destination DC
*/
SetPixel(hTempDC1, 0, 0, rgb);
PatBlt(hTempDC2, 0, 0, 1, 1, WHITENESS);
BitBlt(hTempDC2, 0, 0, 1, 1, hTempDC1, 0, 0, SRCINVERT);
rgbInv = GetPixel(hTempDC2, 0, 0);

/* clean up ... */
SelectObject(hTempDC1, hOldObj1);
SelectObject(hTempDC2, hOldObj2);
DeleteObject(hTempBit1);
DeleteObject(hTempBit2);
DeleteDC(hTempDC1);
DeleteDC(hTempDC2);

/* ...and return the inverted RGB value */
return rgbInv;
}



/************************************************************************
* SetColorPalette
*
*
* History:
*
************************************************************************/

VOID SetColorPalette(
INT nColors,
INT iType,
BOOL fForce)
{
/*
* Quit if nothing changed (unless they are forcing it to be updated).
*/
if (!fForce && nColors == gnColorPalColors && iType == giColorPalType)
return;

/*
* Set the globals that all the color palette routines use.
*/
gnColorPalColors = nColors;
giColorPalType = iType;

if (gnColorPalColors == 2)
gargbCurrent = gargbMono;
else
gargbCurrent = gargbColor;

ShowWindow(GetDlgItem(ghwndColor, DID_COLORSCREENLABEL),
(giColorPalType == FT_BITMAP) ? SW_HIDE : SW_SHOW);
ShowWindow(GetDlgItem(ghwndColor, DID_COLORINVERSELABEL),
(giColorPalType == FT_BITMAP) ? SW_HIDE : SW_SHOW);

SetLeftColor(1, MODE_COLOR);
SetRightColor(0, MODE_COLOR);

InvalidateRect(GetDlgItem(ghwndColor, DID_COLORBOX), NULL, TRUE);
}



/************************************************************************
* RestoreDefaultColors
*
*
* History:
*
************************************************************************/

VOID RestoreDefaultColors(VOID)
{
INT i;

for (i = 0; i < COLORSMAX; i++)
gargbColor[i] = gargbDefaultColor[i];

SetColorPalette(16, giColorPalType, TRUE);
}