PALETTE.CXX

/******************************Module*Header*******************************\ 
* Module Name: palette.cxx
*
* Palette processing functions
*
* Copyright 1996 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <GL/gl.h>
#include "ssintrnl.hxx"
#include "palette.hxx"

#if(WINVER < 0x0400)
// Ordinarily not defined for versions before 4.00.
#define COLOR_3DDKSHADOW 21
#define COLOR_3DLIGHT 22
#define COLOR_INFOTEXT 23
#define COLOR_INFOBK 24
#endif

#define TKASSERT(x) SS_ASSERT( x, "palette processing failure\n" )

/******************************************************************************/

// Fixed palette support.

#define BLACK PALETTERGB(0,0,0)
#define WHITE PALETTERGB(255,255,255)
#define MAX_STATIC_COLORS (COLOR_INFOBK - COLOR_SCROLLBAR + 1)
static int gNumStaticColors = MAX_STATIC_COLORS;

// TRUE if static system color settings have been replaced with B&W settings.

// TRUE if original static colors saved
static BOOL tkStaticColorsSaved = FALSE;

// saved system static colors (initialize with default colors)
static COLORREF gacrSave[MAX_STATIC_COLORS];

// new B&W system static colors
static COLORREF gacrBlackAndWhite[] = {
WHITE, // COLOR_SCROLLBAR
BLACK, // COLOR_BACKGROUND
BLACK, // COLOR_ACTIVECAPTION
WHITE, // COLOR_INACTIVECAPTION
WHITE, // COLOR_MENU
WHITE, // COLOR_WINDOW
BLACK, // COLOR_WINDOWFRAME
BLACK, // COLOR_MENUTEXT
BLACK, // COLOR_WINDOWTEXT
WHITE, // COLOR_CAPTIONTEXT
WHITE, // COLOR_ACTIVEBORDER
WHITE, // COLOR_INACTIVEBORDER
WHITE, // COLOR_APPWORKSPACE
BLACK, // COLOR_HIGHLIGHT
WHITE, // COLOR_HIGHLIGHTTEXT
WHITE, // COLOR_BTNFACE
BLACK, // COLOR_BTNSHADOW
BLACK, // COLOR_GRAYTEXT
BLACK, // COLOR_BTNTEXT
BLACK, // COLOR_INACTIVECAPTIONTEXT
BLACK, // COLOR_BTNHIGHLIGHT
BLACK, // COLOR_3DDKSHADOW
WHITE, // COLOR_3DLIGHT
BLACK, // COLOR_INFOTEXT
WHITE // COLOR_INFOBK
};
static INT gaiStaticIndex[] = {
COLOR_SCROLLBAR ,
COLOR_BACKGROUND ,
COLOR_ACTIVECAPTION ,
COLOR_INACTIVECAPTION ,
COLOR_MENU ,
COLOR_WINDOW ,
COLOR_WINDOWFRAME ,
COLOR_MENUTEXT ,
COLOR_WINDOWTEXT ,
COLOR_CAPTIONTEXT ,
COLOR_ACTIVEBORDER ,
COLOR_INACTIVEBORDER ,
COLOR_APPWORKSPACE ,
COLOR_HIGHLIGHT ,
COLOR_HIGHLIGHTTEXT ,
COLOR_BTNFACE ,
COLOR_BTNSHADOW ,
COLOR_GRAYTEXT ,
COLOR_BTNTEXT ,
COLOR_INACTIVECAPTIONTEXT,
COLOR_BTNHIGHLIGHT ,
COLOR_3DDKSHADOW ,
COLOR_3DLIGHT ,
COLOR_INFOTEXT ,
COLOR_INFOBK
};

#define RESTORE_FROM_REGISTRY 1
#if RESTORE_FROM_REGISTRY
// Registry names for the system colors.
static CHAR *gaszSysClrNames[] = {
"Scrollbar", // COLOR_SCROLLBAR 0
"Background", // COLOR_BACKGROUND 1 (also COLOR_DESKTOP)
"ActiveTitle", // COLOR_ACTIVECAPTION 2
"InactiveTitle", // COLOR_INACTIVECAPTION 3
"Menu", // COLOR_MENU 4
"Window", // COLOR_WINDOW 5
"WindowFrame", // COLOR_WINDOWFRAME 6
"MenuText", // COLOR_MENUTEXT 7
"WindowText", // COLOR_WINDOWTEXT 8
"TitleText", // COLOR_CAPTIONTEXT 9
"ActiveBorder", // COLOR_ACTIVEBORDER 10
"InactiveBorder", // COLOR_INACTIVEBORDER 11
"AppWorkspace", // COLOR_APPWORKSPACE 12
"Hilight", // COLOR_HIGHLIGHT 13
"HilightText", // COLOR_HIGHLIGHTTEXT 14
"ButtonFace", // COLOR_BTNFACE 15 (also COLOR_3DFACE)
"ButtonShadow", // COLOR_BTNSHADOW 16 (also COLOR_3DSHADOW)
"GrayText", // COLOR_GRAYTEXT 17
"ButtonText", // COLOR_BTNTEXT 18
"InactiveTitleText", // COLOR_INACTIVECAPTIONTEXT 19
"ButtonHilight", // COLOR_BTNHIGHLIGHT 20 (also COLOR_3DHILIGHT)
"ButtonDkShadow", // COLOR_3DDKSHADOW 21
"ButtonLight", // COLOR_3DLIGHT 22
"InfoText", // COLOR_INFOTEXT 23
"InfoWindow" // COLOR_INFOBK 24
};

static BOOL GetRegistrySysColors(COLORREF *, int);
#endif

unsigned char ss_ComponentFromIndex(int i, int nbits, int shift );
static int ss_PixelFormatDescriptorFromDc( HDC hdc, PIXELFORMATDESCRIPTOR *Pfd );

/******************************************************************************/

#if RESTORE_FROM_REGISTRY
/******************************Public*Routine******************************\
* GetRegistrySysColors
*
* Reads the Control Panel's color settings from the registry and stores
* those values in pcr. If we fail to get any value, then the corresponding
* entry in pcr is not modified.
\**************************************************************************/

static BOOL GetRegistrySysColors(COLORREF *pcr, int nColors)
{
BOOL bRet = FALSE;
long lRet;
HKEY hkSysColors = (HKEY) NULL;
int i;
DWORD dwDataType;
char achColor[64];
DWORD cjColor;

TKASSERT(nColors <= gNumStaticColors);

// Open the key for the system color settings.

lRet = RegOpenKeyExA(HKEY_CURRENT_USER,
"Control Panel\\Colors",
0,
KEY_QUERY_VALUE,
&hkSysColors);

if ( lRet != ERROR_SUCCESS )
{
goto GetRegistrySysColors_exit;
}

// Read each system color value. The names are stored in the global
// array of char *, gaszSysClrNames.

for (i = 0; i < nColors; i++)
{
cjColor = sizeof(achColor);
lRet = RegQueryValueExA(hkSysColors,
(LPSTR) gaszSysClrNames[i],
(LPDWORD) NULL,
&dwDataType,
(LPBYTE) achColor,
&cjColor);

TKASSERT(lRet != ERROR_MORE_DATA);

if ( lRet == ERROR_SUCCESS && dwDataType == REG_SZ )
{
DWORD r, g, b;

sscanf(achColor, "%ld %ld %ld", &r, &g, &b);
pcr[i] = RGB(r, g, b);
}
}

bRet = TRUE;

GetRegistrySysColors_exit:
if (hkSysColors)
RegCloseKey(hkSysColors);

return bRet;
}
#endif

/******************************Public*Routine******************************\
* GrabStaticEntries
*
* Support routine for Realize to manage the static system color
* usage.
*
* This function will save the current static system color usage state.
* It will fail if:
*
* 1. TK is not in "sys color in use state but system palette is in
* SYSPAL_NOSTATIC mode. This means that another app still possesses
* the static system colors. If this happens <TBD>
*
* Side effect:
* If system colors are changed, then WM_SYSCOLORCHANGE message is
* broadcast to all top level windows.
*
* Returns:
* TRUE if successful, FALSE otherwise (see above).
\**************************************************************************/

BOOL
SS_PAL::GrabStaticEntries()
{
int i;
BOOL bRet = FALSE;

// Do nothing if sys colors already in use.

if ( !bSystemColorsInUse )
{
// Take possession only if no other app has the static colors.
// How can we tell? If the return from SetSystemPaletteUse is
// SYSPAL_STATIC, then no other app has the statics. If it is
// SYSPAL_NOSTATIC, someone else has them and we must fail.
//
// SetSystemPaletteUse is properly synchronized internally
// so that it is atomic.
//
// Because we are relying on SetSystemPaletteUse to synchronize TK,
// it is important to observe the following order for grabbing and
// releasing:
//
// Grab call SetSystemPaletteUse and check for SYSPAL_STATIC
// save sys color settings
// set new sys color settings
//
// Release restore sys color settings
// call SetSystemPaletteUse

// potential pitfall here, if a 'bad' app has not released the static
// colors on deactivation.
if ( SetSystemPaletteUse( hdc, SYSPAL_NOSTATIC ) == SYSPAL_STATIC )
{
// Save current sys color settings.

for (i = COLOR_SCROLLBAR; i <= COLOR_BTNHIGHLIGHT; i++)
gacrSave[i - COLOR_SCROLLBAR] = GetSysColor(i);

bSystemColorsInUse = TRUE;

// Set b&w sys color settings.

SetSysColors(gNumStaticColors, gaiStaticIndex, gacrBlackAndWhite);

// Inform all other top-level windows of the system color change.

PostMessage(HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0);

bRet = TRUE;
} else {
// handle case where can't get sys colors
}
}
else
bRet = TRUE;

return bRet;
}

/******************************Public*Routine******************************\
* ReleaseStaticEntries
*
* Support routine for Realize to manage the static system color
* usage.
*
* This function will reset the current static system color usage state.
* It will fail if:
*
* 1. TK is not in a "sys colors in use" state. If we are in this case,
* then the static system colors do not need to be released.
*
* Side effect:
* If system colors are changed, then WM_SYSCOLORCHANGE message is
* broadcast to all top level windows.
*
* Returns:
* TRUE if successful, FALSE otherwise (see above).
\**************************************************************************/

BOOL
SS_PAL::ReleaseStaticEntries()
{
BOOL bRet = FALSE;

// Do nothing if sys colors not in use.

if ( bSystemColorsInUse )
{
#if RESTORE_FROM_REGISTRY
// Replace saved system colors with registry values. We do it now
// rather than earlier because someone may have changed registry while
// TK app was running in the foreground (very unlikely, but it could
// happen).
//
// Also, we still try to save current setting in GrabStaticEntries so
// that if for some reason we fail to grab one or more of the colors
// from the registry, we can still fall back on what we grabbed via
// GetSysColors (even though there is a chance its the wrong color).

GetRegistrySysColors(gacrSave, gNumStaticColors);
#endif

// Do this now, since SetSysColors() generates WM_SYSCOLORCHANGE,
// which can cause this routine to be re-entered
// back to here.
bSystemColorsInUse = FALSE;

// Return the system palette to SYSPAL_STATIC.

SetSystemPaletteUse( hdc, SYSPAL_STATIC );

// Restore the saved system color settings.

SetSysColors(gNumStaticColors, gaiStaticIndex, gacrSave);

// Inform all other top-level windows of the system color change.

PostMessage(HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0);

// Reset the "sys colors in use" state and return success.

bSystemColorsInUse = FALSE;
bRet = TRUE;
}

return bRet;
}

// Gamma correction factor * 10
#define GAMMA_CORRECTION 14

// Maximum color distance with 8-bit components
#define MAX_COL_DIST (3*256*256L)

// Number of static colors
#define STATIC_COLORS 20

// Flags used when matching colors
#define EXACT_MATCH 1
#define COLOR_USED 1

// Conversion tables for n bits to eight bits

#if GAMMA_CORRECTION == 10
// These tables are corrected for a gamma of 1.0
static unsigned char abThreeToEight[8] =
{
0, 0111 >> 1, 0222 >> 1, 0333 >> 1, 0444 >> 1, 0555 >> 1, 0666 >> 1, 0377
};
static unsigned char abTwoToEight[4] =
{
0, 0x55, 0xaa, 0xff
};
static unsigned char abOneToEight[2] =
{
0, 255
};
#else
// These tables are corrected for a gamma of 1.4
static unsigned char abThreeToEight[8] =
{
0, 63, 104, 139, 171, 200, 229, 255
};
static unsigned char abTwoToEight[4] =
{
0, 116, 191, 255
};
static unsigned char abOneToEight[2] =
{
0, 255
};
#endif

// Table which indicates which colors in a 3-3-2 palette should be
// replaced with the system default colors
#if GAMMA_CORRECTION == 10
static int aiDefaultOverride[STATIC_COLORS] =
{
0, 4, 32, 36, 128, 132, 160, 173, 181, 245,
247, 164, 156, 7, 56, 63, 192, 199, 248, 255
};
#else
static int aiDefaultOverride[STATIC_COLORS] =
{
0, 3, 24, 27, 64, 67, 88, 173, 181, 236,
247, 164, 91, 7, 56, 63, 192, 199, 248, 255
};
#endif

unsigned char
ss_ComponentFromIndex(int i, int nbits, int shift)
{
unsigned char val;

TKASSERT(nbits >= 1 && nbits <= 3);

val = i >> shift;
switch (nbits)
{
case 1:
return abOneToEight[val & 1];

case 2:
return abTwoToEight[val & 3];

case 3:
return abThreeToEight[val & 7];
}
return 0;
}

// System default colors
static PALETTEENTRY apeDefaultPalEntry[STATIC_COLORS] =
{
{ 0, 0, 0, 0 },
{ 0x80,0, 0, 0 },
{ 0, 0x80,0, 0 },
{ 0x80,0x80,0, 0 },
{ 0, 0, 0x80, 0 },
{ 0x80,0, 0x80, 0 },
{ 0, 0x80,0x80, 0 },
{ 0xC0,0xC0,0xC0, 0 },

{ 192, 220, 192, 0 },
{ 166, 202, 240, 0 },
{ 255, 251, 240, 0 },
{ 160, 160, 164, 0 },

{ 0x80,0x80,0x80, 0 },
{ 0xFF,0, 0, 0 },
{ 0, 0xFF,0, 0 },
{ 0xFF,0xFF,0, 0 },
{ 0, 0, 0xFF, 0 },
{ 0xFF,0, 0xFF, 0 },
{ 0, 0xFF,0xFF, 0 },
{ 0xFF,0xFF,0xFF, 0 }
};

/******************************Public*Routine******************************\
*
* UpdateStaticMapping
*
* Computes the best match between the current system static colors
* and a 3-3-2 palette
*
* History:
* Tue Aug 01 18:18:12 1995-by-Drew Bliss [drewb]
* Created
*
\**************************************************************************/

static void
UpdateStaticMapping(PALETTEENTRY *pe332Palette)
{
HPALETTE hpalStock;
int iStatic, i332;
int iMinDist, iDist;
int iDelta;
int iMinEntry;
PALETTEENTRY *peStatic, *pe332;

hpalStock = (HPALETTE) GetStockObject(DEFAULT_PALETTE);

// The system should always have one of these
TKASSERT(hpalStock != NULL);
// Make sure there's the correct number of entries
TKASSERT(GetPaletteEntries(hpalStock, 0, 0, NULL) == STATIC_COLORS);

// Get the current static colors
GetPaletteEntries(hpalStock, 0, STATIC_COLORS, apeDefaultPalEntry);

// Zero the flags in the static colors because they are used later
peStatic = apeDefaultPalEntry;
for (iStatic = 0; iStatic < STATIC_COLORS; iStatic++)
{
peStatic->peFlags = 0;
peStatic++;
}

// Zero the flags in the incoming palette because they are used later
pe332 = pe332Palette;
for (i332 = 0; i332 < 256; i332++)
{
pe332->peFlags = 0;
pe332++;
}

// Try to match each static color exactly
// This saves time by avoiding the least-squares match for each
// exact match
peStatic = apeDefaultPalEntry;
for (iStatic = 0; iStatic < STATIC_COLORS; iStatic++)
{
pe332 = pe332Palette;
for (i332 = 0; i332 < 256; i332++)
{
if (peStatic->peRed == pe332->peRed &&
peStatic->peGreen == pe332->peGreen &&
peStatic->peBlue == pe332->peBlue)
{
TKASSERT(pe332->peFlags != COLOR_USED);

peStatic->peFlags = EXACT_MATCH;
pe332->peFlags = COLOR_USED;
aiDefaultOverride[iStatic] = i332;

break;
}

pe332++;
}

peStatic++;
}

// Match each static color as closely as possible to an entry
// in the 332 palette by minimized the square of the distance
peStatic = apeDefaultPalEntry;
for (iStatic = 0; iStatic < STATIC_COLORS; iStatic++)
{
// Skip colors already matched exactly
if (peStatic->peFlags == EXACT_MATCH)
{
peStatic++;
continue;
}

iMinDist = MAX_COL_DIST+1;
#if DBG
iMinEntry = -1;
#endif

pe332 = pe332Palette;
for (i332 = 0; i332 < 256; i332++)
{
// Skip colors already used
if (pe332->peFlags == COLOR_USED)
{
pe332++;
continue;
}

// Compute Euclidean distance squared
iDelta = pe332->peRed-peStatic->peRed;
iDist = iDelta*iDelta;
iDelta = pe332->peGreen-peStatic->peGreen;
iDist += iDelta*iDelta;
iDelta = pe332->peBlue-peStatic->peBlue;
iDist += iDelta*iDelta;

if (iDist < iMinDist)
{
iMinDist = iDist;
iMinEntry = i332;
}

pe332++;
}

TKASSERT(iMinEntry != -1);

// Remember the best match
aiDefaultOverride[iStatic] = iMinEntry;
pe332Palette[iMinEntry].peFlags = COLOR_USED;

peStatic++;
}

// Zero the flags in the static colors because they may have been
// set. We want them to be zero so the colors can be remapped
peStatic = apeDefaultPalEntry;
for (iStatic = 0; iStatic < STATIC_COLORS; iStatic++)
{
peStatic->peFlags = 0;
peStatic++;
}

// Reset the 332 flags because we may have set them
pe332 = pe332Palette;
for (i332 = 0; i332 < 256; i332++)
{
pe332->peFlags = PC_NOCOLLAPSE;
pe332++;
}
}

/******************************Public*Routine******************************\
* FlushPalette
*
* Because of Win 3.1 compatibility, GDI palette mapping always starts
* at zero and stops at the first exact match. So if there are duplicates,
* the higher colors aren't mapped to--which is often a problem if we
* are trying to make to any of the upper 10 static colors. To work around
* this, we flush the palette to all black.
*
\**************************************************************************/

void
SS_PAL::Flush()
{
LOGPALETTE *pPal;
HPALETTE hpalBlack, hpalOld;
int i;

if( nEntries == 256 )
{
pPal = (LOGPALETTE *) LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT,
sizeof(LOGPALETTE) + nEntries * sizeof(PALETTEENTRY));

if (pPal)
{
pPal->palVersion = 0x300;
pPal->palNumEntries = nEntries;

// Mark everything PC_NOCOLLAPSE and PC_RESERVED to force every
// thing into the palette. Colors are already black because
// we zero initialized during memory allocation.

for (i = 0; i < nEntries; i++)
{
pPal->palPalEntry[i].peFlags = PC_NOCOLLAPSE | PC_RESERVED;
}

hpalBlack = CreatePalette(pPal);
LocalFree(pPal);

hpalOld = SelectPalette(hdc, hpalBlack, FALSE);
RealizePalette(hdc);

SelectPalette(hdc, hpalOld, FALSE);
DeleteObject(hpalBlack);
}
}
}

/******************************Public*Routine******************************\
* Realize
*
* Select the given palette in background or foreground mode (as specified
* by the bForceBackground flag), and realize the palette.
*
* If static system color usage is set, the system colors are replaced.
\**************************************************************************/

long
SS_PAL::Realize( HWND hwndArg, HDC hdcArg, BOOL bForceBackground )
{
// cache:
hwnd = hwndArg;
hdc = hdcArg;

if( bTakeOver ) {
// Easy case
SetSystemPaletteUse(hdc, SYSPAL_NOSTATIC);
SelectPalette(hdc, hPal, bForceBackground );
RealizePalette(hdc);
return 1;
}


if( bFlush ) {
Flush();
bFlush = FALSE;
}

return Realize( bForceBackground );
}

long
SS_PAL::Realize( BOOL bForceBackground )
{
long Result = -1;
BOOL bHaveSysPal = TRUE;

SS_DBGLEVEL2( SS_LEVEL_INFO, "SS_PAL::Realize: %d for %d\n", bForceBackground, hwnd );

// If static system color usage is set, prepare to take over the
// system palette.

if( bUseStatic )
{
// If foreground, take over the static colors. If background, release
// the static colors.

if ( !bForceBackground )
{
// If GrabStaticEntries succeeds, then it is OK to take over the
// static colors. If not <mf:TBD>

bHaveSysPal = GrabStaticEntries();
}
else
{
// If we are currently using the system colors (bSystemColorsInUse)
// and Realize was called with bForceBackground set, we
// are being deactivated and must release the static system colors.

ReleaseStaticEntries();
}

// Rerealize the palette.
//
// If set to TRUE, bForceBackground will force the palette to be
// realized as a background palette, regardless of focus. This
// will happen anyway if the TK window does not have the keyboard focus.

if ( (bForceBackground || bHaveSysPal) &&
UnrealizeObject( hPal ) &&
NULL != SelectPalette( hdc, hPal, bForceBackground ) )
{
Result = RealizePalette( hdc );
}

// If some rude app still has the system colors and we're in the
// foreground, make the best of it.
if( !bForceBackground && !bHaveSysPal ) {
if( UnrealizeObject( hPal ) &&
NULL != SelectPalette( hdc, hPal, TRUE ) )
{
Result = RealizePalette( hdc );
}
}
}
else
{
if ( NULL != SelectPalette( hdc, hPal, FALSE ) )
{
Result = RealizePalette( hdc );
}
}

return( Result );
}

/******************************Public*Routine******************************\
* SS_PAL constructor
*
* This creates the palette, but does not select or realize it
*
\**************************************************************************/

SS_PAL::SS_PAL( HDC hdcArg, PIXELFORMATDESCRIPTOR *ppfd, BOOL bTakeOverPalette )
{
hwnd = 0;
hPal = 0;
bUseStatic = FALSE;
bSystemColorsInUse = FALSE;
pfd = *ppfd; // this is for palette purposes only (other fields may not apply)
hdc = hdcArg;
bTakeOver = bTakeOverPalette; // mf: for now, when this is set, it means
// the screen saver is running in full screen mode - implying that
// interaction with other apps not necessary

if( bTakeOver ) {
bFlush = FALSE;
bUseStatic = TRUE;
} else {
bFlush = TRUE;
bUseStatic = ppfd->dwFlags & PFD_NEED_SYSTEM_PALETTE;
}

if( bUseStatic )
// save current static palette usage so we can restore it
uiOldStaticUse = GetSystemPaletteUse( hdc );

paletteManageProc = NullPaletteManageProc;

// Now create the palette and return
hPal = MakeRGBPalette();
SS_ASSERT( hPal, "SS_PAL constructor failure\n" );
}

/******************************Public*Routine******************************\
* SS_PAL destructor

\**************************************************************************/

SS_PAL::~SS_PAL()
{
if( bUseStatic )
{
if( uiOldStaticUse )
SetSystemPaletteUse(hdc, uiOldStaticUse);

PostMessage(HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0);
}
if( hPal ) {
SelectPalette( hdc, (HPALETTE) GetStockObject(DEFAULT_PALETTE), TRUE );
DeleteObject( hPal );
}
}

/******************************Public*Routine******************************\
* ReCreateRGBPalette
*
*
\**************************************************************************/

void
SS_PAL::ReCreateRGBPalette()
{
if( bTakeOver )
return;

HPALETTE hPalTmp = hPal;
hPal = MakeRGBPalette();
if( hPal ) {
DeleteObject( hPalTmp );
bFlush = TRUE;
}
}

/******************************Public*Routine******************************\
* MakeRGBPalette
*
* Creates an HPALETTE with values required for a logical rgb palette.
* If bUseStatic is TRUE, the static system
* colors will be overridden. Otherwise, the PALETTEENTRY array will be
* fixed up to contain the default static system colors.
*
\**************************************************************************/

HPALETTE
SS_PAL::MakeRGBPalette()
{
LOGPALETTE *pPal;
HPALETTE hpal;
int count, i;
PIXELFORMATDESCRIPTOR *ppfd = &pfd;

count = 1 << ppfd->cColorBits;
nEntries = count;
pPal = (PLOGPALETTE)LocalAlloc(LMEM_FIXED, sizeof(LOGPALETTE) +
count * sizeof(PALETTEENTRY));
if( !pPal )
return (HPALETTE) 0;

pPal->palVersion = 0x300;
pPal->palNumEntries = count;

PALETTEENTRY *pEntry = pPal->palPalEntry;

for ( i = 0; i < count ; i++, pEntry++ )
{
pEntry->peRed = ss_ComponentFromIndex(i, ppfd->cRedBits,
ppfd->cRedShift);
pEntry->peGreen = ss_ComponentFromIndex(i, ppfd->cGreenBits,
ppfd->cGreenShift);
pEntry->peBlue = ss_ComponentFromIndex(i, ppfd->cBlueBits,
ppfd->cBlueShift);
pEntry->peFlags = PC_NOCOLLAPSE;
}

if( count == 256 )
{
// If app set static system color usage for fixed palette support,
// setup to take over the static colors. Otherwise, fixup the
// static system colors.

if ( bUseStatic )
{
// Black and white already exist as the only remaining static
// colors. Let those remap. All others should be put into
// the palette (i.e., set PC_NOCOLLAPSE).

pPal->palPalEntry[0].peFlags = 0;
pPal->palPalEntry[255].peFlags = 0;
}
else
{
// The defaultOverride array is computed assuming a 332
// palette where red has zero shift, etc.

if ( (3 == ppfd->cRedBits) && (0 == ppfd->cRedShift) &&
(3 == ppfd->cGreenBits) && (3 == ppfd->cGreenShift) &&
(2 == ppfd->cBlueBits) && (6 == ppfd->cBlueShift) )
{
pEntry = pPal->palPalEntry;
UpdateStaticMapping( pEntry );

for ( i = 0 ; i < STATIC_COLORS ; i++)
{
pEntry[aiDefaultOverride[i]] = apeDefaultPalEntry[i];
}
}
}
}

hpal = CreatePalette(pPal);
LocalFree(pPal);
return hpal;
}