MYPAL.C


/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright 1993 - 1998 Microsoft Corporation.
* All rights reserved.
* This source code is only intended as a supplement to
* Microsoft Development Tools and/or WinHelp documentation.
* See these sources for detailed information regarding the
* Microsoft samples programs.
\******************************************************************************/

/***************************************************************************
* *
* PROGRAM : MyPal.c *
* *
* PURPOSE : Sets up a bar representation of the current physical *
* palette and displays useful information regarding *
* pixel colors and palette indices. *
* *
* FUNCTIONS : WinMain() - calls initialization function, *
* processes message loop *
* *
* WndProc() - Window function for app. Processes *
* window messages. *
* *
* ShowColor() - Displays a little box on each side of the *
* caption bar displaying the pixel color at the*
* mouse position. *
***************************************************************************/

#include <windows.h>
#include "mypal.h"

HANDLE hPal; /* Handle to the application's logical palette */
static INT nSizeX; /* Width of the application window */
static INT nSizeY; /* Height of the application window */
NPLOGPALETTE pLogPal; /* Pointer to program's logical palette */
INT nXBorder; /* Width of window border */
INT nXTitle; /* Width of title bar */
INT nYTitle; /* Height of title bar */
BOOL bCaptureOn; /* Indicates if mouse capture is on */
INT iIndex; /* Last index selected in palette */
CHAR szTitlebuf[90];/* Buffer for pixel and palette info. text */
HDC hDCGlobal; /* The Screen DC */
INT iNumColors; /* Number of colors supported by device */
INT iRasterCaps; /* Raster capabilities */
RECT rClientRect; /* Client rectangle coordinates */
DWORD dwPal[PALETTESIZE]; /* Stores palette entries for later lookup */
INT iGlobalXOffset;
INT iGlobalYOffset;
INT iYMiddle;

LONG APIENTRY WndProc(HWND hWnd, UINT iMessage, UINT wParam, LONG lParam);

/****************************************************************************
* *
* FUNCTION : void ShowColor(HWND hWnd, HDC hDC) *
* *
* PURPOSE : Displays a little box on each side of the caption bar *
* displaying the pixel color at the mouse position. *
* *
****************************************************************************/
VOID ShowColor (
HWND hWnd,
HDC hDC)
{
HBRUSH hBrush, hOldBrush;

hBrush = CreateSolidBrush ( PALETTEINDEX(iIndex) );
hOldBrush = SelectObject (hDC,hBrush) ;

GetWindowRect (hWnd, (LPRECT)&rClientRect);

PatBlt ( hDC,
rClientRect.left + nXTitle + nXBorder + 1,
rClientRect.top + nXBorder,
nXTitle,
nYTitle,
PATCOPY);

PatBlt(hDC,
rClientRect.right - ( 3 * nXTitle + nXBorder + 2),
rClientRect.top + nXBorder,
nXTitle,
nYTitle,
PATCOPY);
SelectObject (hDC, hOldBrush);
DeleteObject (hBrush) ;
}

/****************************************************************************
* *
* FUNCTION : WinMain(HANDLE, HANDLE, LPSTR, int) *
* *
* PURPOSE : Creates the app. window and processes the message loop. *
* *
****************************************************************************/
int APIENTRY WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
)
{
static CHAR szAppName [] = "MyPal";
HWND hWnd;
WNDCLASS wndclass;
MSG msg ;
INT xScreen;
INT yScreen;

UNREFERENCED_PARAMETER( lpCmdLine );

if (!hPrevInstance){
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = (WNDPROC) WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(hInstance, szAppName);
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
wndclass.lpszMenuName = szAppName;
wndclass.lpszClassName = szAppName;

if (!RegisterClass (&wndclass))
return FALSE ;
}

/* Do some global initializations */
xScreen = GetSystemMetrics (SM_CXSCREEN);
yScreen = GetSystemMetrics (SM_CYSCREEN);
nXBorder = (INT)GetSystemMetrics (SM_CXFRAME);
nXTitle = (INT)GetSystemMetrics (SM_CXSIZE);
nYTitle = (INT)GetSystemMetrics (SM_CYSIZE);
iIndex = 0;
bCaptureOn = FALSE;

hDCGlobal = GetDC (NULL);
iRasterCaps = GetDeviceCaps(hDCGlobal, RASTERCAPS);
iRasterCaps = (iRasterCaps & RC_PALETTE) ? TRUE : FALSE;

if (iRasterCaps)
iNumColors = GetDeviceCaps(hDCGlobal, SIZEPALETTE);
else
iNumColors = GetDeviceCaps( hDCGlobal, NUMCOLORS);
ReleaseDC (NULL,hDCGlobal);

nSizeX = ((xScreen - 2*nXBorder) / PALETTESIZE) * PALETTESIZE;

/* create the app. window */
hWnd = CreateWindow (szAppName,
"My Physical Palette ",
WS_OVERLAPPEDWINDOW,
(xScreen-nSizeX) / 2 - nXBorder,
yScreen - ( 4 * GetSystemMetrics (SM_CYCAPTION)),
nSizeX + 2 * nXBorder,
4 * GetSystemMetrics (SM_CYCAPTION),
NULL,
NULL,
hInstance,
NULL);
ShowWindow (hWnd, nCmdShow);
UpdateWindow (hWnd);

while (GetMessage (&msg, NULL, 0, 0)){
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}

return msg.wParam ;
}

/******************************************************************************
* *
* FUNCTION: WndProc(HWND, unsigned, WORD, LONG) *
* *
* PURPOSE: Processes window messages and sets up a 256 bar representation *
* of the current physical palette. Specifically, in response to: *
* *
* WM_CREATE -Allocates for and sets up a LOGPALETTE *
* structure, creates a logical palette the same *
* size as the physical palette and obtains a *
* handle to the logical palette. *
* *
* WM_DESTROY -Destroys the logical palette and shuts down app. *
* *
* WM_PAINT -Resizes client area to hold as many vertical *
* color bars as there are physical palette entries.*
* Also realises the current logical palette and *
* draws one color bar corresponding to each *
* palette entry *
* *
* WM_RBUTTONDOWN -Captures the mouse and initiates the below *
* process: *
* *
* WM_MOUSEMOVE -Following a WM_RBUTTONDOWN, if the right mouse *
* key is depressed, displays info about the *
* pixel RGB value and palette index of the mouse *
* coordinates. *
* *
* WM_RBUTTONUP -Release mouse capture and terminates the above *
* process *
* *
* WM_LBUTTONDOWN -Determines and displays the palette index and *
* RGB value of the bar under the mouse. *
* *
* WM_KEYDOWN -Allows use of the arrow keys in stepping thro' *
* palette entries. *
* *
*****************************************************************************/
LONG APIENTRY WndProc (
HWND hWnd,
UINT iMessage,
UINT wParam,
LONG lParam)
{
HDC hDC;
PAINTSTRUCT ps;
INT iLoop;
INT nStart;
HBRUSH hBrush;
HBRUSH hOldBrush;

MPOINT pt;
static INT nIncr;
static DWORD dwColor;
static DWORD dwLastColor;
static INT i, x;

switch (iMessage) {
case WM_DESTROY:
/* delete the handle to the logical palette if it has any
* color entries and quit.
*/
if (pLogPal->palNumEntries)
DeleteObject (hPal);
PostQuitMessage (0) ;
break ;

case WM_CREATE:
/* Allocate enough memory for a logical palette with
* PALETTESIZE entries and set the size and version fields
* of the logical palette structure.
*/
pLogPal = (NPLOGPALETTE) LocalAlloc (LMEM_FIXED,
(sizeof (LOGPALETTE) +
(sizeof (PALETTEENTRY) * (PALETTESIZE))));

if(!pLogPal){
MessageBox(hWnd, "<WM_CREATE> Not enough memory for palette.", NULL, MB_OK | MB_ICONHAND);
PostQuitMessage (0) ;
break;
}

pLogPal->palVersion = 0x300;
pLogPal->palNumEntries = PALETTESIZE;

/* fill in intensities for all palette entry colors */
for (iLoop = 0; iLoop < PALETTESIZE; iLoop++) {
*((WORD *) (&pLogPal->palPalEntry[iLoop].peRed)) = (WORD)iLoop;
pLogPal->palPalEntry[iLoop].peBlue = 0;
pLogPal->palPalEntry[iLoop].peFlags = PC_EXPLICIT;
}

/* create a logical color palette according the information
* in the LOGPALETTE structure.
*/
hPal = CreatePalette ((LPLOGPALETTE) pLogPal) ;
break;

case WM_GETMINMAXINFO:

((LPRGPT)lParam)->iInfo[6] = nXBorder * 2 + PALETTESIZE;
((LPRGPT)lParam)->iInfo[7] = nXBorder * 2 + nYTitle*3;

return DefWindowProc (hWnd, iMessage, wParam, lParam) ;
break;

case WM_PAINT:

/* Divide client width into equal-sized parts, one per palette
* entry, and re-calculate client width so that it will display
* exactly as many vertical bars as there are palette entries.
*/
GetClientRect(hWnd,(LPRECT) &rClientRect);
nSizeX = (rClientRect.right - rClientRect.left);
nSizeX = (nSizeX/iNumColors) * iNumColors;

nSizeY = rClientRect.bottom - rClientRect.top;
GetWindowRect(hWnd,(LPRECT) &rClientRect);

/* Adjust window width so that it can display exactly
* as many vertical bars( of equal width) as there are palette
* colors.
*/

SetWindowPos( hWnd,
(HWND)NULL,
0,
0,
nSizeX + 2*nXBorder,
rClientRect.bottom - rClientRect.top,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);

hDC = BeginPaint(hWnd, &ps);

/* Select the palette into the window device context and
* make the Palette Manager map the logical palette to the
* system palette (realize it).
*/
SelectPalette (hDC, hPal, 1);
RealizePalette (hDC);

/* Calculate width of each color bar to be displayed */
nIncr = nSizeX / iNumColors;

/* Paint the individual bars separately on the app. window */
for (nStart = iLoop = 0; iLoop < iNumColors; iLoop++){

/* Since this app. uses a logical palette, use the
* PALETTEINDEX macro to specify the palette entry
* index instead of using an explicit RGB value.
*/
hBrush = CreateSolidBrush (PALETTEINDEX (iLoop));
dwPal[iLoop] = GetNearestColor (hDC, PALETTEINDEX (iLoop) );
hOldBrush = SelectObject (hDC,hBrush) ;
PatBlt (hDC, nStart, 0, nIncr, nSizeY, PATCOPY);
nStart += nIncr;
SelectObject (hDC, hOldBrush);
DeleteObject (hBrush) ;
}
wsprintf (szTitlebuf, "MyPal Colors= %d", iNumColors);
SetWindowText (hWnd, (LPSTR)szTitlebuf);

EndPaint(hWnd,&ps);

break ;

case WM_MOUSEMOVE:

if (wParam & MK_RBUTTON) {

POINT pt;

#ifdef WIN16
/* Convert mouse position to screen coordinates */
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
#else
LONG2POINT(lParam, pt);
#endif
ClientToScreen(hWnd, &pt);

/* Get RGB value (color) of pixel under mouse coordinate */
dwColor = GetPixel(hDCGlobal, pt.x, pt.y);

/* If color value already exists in palette lookup table,
* obtain it's index.
*/
for (i=0 ; i < iNumColors ; i++)
if ( dwColor == dwPal[i] )
break;
iIndex = i;

/* If previous color value was not identical to current one,
* display color boxes on either side of title bar,
* the R, G, B values and palette index of current color.
*/
if (dwColor != dwLastColor) {
wsprintf ( szTitlebuf,
"MyPal Colors=%d Index=%d R=%3u G=%3u B=%3u",
iNumColors,
iIndex,
(WORD)(BYTE) GetRValue (dwColor),
(WORD)(BYTE) GetGValue (dwColor),
(WORD)(BYTE) GetBValue (dwColor));
SetWindowText (hWnd, (LPSTR)szTitlebuf);
ShowColor (hWnd, hDCGlobal);
dwLastColor = dwColor;
}
}
break;

case WM_RBUTTONDOWN:

/* Determine number of color bar under mouse, thus the index
* of color in palette.
*/
x = LOWORD(lParam);
iIndex = (x / nIncr );

wsprintf ( szTitlebuf,
"MyPal Colors=%d Index=%d PalSize=%d RasterCaps:%d",
iNumColors,
iIndex,
iNumColors,
iRasterCaps );

SetWindowText (hWnd, (LPSTR)szTitlebuf);

/* Set mouse capture so that subsequent WM_MOUSEMOVEs
* (with right mouse button depressed) will allow MyPal
* to display RGB info anywhere on the screen without losing
* the focus.
*/
SetCapture (hWnd);
bCaptureOn = TRUE;
hDCGlobal = GetDC(NULL);
if (hPal) {
SelectPalette (hDCGlobal, hPal, FALSE);
RealizePalette (hDCGlobal);
}
break;

case WM_RBUTTONUP:
/* Stops displaying RGB and palette info and releases mouse
* capture
*/
ReleaseDC (NULL, hDCGlobal);
bCaptureOn = FALSE;
ReleaseCapture ();
break;

case WM_MOVE:
/* If you have a wide column, this adds 1/2 so X is centered */
iGlobalXOffset = LOWORD (lParam);
iGlobalYOffset = HIWORD (lParam) + nXBorder;
break;

case WM_SIZE:
iYMiddle = (HIWORD (lParam)/2);
break;

case WM_LBUTTONDOWN:
case WM_KEYDOWN:

if (iMessage == WM_LBUTTONDOWN){
/* determine which column was hit by the mouse */
x = LOWORD(lParam);
iIndex = (x / nIncr );
}
else{
/* Use arrow keys to step thro' the palette entries */
switch (wParam) {
case VK_RIGHT:
case VK_UP:
/* go to next (higher) palette entry */
iIndex++;
break;
case VK_LEFT:
case VK_DOWN:
/* go to previous (lower) palette entry */
iIndex--;
break;
case VK_NEXT:
iIndex += 10;
break;
case VK_PRIOR:
iIndex -= 10;
break;
case VK_HOME:
/* go to first palette entry */
iIndex = 0;
break;
case VK_END:
/* go to last palette entry */
iIndex = iNumColors-1;
break;
default:
return 0L;
break;
}
/* Make sure the palette index is within range else
* set it to the limiting values and give a warning beep.
*/
if (iIndex < 0) {
iIndex = 0;
MessageBeep(1);
}
else{
if (iIndex > iNumColors-1) {
iIndex = iNumColors-1;
MessageBeep(1);
}
}

pt.x = (SHORT)((iIndex * nIncr) +
iGlobalXOffset +
((nIncr > 1) ? (nIncr / 2) : 1));
pt.y = (SHORT)(iYMiddle + iGlobalYOffset);

SetCursorPos (pt.x, pt.y);
}

if (TRUE == bCaptureOn) {
MessageBeep(1);
break;
}

/* Select & realize the palette or the colors > 0x7
* will not match up.
*/
hDC = GetDC(NULL);
SelectPalette (hDC, hPal, 1);
RealizePalette (hDC) ;

dwColor = GetNearestColor (hDC, PALETTEINDEX (iIndex));

wsprintf ( szTitlebuf,
"MyPal Colors=%d Index=%d R=%3u G=%3u B=%3u",
iNumColors,
iIndex,
(WORD)(BYTE)GetRValue (dwColor),
(WORD)(BYTE)GetGValue (dwColor),
(WORD)(BYTE)GetBValue (dwColor)
);

SetWindowText (hWnd, (LPSTR)szTitlebuf);
ShowColor (hWnd,hDC);
ReleaseDC(NULL, hDC);
break;

default:
return DefWindowProc (hWnd, iMessage, wParam, lParam) ;

}
return 0L ;
}