📄 ch15.htm
字号:
<HR>
</BLOCKQUOTE>
<P><A NAME="06"></A><A HREF="06.htm"><B>Figure 15.6.</B></A> <I><BR>
Windows makes 236 palette entries available to applications.</I>
<BLOCKQUOTE>
<P>
<HR>
<B> </B><FONT COLOR="#000077"><B>Just a Minute:</B></FONT><B> </B>At first glance,
it might seem unfair for 20 entries to be removed from the system palette. These
entries are removed to keep the basic window display predictable. The 20 reserved
palette entries include the colors used by 16-color VGA devices. As long as these
palette entries are available, Windows applications that don't use the palette are
displayed as expected.
<HR>
</BLOCKQUOTE>
<H3><FONT COLOR="#000077"><B>The System Palette</B></FONT></H3>
<P>A palette is one of the attributes that belong to a DC. After you have decided
to use a palette in your application, you must follow these basic steps:
<DL>
<DD>1. Create a logical palette. This is the process of allocating space for the
palette entries and describing the colors that will be included in the palette. As
you'll learn, this process is much easier than it sounds. In most cases, when you
must create a palette you copy the colors from a bitmap or other object into the
new palette. This step is usually performed once, and the logical palette is stored
by the application so that it can be used whenever needed.<BR>
<BR>
2. Select the palette into a DC. Unlike other GDI objects, <TT>SelectObject</TT>
doesn't work for logical palettes. You must use the <TT>SelectPalette</TT> function.<BR>
<BR>
3. Realize the palette. Basically, realizing the palette asks the Windows Palette
Manager to add your palette to the system palette or map your palette to a reasonably
close substitute. Selecting and realizing the palette always happens at the same
time; there's no point in selecting a palette unless you intend to realize it immediately.<BR>
<BR>
4. Use the palette. If the system palette was updated, the application should redraw
itself. This is usually done by invalidating any windows that depend on palette entries.<BR>
<BR>
5. Deselect the palette by selecting the previous palette back into the DC.<BR>
<BR>
6. Delete the palette object. This step is usually performed only when you're sure
the palette is no longer needed.
</DL>
<P>Two messages related to palettes are sent to your application:
<UL>
<LI>When a window moves into the foreground, Windows sends it a <TT>WM_QUERYNEWPALETTTE</TT>
message. In response to this message, your application should realize its palette.<BR>
<BR>
<LI>If the system palette is changed, all windows in the background receive a <TT>WM_PALETTECHANGED</TT>
message. An application in the background should realize its palette to attempt to
reassert colors in the system palette. If no free positions in the system palette
are available, the Palette Manager maps the requested color to the closest palette
entry.
</UL>
<P>In any case, you should invalidate any parts of your application that depend on
your logical palette if the system palette is updated.
<H2><FONT COLOR="#000077"><B>The Dib Example</B></FONT></H2>
<P>As an example of how 256-color bitmaps are displayed, you will now create an MFC
example named Dib. The design of the Dib project uses a basic AppWizard SDI skeleton
with these modifications:
<UL>
<LI>A reusable class that handles the display of DIBs, <TT>CDIBitmap</TT><BR>
<BR>
<LI>A reusable class that handles the creation of a new 256-color palette, <TT>CBmpPalette</TT><BR>
<BR>
<LI>Additional palette message-handling functions
</UL>
<H3><FONT COLOR="#000077"><B>The <TT>CDIBitmap</TT> Class</B></FONT></H3>
<P>The <TT>CDIBitmap</TT> class does most of the work in the Dib project. The <TT>CDIBitmap</TT>
class provides an easy-to-use interface for handling 256-color DIBs. The class interface
for <TT>CDIBitmap</TT> is shown in Listing 15.2. Save this file as <TT>dib256.h</TT>.
<H4><FONT COLOR="#000077">TYPE: Listing 15.2. The CDIBitmap class interface.</FONT></H4>
<PRE><FONT COLOR="#0066FF"><TT>#ifndef DIBMP_TYS</TT>
<TT>#define DIBMP_TYS</TT>
<TT>class CDIBitmap</TT>
<TT>{</TT>
<TT> friend class CBmpPalette;</TT>
<TT>//constructors</TT>
<TT>public:</TT>
<TT> CDIBitmap();</TT>
<TT> virtual ~CDIBitmap();</TT>
<TT>private:</TT>
<TT> CDIBitmap( const CDIBitmap& dbmp ){};</TT>
<TT>//operations</TT>
<TT>public:</TT>
<TT> inline BITMAPINFO* GetHeaderPtr();</TT>
<TT> inline BYTE* GetPixelPtr();</TT>
<TT> virtual void DrawDIB ( CDC* pDC, int x, int y );</TT>
<TT> virtual BOOL Load( CFile* pFile );</TT>
<TT> RGBQUAD* GetColorTablePtr();</TT>
<TT>protected:</TT>
<TT> int GetPalEntries() const;</TT>
<TT> int GetPalEntries( BITMAPINFOHEADER& infoHeader ) const;</TT>
<TT>protected:</TT>
<TT> int GetWidth() const;</TT>
<TT> int GetHeight() const;</TT>
<TT>//implementation</TT>
<TT>private:</TT>
<TT> BITMAPINFO* m_pInfo;</TT>
<TT> BYTE* m_pPixels;</TT>
<TT>};</TT>
<TT>#endif</TT>
</FONT></PRE>
<P>Note that <TT>CDIBitmap</TT> isn't derived from <TT>CBitmap</TT>, or <TT>CObject</TT>
for that matter. The class itself consumes only a few bytes, requiring space for
two pointers and a virtual function table. <TT>CDIBitmap</TT> has five public functions:
<UL>
<LI><TT>GetHeaderPtr</TT>: Returns a pointer to the <TT>BITMAPINFO</TT> structure.<BR>
<BR>
<LI><TT>GetPixelPtr</TT>: Returns a pointer to the beginning of the pixel image array.<BR>
<BR>
<LI><TT>DrawDIB</TT>: Draws the DIB at a specified location.<BR>
<BR>
<LI><TT>Load</TT>: Reads a DIB from a <TT>.BMP</TT> file and initializes the <TT>CDIBitmap</TT>
object.<BR>
<BR>
<LI><TT>GetColorTablePtr</TT>: Returns a pointer to the color table.
</UL>
<P>The source code for the implementation of <TT>CDIBitmap</TT> is provided in Listing
15.3. Save this file as <TT>dib256.cpp</TT>, and add it to the Dib project.
<H4><FONT COLOR="#000077">TYPE: Listing 15.3. The implementation of the CDIBitmap
class.</FONT></H4>
<PRE><FONT COLOR="#0066FF"><TT>#include "stdafx.h"</TT>
<TT>#include "dib256.h"</TT>
<TT>CDIBitmap::CDIBitmap()</TT>
<TT>{</TT>
<TT> m_pInfo = 0;</TT>
<TT> m_pPixels = 0;</TT>
<TT>}</TT>
<TT>CDIBitmap::~CDIBitmap()</TT>
<TT>{</TT>
<TT> delete [] (BYTE*)m_pInfo;</TT>
<TT> delete [] m_pPixels;</TT>
<TT>}</TT>
<TT>BOOL CDIBitmap::Load( CFile* pFile )</TT>
<TT>{</TT>
<TT> ASSERT( pFile );</TT>
<TT> BOOL fReturn = TRUE;</TT>
<TT> delete [] (BYTE*)m_pInfo;</TT>
<TT> delete [] m_pPixels;</TT>
<TT> m_pInfo = 0;</TT>
<TT> m_pPixels = 0;</TT>
<TT> DWORD dwStart = pFile->GetPosition();</TT>
<TT> // Check to make sure we have a bitmap. The first two bytes must</TT>
<TT> // be `B' and `M'.</TT>
<TT> BITMAPFILEHEADER fileHeader;</TT>
<TT> pFile->Read(&fileHeader, sizeof(fileHeader));</TT>
<TT> if( fileHeader.bfType != 0x4D42 )</TT>
<TT> return FALSE;</TT>
<TT> BITMAPINFOHEADER infoHeader;</TT>
<TT> pFile->Read( &infoHeader, sizeof(infoHeader) );</TT>
<TT> if( infoHeader.biSize != sizeof(infoHeader) )</TT>
<TT> return FALSE;</TT>
<TT> // Store the sizes of the DIB structures</TT>
<TT> int cPaletteEntries = GetPalEntries( infoHeader );</TT>
<TT> int cColorTable = 256 * sizeof(RGBQUAD);</TT>
<TT> int cInfo = sizeof(BITMAPINFOHEADER) + cColorTable;</TT>
<TT> int cPixels = fileHeader.bfSize - fileHeader.bfOffBits;</TT>
<TT> // Allocate space for a new bitmap info header, and copy</TT>
<TT> // the info header that was loaded from the file. Read the</TT>
<TT> // the file and store the results in the color table.</TT>
<TT> m_pInfo = (BITMAPINFO*)new BYTE[cInfo];</TT>
<TT> memcpy( m_pInfo, &infoHeader, sizeof(BITMAPINFOHEADER) );</TT>
<TT> pFile->Read( ((BYTE*)m_pInfo) + sizeof(BITMAPINFOHEADER),</TT>
<TT> cColorTable );</TT>
<TT> //</TT>
<TT> // Allocate space for the pixel area, and load the pixel</TT>
<TT> // info from the file.</TT>
<TT> m_pPixels = new BYTE[cPixels];</TT>
<TT> pFile->Seek(dwStart + fileHeader.bfOffBits, CFile::begin);</TT>
<TT> pFile->Read( m_pPixels, cPixels );</TT>
<TT> return fReturn;</TT>
<TT>}</TT>
<TT>// DrawDib uses StretchDIBits to display the bitmap.</TT>
<TT>void CDIBitmap::DrawDIB( CDC* pDC, int x, int y )</TT>
<TT>{</TT>
<TT> HDC hdc = pDC->GetSafeHdc();</TT>
<TT> if( m_pInfo )</TT>
<TT> StretchDIBits( hdc, x, y,</TT>
<TT> GetWidth(),</TT>
<TT> GetHeight(),</TT>
<TT> 0, 0,</TT>
<TT> GetWidth(),</TT>
<TT> GetHeight(),</TT>
<TT> GetPixelPtr(),</TT>
<TT> GetHeaderPtr(),</TT>
<TT> DIB_RGB_COLORS,</TT>
<TT> SRCCOPY );</TT>
<TT>}</TT>
<TT>BITMAPINFO* CDIBitmap::GetHeaderPtr()</TT>
<TT>{</TT>
<TT> return m_pInfo;</TT>
<TT>}</TT>
<TT>RGBQUAD* CDIBitmap::GetColorTablePtr()</TT>
<TT>{</TT>
<TT> RGBQUAD* pColorTable = 0;</TT>
<TT> if( m_pInfo != 0 )</TT>
<TT> {</TT>
<TT> int cOffset = sizeof(BITMAPINFOHEADER);</TT>
<TT> pColorTable = (RGBQUAD*)(((BYTE*)(m_pInfo)) + cOffset);</TT>
<TT> }</TT>
<TT> return pColorTable;</TT>
<TT>}</TT>
<TT>BYTE* CDIBitmap::GetPixelPtr()</TT>
<TT>{</TT>
<TT> return m_pPixels;</TT>
<TT>}</TT>
<TT>int CDIBitmap::GetWidth() const</TT>
<TT>{</TT>
<TT> return m_pInfo->bmiHeader.biWidth;</TT>
<TT>}</TT>
<TT>int CDIBitmap::GetHeight() const</TT>
<TT>{</TT>
<TT> return m_pInfo->bmiHeader.biHeight;</TT>
<TT>}</TT>
<TT>int CDIBitmap::GetPalEntries() const</TT>
<TT>{</TT>
<TT> return GetPalEntries( *(BITMAPINFOHEADER*)m_pInfo );</TT>
<TT>}</TT>
<TT>int CDIBitmap::GetPalEntries( BITMAPINFOHEADER& infoHeader ) const</TT>
<TT>{</TT>
<TT> int nReturn;</TT>
<TT> if( infoHeader.biClrUsed == 0 )</TT>
<TT> {</TT>
<TT> nReturn = ( 1 << infoHeader.biBitCount );</TT>
<TT> }</TT>
<TT> else</TT>
<TT> nReturn = infoHeader.biClrUsed;</TT>
<TT> return nReturn;</TT>
<TT>}</TT>
</FONT></PRE>
<P>Most of the work in the <TT>CDIBitmap</TT> class is done by the <TT>CDIBitmap::Load</TT>
member function. This member function takes a pointer to an MFC <TT>CFile</TT> object
as its only parameter. Later in this hour, you will see how the sample program provides
a <TT>CFile</TT> pointer to this function during serialization.</P>
<P>After verifying that the <TT>CFile</TT> object refers to a Windows bitmap, the
<TT>Load</TT> function reads each part of the bitmap data structure and creates a
DIB dynamically. Note that there actually are two calls to the new operator; there
is no requirement that the DIB exist in one solid chunk of memory. The <TT>BITMAPINFOHEADER</TT>
is stored in one location, and the pixel image array is stored in another location.</P>
<P>The <TT>CDIBitmap::DrawDIB</TT> member function calls <TT>StretchDIBits</TT> to
display the DIB. Very little work is actually done in this function. For example,
the width and height of the DIB are calculated using <TT>CDIBitmap</TT> member functions.</P>
<P>The remaining member functions are used to calculate various bits of information
about the DIB. Only a pointer to the beginning of the <TT>BITMAPINFO</TT> structure
and a pointer to the beginning of the pixel image array are stored; all other information
is calculated as it is needed.
<H3><FONT COLOR="#000077"><B>The <TT>CBmpPalette</TT> Class</B></FONT></H3>
<P>You use the <TT>CBmpPalette</TT> class to create a logical palette that contains
the colors used by a <TT>CDIBitmap</TT> object. Although the MFC class library includes
a <TT>CPalette</TT> class, you must derive your own class from it in order to do
any meaningful work. Listing 15.4 contains the declaration for <TT>CBmpPalette</TT>.
Save this file as <TT>dibpal.h</TT>.
<H4><FONT COLOR="#000077">TYPE: Listing 15.4. The CBmpPalette class interface.</FONT></H4>
<PRE><FONT COLOR="#0066FF"><TT>#ifndef BMP_PAL_TYS</TT>
<TT>#define BMP_PAL_TYS</TT>
<TT>class CBmpPalette : public CPalette</TT>
<TT>{</TT>
<TT>public:</TT>
<TT> CBmpPalette( CDIBitmap* pBmp );</TT>
<TT>};</TT>
<TT>#endif</TT>
</FONT></PRE>
<P>All the work done by <TT>CBmpPalette</TT> is done in the constructor; there are
no member functions other than the function inherited from <TT>CPalette</TT>, the
MFC base class. The <TT>CBmpPalette</TT> class is always used with <TT>CDIBitmap</TT>.
A pointer to a <TT>CDIBitmap</TT> object is passed to <TT>CBmpPalette</TT> as a constructor
parameter.</P>
<P><TT>CBmpPalette</TT> allocates a logical palette with enough entries to store
the palette required by the <TT>CDIBitmap</TT> object. After storing some basic palette
information, the palette entries are filled in, using the values collected from the
<TT>CDIBitmap</TT> object. After the palette is created, the logical palette is deleted.
The implementation from <TT>CBmpPalette</TT> is provided in Listing 15.5 and is included
in the Dib project as <TT>dibpal.cpp</TT>.
<H4><FONT COLOR="#000077">TYPE: Listing 15.5. The implementation of the CBmpPalette
class.</FONT></H4>
<PRE><FONT COLOR="#0066FF"><TT>#include "stdafx.h"</TT>
<TT>#include "dib256.h"</TT>
<TT>#include "dibpal.h"</TT>
<TT>CBmpPalette::CBmpPalette( CDIBitmap* pBmp )</TT>
<TT>{</TT>
<TT> ASSERT( pBmp );</TT>
<TT> int cPaletteEntries = pBmp->GetPalEntries();</TT>
<TT> int cPalette = sizeof(LOGPALETTE) +</TT>
<TT> sizeof(PALETTEENTRY) * cPaletteEntries;</TT>
<TT> // Since the LOGPALETTE structure is open-ended, you</TT>
<TT> // must dynamically allocate it.</TT>
<TT> LOGPALETTE* pPal = (LOGPALETTE*)new BYTE[cPalette];</TT>
<TT> RGBQUAD* pColorTab = pBmp->GetColorTablePtr();</TT>
<TT> pPal->palVersion = 0x300;</TT>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -