Figure 2   Steps for DIBVIEW

step one
Baby Bitmap Viewer. CDib is implemented using the Video for Windows DrawDib API. Draws bitmaps correctly but doesn't handle palette messages. If you open one bitmap, then another, the first bitmap looks psychedelic.
step two
CDib implemented as a CBitmap using standard Win32 API calls instead of DrawDib. Option to dither using DrawDib. DIBVIEW still doesn't handle palette messages.
step three
Adds support for both MDI and SDI versions through a single compile-time #define variable, _MDI. Adds palette message handlers to do correct foreground/background palette realization. Now when you activate one window—either one of multiple instances of DIBVIEW, or one of multiple child windows in the MDI version—the other windows realize their palettes in the background.
step four
Same as step three, but now the palette-handling functionality is encapsulated in a new class, CPalMsgHandler, that uses CMsgHook to handle palette messages in totally generic fashion. You can use CPalMsgHandler in any app that does palettes. CMsgHook is a generic subclassing mechanism for MFC windows. Step four also adds a slew of features: printing, zooming, BITMAPINFOHEADER displayed in user's choice of font, and font bigger/smaller buttons. (This step will be discussed in Part II.)


Figure 4   Dib1.cpp


////////////////////////////////////////////////////////////////
// 1996 Microsoft Systems Journal. 
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
// CDib - Device Independent Bitmap.
// This implementation uses the Video for Windows DrawDib API to draw.
//
#include "StdAfx.h"
#include "Dib.h"

IMPLEMENT_DYNAMIC(CDib, CObject)

CDib::CDib()
{
   m_pbmih = NULL;
   m_hdd = NULL;
}

CDib::~CDib()
{
   DeleteObject();
}

//////////////////
// Common function to delete stuff
//
void CDib::DeleteObject()
{
   if (m_hdd) {
      DrawDibClose(m_hdd);
      m_hdd = NULL;
   }
   if (m_pbmih) {
      delete [] (char*)m_pbmih;
      m_pbmih = NULL;
   }
}

//////////////////
// Get number of palette colors
// 
UINT CDib::GetNumPaletteColors()
{
   // Calculate # entries in color table:
   // if biClrUsed is not specified, then use:
   //    (2,16,256)  for (1,4,8)-bit bitmaps;
   //    0           for 24, 32-bit bitmaps (no color table)
   // 
   UINT nColors=m_pbmih->biClrUsed;
   if (nColors==0 && m_pbmih->biBitCount<=8)
      nColors = 1<<m_pbmih->biBitCount;
   return nColors;
}

//////////////////
// Get ptr to actual bits.
// 
LPBYTE CDib::GetBits()
{
   return (LPBYTE)m_pbmih + m_pbmih->biSize 
      + GetNumPaletteColors()*sizeof(RGBQUAD); 
}

// First two bytes of bitmap file should be "BM"
const BITMAP_TYPE =  (WORD)('M' << 8) | 'B';

 •
 •
 •
////////////////////////////////////////////////////////////////
// Draw routine for a DIB on caller's DC. Like above, but
// caller can specify source and destination rectangles.
// Use Video for Windows DrawDIB API to do the work.
//
BOOL CDib::Draw(CDC& dc, const CRect* rcDst, const CRect* rcSrc)
{
   if (!m_pbmih)
      return FALSE;

   if (!m_hdd)
      VERIFY(m_hdd = DrawDibOpen());

   CRect rc;
   if (!rcSrc) {
      // if no source rect, use whole bitmap
      rc.SetRect(0, 0, m_pbmih->biWidth, m_pbmih->biHeight);
      rcSrc=&rc;
   }
   if (!rcDst)
      // if no destination rect, use source
      rcDst=rcSrc;

   // This is as easy as it gets in Windows.
   return DrawDibDraw(m_hdd, dc,
      rcDst->left, rcDst->top, rcDst->Width(), rcDst->Height(),
      m_pbmih, GetBits(), 
      rcSrc->left, rcSrc->top, rcSrc->Width(), rcSrc->Height(), 0);
}

Figure 11   DIBVIEW3

Debug.cpp


 ////////////////////////////////////////////////////////////////
 // Copyright 1996 Microsoft Systems Journal. 
 // If this program works, it was written by Paul DiLascia.
 // If not, I don't know who wrote it.
 //
 // General purpose debugging utilities
 // 
 #include "StdAfx.h"
 #include "Debug.h"
 #include <afxpriv.h> // for MFC WM_ messages
 
 #ifdef _DEBUG
 
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 
 int CTraceFn::nIndent=-1;      // current indent level
 
 #define _countof(array) (sizeof(array)/sizeof(array[0]))
 
 ////////////////
 // These functions are copied from dumpout.cpp in the MFC source,
 // with my modification to do indented TRACEing
 //
 void AFXAPI AfxDump(const CObject* pOb)
 {
    afxDump << pOb;
 }
 
 void AFX_CDECL AfxTrace(LPCTSTR lpszFormat, ...)
 {
 #ifdef _DEBUG // all AfxTrace output is controlled by afxTraceEnabled
    if (!afxTraceEnabled)
       return;
 #endif
 
    va_list args;
    va_start(args, lpszFormat);
 
    int nBuf;
    TCHAR szBuffer[512];
 
    nBuf = _vstprintf(szBuffer, lpszFormat, args);
    ASSERT(nBuf < _countof(szBuffer));
 
    // PD: Here are my added lines to do the indenting. Search
    // for newlines and insert prefix before each one. Yawn.
    //
    static BOOL bStartNewLine = TRUE;
    char* nextline;
    for (char* start = szBuffer; *start; start=nextline+1) {
       if (bStartNewLine) {
          if ((afxTraceFlags & traceMultiApp) && (AfxGetApp() != NULL))
             afxDump << AfxGetApp()->m_pszExeName << ": ";
          afxDump << CString(' ',CTraceFn::nIndent);
          bStartNewLine = FALSE;
       }
       nextline = strchr(start, '\n');
       if (nextline) {
          *nextline = 0; // terminate string at newline
          bStartNewLine = TRUE;
       }
       afxDump << start;
       if (!nextline)
          break;
       afxDump << "\n"; // the one I terminated
    }
       
    va_end(args);
 }
 
 //////////////////
 // Get window name in the form classname[HWND,title]
 // Searches all the parents for a window with a title.
 //
 CString sDbgName(CWnd* pWnd)
 {
    CString sTitle;
    HWND hwnd = pWnd->GetSafeHwnd();
    if (hwnd==NULL)
       sTitle = "NULL";
    else if (!::IsWindow(hwnd))
       sTitle = "[bad window]";
    else {
       sTitle = "[no title]";
       for (CWnd* pw = pWnd; pw; pw = pw->GetParent()) {
          if (pw->GetWindowTextLength() > 0) {
             pw->GetWindowText(sTitle);
             break;
          }
       }
    }
    CString s;
    s.Format("%s[0x%04x,\"%s\"]", 
       pWnd ? pWnd->GetRuntimeClass()->m_lpszClassName : "NULL",
       hwnd, (LPCTSTR)sTitle);
    return s;
 }
 
 struct {
    UINT     msg;
    LPCTSTR  name;
 } MsgData[] = {
    { WM_CREATE,_T("WM_CREATE") },
    { WM_DESTROY,_T("WM_DESTROY") },
    { WM_MOVE,_T("WM_MOVE") },
    { WM_SIZE,_T("WM_SIZE") },
    .
    . // (about 220 messages)
    .
    { WM_OCC_LOADFROMSTREAM,_T("*WM_OCC_LOADFROMSTREAM") },
    { WM_OCC_LOADFROMSTORAGE,_T("*WM_OCC_LOADFROMSTORAGE") },
    { WM_OCC_INITNEW,_T("*WM_OCC_INITNEW") },
    { WM_QUEUE_SENTINEL,_T("*WM_QUEUE_SENTINEL") },
    { 0,NULL }
 };
 
 ////////////////
 // This class is basically just an array of 1024 strings,
 // the names of each WM_ message. Constructor initializes it.
 //
 class CWndMsgMap {
    static LPCTSTR Names[];          // array of WM_ message names
 public:
    CWndMsgMap();                    // constructor initializes them
    CString GetMsgName(UINT msg);    // get name of message
 };
 LPCTSTR CWndMsgMap::Names[WM_USER]; // name of each WM_ message
 
 //////////////////
 // Initialize array from sparse data
 //
 CWndMsgMap::CWndMsgMap()
 {
    // copy sparse MsgData into table
    memset(Names, 0, sizeof(Names));
    for (int i=0; MsgData[i].msg; i++)     
       Names[MsgData[i].msg] = MsgData[i].name;
 }
 
 ////////////////
 // Get the name of a WM_ message
 //
 CString CWndMsgMap::GetMsgName(UINT msg)
 {
    CString name;
    if (msg>=WM_USER)
       name.Format("WM_USER+%d", msg-WM_USER);
    else if (Names[msg])
       name = Names[msg];
    else
       name.Format("0x%04x", msg);
    return name;
 }
 
 //////////////////
 // Get name of WM_ message.
 //
 CString sDbgName(UINT uMsg)
 {
    static CWndMsgMap wndMsgMap; // instantiate 1st time called
    return wndMsgMap.GetMsgName(uMsg);
 }
 
 #endif // DEBUG
Dib.cpp

 ////////////////////////////////////////////////////////////////
 // 1996 Microsoft Systems Journal. 
 // If this program works, it was written by Paul DiLascia.
 // If not, I don't know who wrote it.
 //
 // CDib - Device Independent Bitmap.
 // This implementation draws bitmaps using normal Win32 API functions,
 // not DrawDib. CDib is derived from CBitmap, so you can use it with
 // any other MFC functions that use bitmaps.
 //
 #include "StdAfx.h"
 #include "Dib.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif
 
 IMPLEMENT_DYNAMIC(CDib, CObject)
 
 CDib::CDib()
 {
    memset(&m_bm, 0, sizeof(m_bm));
    m_hdd = NULL;
 }
 
 CDib::~CDib()
 {
    DeleteObject();
 }
 
 //////////////////
 // Delete Object. Delete DIB and palette.
 //
 BOOL CDib::DeleteObject()
 {
    m_pal.DeleteObject();
    if (m_hdd) {
       DrawDibClose(m_hdd);
       m_hdd = NULL;
    }
    memset(&m_bm, 0, sizeof(m_bm));
    return CBitmap::DeleteObject();
 }
 
 //////////////////
 // Read DIB from file.
 //
 BOOL CDib::Load(LPCTSTR lpszPathName)
 {
    return Attach(::LoadImage(NULL, lpszPathName, IMAGE_BITMAP, 0, 0,
       LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_DEFAULTSIZE));
 }
 
 //////////////////
 // Load bitmap resource. Never tested.
 //
 BOOL CDib::Load(HINSTANCE hInst, LPCTSTR lpResourceName)
 {
    return Attach(::LoadImage(hInst, lpResourceName, IMAGE_BITMAP, 0, 0,
       LR_CREATEDIBSECTION | LR_DEFAULTSIZE));
 }
 
 //////////////////
 // Attach is just like the CGdiObject version,
 // except it also creates the palette
 //
 BOOL CDib::Attach(HGDIOBJ hbm)
 {
    if (CBitmap::Attach(hbm)) {
       if (!GetBitmap(&m_bm))        // load BITMAP for speed
          return FALSE;
       m_pal.DeleteObject();         // in case one is already there
       return CreatePalette(m_pal);  // create palette
    }
    return FALSE;  
 }
 
 //////////////////
 // Get size (width, height) of bitmap.
 // extern fn works for ordinary CBitmap objects.
 //
 CSize GetBitmapSize(CBitmap* pBitmap)
 {
    BITMAP bm;
    return pBitmap->GetBitmap(&bm) ?
       CSize(bm.bmWidth, bm.bmHeight) : CSize(0,0);
 }
 
 //////////////////
 // You can use this static function to draw ordinary
 // CBitmaps as well as CDibs
 //
 BOOL DrawBitmap(CDC& dc, CBitmap* pBitmap,
    const CRect* rcDst, const CRect* rcSrc)
 {
    // Compute rectangles where NULL specified
    CRect rc;
    if (!rcSrc) {
       // if no source rect, use whole bitmap
       rc = CRect(CPoint(0,0), GetBitmapSize(pBitmap));
       rcSrc = &rc;
    }
    if (!rcDst) {
       // if no destination rect, use source
       rcDst=rcSrc;
    }
 
    // Create memory DC
    CDC memdc;
    memdc.CreateCompatibleDC(&dc);
    CBitmap* pOldBm = memdc.SelectObject(pBitmap);
 
    // Blast bits from memory DC to target DC.
    // Use StretchBlt if size is different.
    //
    BOOL bRet = FALSE;
    if (rcDst->Size()==rcSrc->Size()) {
       bRet = dc.BitBlt(rcDst->left, rcDst->top,
          rcDst->Width(), rcDst->Height(),
          &memdc, rcSrc->left, rcSrc->top, SRCCOPY);
    } else {
       dc.SetStretchBltMode(COLORONCOLOR);
       bRet = dc.StretchBlt(rcDst->left, rcDst->top, rcDst->Width(),
          rcDst->Height(), &memdc, rcSrc->left, rcSrc->top, rcSrc->Width(),
          rcSrc->Height(), SRCCOPY);
    }
    memdc.SelectObject(pOldBm);
 
    return bRet;
 }
 
 ////////////////////////////////////////////////////////////////
 // Draw DIB on caller's DC. Does stretching from source to destination
 // rectangles. Generally, you can let the following default to zero/NULL:
 //
 //    bDither     = whether to dither (uses DrawDib), default FALSE
 //    pPal        = palette, default=NULL, (use DIB's palette)
 //    bForeground = realize in foreground (default FALSE)
 //
 // If you are handling palette messages, you should use bForeground=FALSE to
 // draw, since you will realize the foreground palette in WM_QUERYNEWPALETTE.
 //
 BOOL CDib::Draw(CDC& dc, const CRect* rcDst, const CRect* rcSrc,
    BOOL bDither, CPalette* pPal, BOOL bForeground)
 {
    if (!m_hObject)
       return FALSE;
 
    BOOL bRet = FALSE;
 
    // Select, realize palette
    if (pPal==NULL)            // no palette specified:
       pPal = GetPalette();    // use default
    CPalette* pOldPal = dc.SelectPalette(pPal, !bForeground);
    dc.RealizePalette();
 
    // Only use DrawDib to dither if more than 8-bit color
    if (bDither && m_bm.bmBitsPixel>8) {
 
       // Compute rectangles where NULL specified
       CRect rc;
       if (!rcSrc) {
          // if no source rect, use whole bitmap
          rc.SetRect(0, 0, m_bm.bmWidth, m_bm.bmHeight);
          rcSrc = &rc;
       }
       if (!rcDst) {
          // if no destination rect, use source
          rcDst=rcSrc;
       }
 
       DIBSECTION ds;
       VERIFY(GetObject(sizeof(ds), &ds)==sizeof(ds));
       if (!m_hdd)
          VERIFY(m_hdd = DrawDibOpen());
       bRet = DrawDibDraw(m_hdd, dc,
          rcDst->left, rcDst->top, rcDst->Width(), rcDst->Height(),
          &ds.dsBmih,    // ptr to BITMAPINFOHEADER
          m_bm.bmBits,   // bits in memory
          rcSrc->left, rcSrc->top, rcSrc->Width(), rcSrc->Height(),
          bForeground ? 0 : DDF_BACKGROUNDPAL);
 
    } else {
       // use normal draw function
       bRet = DrawBitmap(dc, this, rcDst, rcSrc);
    }
    if (pOldPal)
       dc.SelectPalette(pOldPal, TRUE);
    return bRet;
 }
 
 #define PALVERSION 0x300   // magic number for LOGPALETTE
 
 //////////////////
 // Create the palette
 //
 BOOL CDib::CreatePalette(CPalette& pal)
 { 
    static RGBQUAD colors[256]; // to hold colors
 
    // should not already have palette
    ASSERT(pal.m_hObject==NULL);
 
    // I'll use GetDIBColorTable to get the colors
    // Create memory DC compatible w/screen
    CWindowDC dcScreen(NULL);
    CDC memdc;
    memdc.CreateCompatibleDC(&dcScreen);
    CBitmap* pOldBm = memdc.SelectObject(this);
    int nColors = GetDIBColorTable(memdc, 0, 256, colors);
 
    BOOL  bRet = FALSE;
    if (nColors > 0) {
       // Allocate memory for logical palette 
       int len = sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * nColors;
       LOGPALETTE* pLogPal = (LOGPALETTE*)new char[len];
       if (!pLogPal)
          return NULL;
 
       // set version and number of palette entries
       pLogPal->palVersion = PALVERSION;
       pLogPal->palNumEntries = nColors;
 
       // copy color entries 
       for (int i = 0; i < nColors; i++) {
          pLogPal->palPalEntry[i].peRed   = colors[i].rgbRed;
          pLogPal->palPalEntry[i].peGreen = colors[i].rgbGreen;
          pLogPal->palPalEntry[i].peBlue  = colors[i].rgbBlue;
          pLogPal->palPalEntry[i].peFlags = 0;
       }
 
       // create the palette and destroy LOGPAL
       bRet = pal.CreatePalette(pLogPal);
       delete [] (char*)pLogPal;
    } else {
       bRet = pal.CreateHalftonePalette(&dcScreen);
    }
    memdc.SelectObject(pOldBm);
    return bRet;
 }
Doc.cpp

 ////////////////////////////////////////////////////////////////
 // 1996 Microsoft Systems Journal. 
 // If this program works, it was written by Paul DiLascia.
 // If not, I don't know who wrote it.
 //
 #include "StdAfx.h"
 .
 .
 .

 //////////////////
 // Override to load bitmap directly instead of doing serialization stuff.
 //
 BOOL CDIBDoc::OnOpenDocument(LPCTSTR lpszPathName)
 {
    DeleteContents();
    return m_dib.Load(lpszPathName);
 }
 
 //////////////////
 // Delete contents of doc: delete the DIB
 //
 void CDIBDoc::DeleteContents()
 {
    m_dib.DeleteObject();
 }
MainFrm.cpp

 ////////////////////////////////////////////////////////////////
 // 1996 Microsoft Systems Journal. 
 // If this program works, it was written by Paul DiLascia.
 // If not, I don't know who wrote it.
 //
 #include "StdAfx.h"
 #include "DibView.h"
 #include "MainFrm.h"
 #include "Debug.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif
 
 IMPLEMENT_DYNCREATE(CMainFrame, CBaseFrameWnd)
 
 BEGIN_MESSAGE_MAP(CMainFrame, CBaseFrameWnd)
    ON_WM_CREATE()
    ON_WM_SETFOCUS()
    ON_WM_QUERYNEWPALETTE()
    ON_WM_PALETTECHANGED()
 END_MESSAGE_MAP()
 
 static UINT indicators[] = {
    ID_SEPARATOR            // status line indicator
 };
 
 CMainFrame::CMainFrame()
 {
 }
 
 CMainFrame::~CMainFrame()
 {
 }
 
 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
 {
    if (CBaseFrameWnd::OnCreate(lpCreateStruct) == -1)
       return -1;
 
    if (!m_wndToolBar.Create(this) ||
       !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) {
       TRACE0("Failed to create toolbar\n");
       return -1;      // fail to create
    }
    if (!m_wndStatusBar.Create(this) ||
       !m_wndStatusBar.SetIndicators(indicators,
         sizeof(indicators)/sizeof(UINT))) {
       TRACE0("Failed to create status bar\n");
       return -1;      // fail to create
    }
    m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
       CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
 
    return 0;
 }
 
 //////////////////
 // Palette changed: broadcast the message to all children.
 //
 void CMainFrame::OnPaletteChanged(CWnd* pFocusWnd) 
 {
    TRACEFN("CMainFrame::OnPaletteChanged\n");
    const MSG& msg = AfxGetThreadState()->m_lastSentMsg;
    SendMessageToDescendants(WM_PALETTECHANGED, msg.wParam, msg.lParam);
 }
 
 //////////////////
 // I got activated: realize my palette
 //
 BOOL CMainFrame::OnQueryNewPalette() 
 {
    TRACEFN("CMainFrame::OnQueryNewPalette\n");
    CView* pView = GetActiveFrame()->GetActiveView();
    if (pView)
       pView->SendMessage(WM_QUERYNEWPALETTE);
    return FALSE;
 }
View.cpp

 ////////////////////////////////////////////////////////////////
 // 1996 Microsoft Systems Journal. 
 // If this program works, it was written by Paul DiLascia.
 // If not, I don't know who wrote it.
 //
 #include "StdAfx.h"
 #include "DibView.h"
 
 •
 •
 •
//////////////////
 // Get the DIB. Duh.
 //
 CDib* CDIBView::GetDIB()
 {
    CDIBDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    CDib* pDIB = pDoc->GetDIB();
    ASSERT_VALID(pDIB);
    return pDIB->m_hObject ? pDIB : NULL;
 }
 
 //////////////////
 // Initial update: set scroll sizes.
 //
 void CDIBView::OnInitialUpdate()
 {
    TRACE("CDIBView::OnInitialUpdate\n");
    CScrollView::OnInitialUpdate();
    CDib* pDIB = GetDIB();
    SetScrollSizes(MM_TEXT, pDIB ? pDIB->GetSize() : CSize(100,100));
    DoRealizePalette(TRUE);
 }
 
 //////////////////
 // Draw the bitmap. Be careful to specify foreground/background.
 //
 void CDIBView::OnDraw(CDC* pDC)
 {
    CDib* pDIB = GetDIB();
    if (pDIB)
       pDIB->Draw(*pDC, NULL, NULL, m_bDither);
 }
 
 //////////////////
 // Dithering command
 //
 void CDIBView::OnDither()
 {
    m_bDither = ! m_bDither;
    Invalidate();
 }
 
 void CDIBView::OnUpdateDither(CCmdUI* pCmdUI)
 {
    pCmdUI->SetCheck(m_bDither);
 }
 
 //////////////////
 // Palette changed: 
 // If not because of me, realize the palette in background.
 //
 void CDIBView::OnPaletteChanged(CWnd* pFocusWnd)
 {
    TRACEFN("%s::OnPaletteChanged(from %s)\n",
       DbgName(this), DbgName(pFocusWnd));
 
    if (pFocusWnd!=this) {
       DoRealizePalette(FALSE);
    } else {
       TRACE0("[It's me, don't realize palette]\n");
    }
 }
 
 ////////////////
 // Realize my palette.
 //
 int CDIBView::DoRealizePalette(BOOL bForeground)
 {
    TRACEFN("%s::DoRealizePalette(%s)\n", DbgName(this),
       bForeground ? "foreground" : "background");
 
    CDib* pDIB = GetDIB();
    if (!pDIB)
       return 0;
 
    // Selet palette into client DC and realize it
    CClientDC dc = this;
    CPalette* pPal = pDIB->GetPalette();
    CPalette* pOldPal = dc.SelectPalette(pPal, !bForeground);
    UINT nColorsChanged = dc.RealizePalette();
    if (nColorsChanged > 0)
       Invalidate(FALSE);   // tell Windows to repaint
    if (pOldPal)
       dc.SelectPalette(pOldPal, TRUE);
    TRACE("%d colors changed\n", nColorsChanged);
    return nColorsChanged;
 }
 
 ////////////////
 // Got WM_QUEYNEWPALETTE from parent frame: realize my palette.
 // Normally, this message is only sent to top-level windows.
 // Return num colors changed.
 //
 BOOL CDIBView::OnQueryNewPalette() 
 {
    TRACEFN("%s::OnQueryNewPalette\n", DbgName(this));
    return DoRealizePalette(TRUE);
 }
 
 //////////////////
 // View was activated: realize foreground palette.
 //
 void CDIBView::OnSetFocus(CWnd* pOldWnd)
 {
    TRACE("CDIBView::OnSetFocus\n");
    CView::OnSetFocus(pOldWnd);
    DoRealizePalette(TRUE);
 }

Figure 13   Reserved Palette Indexes

Static colors unaltered by palette changes. These are guaranteed to be there forever.
INDEX
RED
GREEN
BLUE
COLOR
0
0
0
0
black
1
128
0
0
dark red
2
0
128
0
dark green
3
128
128
0
dark yellow
4
0
0
128
dark blue
5
128
0
128
dark magenta
6
0
128
128
dark cyan
7
192
192
192
light gray
8
192
220
192
money green
9
166
202
240
sky blue

246
255
251
240
cream
247
160
160
164
light gray
248
128
128
128
medium gray
249
255
0
0
red
250
0
255
0
green
251
255
255
0
yellow
252
0
0
255
blue
253
255
0
255
magenta
254
0
255
255
cyan
255
255
255
255
white


Figure 18   Tracing DIBVIEW


 // Palette scenario 1: I activated DIBVIEW from another app
 // 
 CMainFrame::OnQueryNewPalette
  CDIBView[0x0ab8,"surf.dib"]::OnQueryNewPalette
   CDIBView[0x0ab8,"surf.dib"]::DoRealizePalette(foreground)
    CMainFrame::OnPaletteChanged
     CDIBView[0x0ab8,"surf.dib"]::OnPaletteChanged (from CDIBView[0x0ab8,"surf.dib"])
     [It's me, don't realize palette]
     CDIBView[0x0c9c,"virgil.dib"]::OnPaletteChanged (from CDIBView[0x0ab8,"surf.dib"])
      CDIBView[0x0c9c,"virgil.dib"]::DoRealizePalette(background)
      220 colors changed
   235 colors changed
 .
 .
 .
// Palete scenario 2: I changed focus within DIBVIEW from surf.dib to virgil.dib 
 // 
 CDIBView[0x0900,"virgil.dib"]::OnSetFocus
  CDIBView[0x0900,"virgil.dib"]::DoRealizePalette(foreground)
   CMainFrame::OnPaletteChanged
    CDIBView[0x0900,"virgil.dib"]::OnPaletteChanged (from CDIBView[0x0900,"virgil.dib"])
    [It's me, don't realize palette]
    CDIBView[0x079c,"surf.dib"]::OnPaletteChanged (from CDIBView[0x0900,"virgil.dib"])
     CDIBView[0x079c,"surf.dib"]::DoRealizePalette(background)
     236 colors changed
  238 colors changed