|
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 windoweither one of multiple instances of DIBVIEW, or one of multiple child windows in the MDI versionthe 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