16.3 调色板和显示世界中的图像

摘录于《Windows程序(第5版,珍藏版).CHarles.Petzold 著》P680

        如下所示的 PACKEDIB 文件显示了一系列用于处理紧凑 DIB 内存块的函数,它们将在接下来的三个程序中给我们提供许多帮助。

/*---------------------------------------
   PACKEDIB.H header file for PACKEDIB.C
  ---------------------------------------*/

#include 

BITMAPINFO * PackedDibLoad (PTSTR szFileName) ;
int PackedDibGetWidth (BITMAPINFO * pPackedDib) ;
int PackedDibGetHeight (BITMAPINFO * pPackedDib) ;
int PackedDibGetBitCount (BITMAPINFO * pPackedDib) ;
int PackedDibGetRowLength (BITMAPINFO * pPackedDib) ;
int PackedDibGetInfoHeaderSize (BITMAPINFO * pPackedDib) ;
int PackedDibGetColorsUsed (BITMAPINFO * pPackedDib) ;
int PackedDibGetNumColors (BITMAPINFO * pPackedDib) ;
int PackedDibGetColorTableSize (BITMAPINFO * pPackedDib) ;
RGBQUAD * PackedDibGetColorTablePtr (BITMAPINFO * pPackedDib) ;
RGBQUAD * PackedDibGetColorTableEntry (BITMAPINFO * pPackedDib, int i) ;
BYTE * PackedDibGetBitsPtr (BITMAPINFO * pPackedDib) ;
int PackedDibGetBitsSize (BITMAPINFO * pPackedDib) ;
HPALETTE PackedDibCreatePalette (BITMAPINFO * pPackedDib) ;
/*----------------------------------------------
   PACKEDIB.C -- Routines for using packed DIBs
                 (c) Charles Petzold, 1998
  ----------------------------------------------*/

#include 

/*---------------------------------------------------------
   PackedDibLoad: Load DIB File as Packed-Dib Memory Block
  ---------------------------------------------------------*/

BITMAPINFO * PackedDibLoad (PTSTR szFileName)
{
     BITMAPFILEHEADER bmfh ;
     BITMAPINFO     * pbmi ;
     BOOL             bSuccess ;
     DWORD            dwPackedDibSize, dwBytesRead ;
     HANDLE           hFile ;

          // Open the file: read access, prohibit write access

     hFile = CreateFile (szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, 
                         OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL) ;

     if (hFile == INVALID_HANDLE_VALUE)
          return NULL ;

          // Read in the BITMAPFILEHEADER

     bSuccess = ReadFile (hFile, &bmfh, sizeof (BITMAPFILEHEADER), 
                          &dwBytesRead, NULL) ;

     if (!bSuccess || (dwBytesRead != sizeof (BITMAPFILEHEADER))         
                   || (bmfh.bfType != * (WORD *) "BM"))
     {
          CloseHandle (hFile) ;
          return NULL ;
     }

          // Allocate memory for the packed-DIB & read it in

     dwPackedDibSize = bmfh.bfSize - sizeof (BITMAPFILEHEADER) ;

     pbmi = (BITMAPINFO *)malloc (dwPackedDibSize) ;

     bSuccess = ReadFile (hFile, pbmi, dwPackedDibSize, &dwBytesRead, NULL) ;
     CloseHandle (hFile) ;

     if (!bSuccess || (dwBytesRead != dwPackedDibSize))
     {
          free (pbmi) ;
          return NULL ;
     }

     return pbmi ;
}

/*----------------------------------------------
   Functions to get information from Packed Dib
  ----------------------------------------------*/

int PackedDibGetWidth (BITMAPINFO * pPackedDib)
{
     if (pPackedDib->bmiHeader.biSize == sizeof (BITMAPCOREHEADER))
          return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcWidth ;
     else
          return pPackedDib->bmiHeader.biWidth ;
}

int PackedDibGetHeight (BITMAPINFO * pPackedDib)
{
     if (pPackedDib->bmiHeader.biSize == sizeof (BITMAPCOREHEADER))
          return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcHeight ;
     else
          return abs (pPackedDib->bmiHeader.biHeight) ;
}

int PackedDibGetBitCount (BITMAPINFO * pPackedDib)
{
     if (pPackedDib->bmiHeader.biSize == sizeof (BITMAPCOREHEADER))
          return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcBitCount ;
     else
          return pPackedDib->bmiHeader.biBitCount ;
}

int PackedDibGetRowLength (BITMAPINFO * pPackedDib)
{
     return ((PackedDibGetWidth (pPackedDib) * 
              PackedDibGetBitCount (pPackedDib) + 31) & ~31) >> 3 ;
}

/*-----------------------------------------------------------
   PackedDibGetInfoHeaderSize includes possible color masks!
  -----------------------------------------------------------*/

int PackedDibGetInfoHeaderSize (BITMAPINFO * pPackedDib)
{
     if (pPackedDib->bmiHeader.biSize == sizeof (BITMAPCOREHEADER))
          return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcSize ;

     else if (pPackedDib->bmiHeader.biSize == sizeof (BITMAPINFOHEADER))
          return pPackedDib->bmiHeader.biSize + 
                    (pPackedDib->bmiHeader.biCompression == 
                                        BI_BITFIELDS ? 12 : 0) ;

     else return pPackedDib->bmiHeader.biSize ;
}

/*-------------------------------------------------------------
   PackedDibGetColorsUsed returns value in information header;
          could be 0 to indicate non-truncated color table!
  -------------------------------------------------------------*/

int PackedDibGetColorsUsed (BITMAPINFO * pPackedDib)
{
     if (pPackedDib->bmiHeader.biSize == sizeof (BITMAPCOREHEADER))
          return 0 ;
     else
          return pPackedDib->bmiHeader.biClrUsed ;
}

/*------------------------------------------------------------------
   PackedDibGetNumColors is actual number of entries in color table
  ------------------------------------------------------------------*/

int PackedDibGetNumColors (BITMAPINFO * pPackedDib)
{
     int iNumColors ;

     iNumColors = PackedDibGetColorsUsed (pPackedDib) ;

     if (iNumColors == 0 && PackedDibGetBitCount (pPackedDib) < 16)
          iNumColors = 1 << PackedDibGetBitCount (pPackedDib) ;

     return iNumColors ;
}

int PackedDibGetColorTableSize (BITMAPINFO * pPackedDib)
{
     if (pPackedDib->bmiHeader.biSize == sizeof (BITMAPCOREHEADER))
          return PackedDibGetNumColors (pPackedDib) * sizeof (RGBTRIPLE) ;
     else
          return PackedDibGetNumColors (pPackedDib) * sizeof (RGBQUAD) ;
}

RGBQUAD * PackedDibGetColorTablePtr (BITMAPINFO * pPackedDib)
{
     if (PackedDibGetNumColors (pPackedDib) == 0)
          return 0 ;

     return (RGBQUAD *) (((BYTE *) pPackedDib) + 
                                   PackedDibGetInfoHeaderSize (pPackedDib)) ;
}

RGBQUAD * PackedDibGetColorTableEntry (BITMAPINFO * pPackedDib, int i)
{
     if (PackedDibGetNumColors (pPackedDib) == 0)
          return 0 ;

     if (pPackedDib->bmiHeader.biSize == sizeof (BITMAPCOREHEADER))
          return (RGBQUAD *) 
               (((RGBTRIPLE *) PackedDibGetColorTablePtr (pPackedDib)) + i) ;
     else
          return PackedDibGetColorTablePtr (pPackedDib) + i ;
}

/*------------------------------
   PackedDibGetBitsPtr finally!
  ------------------------------*/

BYTE * PackedDibGetBitsPtr (BITMAPINFO * pPackedDib)
{
     return ((BYTE *) pPackedDib) + PackedDibGetInfoHeaderSize (pPackedDib) +
                                    PackedDibGetColorTableSize (pPackedDib) ;
}

/*----------------------------------------------------------------------- 
   PackedDibGetBitsSize can be calculated from the height and row length
          if it's not explicitly in the biSizeImage field
  -----------------------------------------------------------------------*/

int PackedDibGetBitsSize (BITMAPINFO * pPackedDib)
{
     if ((pPackedDib->bmiHeader.biSize != sizeof (BITMAPCOREHEADER)) &&
         (pPackedDib->bmiHeader.biSizeImage != 0))
         return pPackedDib->bmiHeader.biSizeImage ;

     return PackedDibGetHeight (pPackedDib) * 
            PackedDibGetRowLength (pPackedDib) ;
}

/*----------------------------------------------------------------
   PackedDibCreatePalette creates logical palette from Packed DIB
  ----------------------------------------------------------------*/

HPALETTE PackedDibCreatePalette (BITMAPINFO * pPackedDib)
{
     HPALETTE     hPalette ;
     int          i, iNumColors ;
     LOGPALETTE * plp ;
     RGBQUAD    * prgb ;

     if (0 == (iNumColors = PackedDibGetNumColors (pPackedDib)))
          return NULL ;

     plp = (LOGPALETTE*)malloc (sizeof (LOGPALETTE) * 
                         (iNumColors - 1) * sizeof (PALETTEENTRY)) ;

     plp->palVersion    = 0x0300 ;
     plp->palNumEntries = iNumColors ;

     for (i = 0 ; i < iNumColors ; i++)
     {
          prgb = PackedDibGetColorTableEntry (pPackedDib, i) ;

          plp->palPalEntry[i].peRed   = prgb->rgbRed ;
          plp->palPalEntry[i].peGreen = prgb->rgbGreen ;
          plp->palPalEntry[i].peBlue  = prgb->rgbBlue ;
          plp->palPalEntry[i].peFlags = 0 ;
     }

     hPalette = CreatePalette (plp) ;
     free (plp) ;

     return hPalette ;
}


        第一个函数是 PackedDibLoad,它接收一个文件名参数,并返回一个在内存中的紧凑 DIB 的指针。所有其他的函数都接收这个紧凑 DIB 的指针作为第一个参数,并返回该紧凑 DIB 的有关信息。这些函数按“自下向上”的顺序排列在文件中。每个函数使用从前面的函数所获取的信息。

        我承认以上列出的函数并不是一套完整的可用于紧凑 DIB 的函数集。实际上,我从未试图做出这样的一组函数来,因为我认为这样使用紧凑 DIB 不是一个好方法。如果你想编写下面的函数,就会明显感到这种方式的缺陷:

dwPixel = PackedDibGetPixel (pPackedDib, x, y);
这类函数会涉及很多嵌套的函数调用,会使程序变得缓慢而低效。在本章后面我将描述一种我认为更好的方法。

        此外,你还会注意到,其中有许多函数需要对 OS/2 兼容的 DIB 作不同处理;因此,函数经常会检查 BITMAPINFO 结构的第一个字段的值是否等于 BITMAPCOREHEADER 结构的大小。

        这里特别值得注意的最后一个函数 PackedDibCreatePalette。此函数使用 DIB 里的颜色表来创建一个调色板。如果 DIB 没有颜色表(每像素 16、24 或 32 位的 DIB),则不创建调色板。从 DIB 颜色表创建的调色板有时被称为 DIB 的原生(native)调色板。

        如下显示的 SHOWDIB3 用到了 PACKEDIB 文件中的函数。

/*------------------------------------------------
SHOWDIB3.C -- Displays DIB with native palette
(c) Charles Petzold, 1998
------------------------------------------------*/

#include 
#include "PackeDib.h"
#include "resource.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

TCHAR szAppName[] = TEXT("ShowDib3");

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	HWND     hwnd;
	MSG      msg;
	WNDCLASS wndclass;

	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName = szAppName;
	wndclass.lpszClassName = szAppName;

	if (!RegisterClass(&wndclass))
	{
		MessageBox(NULL, TEXT("This program requires Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	hwnd = CreateWindow(szAppName, TEXT("Show DIB #3: Native Palette"),
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hInstance, NULL);

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);

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

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static BITMAPINFO * pPackedDib;
	static HPALETTE     hPalette;
	static int          cxClient, cyClient;
	static OPENFILENAME ofn;
	static TCHAR        szFileName[MAX_PATH], szTitleName[MAX_PATH];
	static TCHAR        szFilter[] = TEXT("Bitmap Files (*.BMP)\0*.bmp\0")
		TEXT("All Files (*.*)\0*.*\0\0");
	HDC                 hdc;
	PAINTSTRUCT         ps;

	switch (message)
	{
	case WM_CREATE:
		ofn.lStructSize = sizeof(OPENFILENAME);
		ofn.hwndOwner = hwnd;
		ofn.hInstance = NULL;
		ofn.lpstrFilter = szFilter;
		ofn.lpstrCustomFilter = NULL;
		ofn.nMaxCustFilter = 0;
		ofn.nFilterIndex = 0;
		ofn.lpstrFile = szFileName;
		ofn.nMaxFile = MAX_PATH;
		ofn.lpstrFileTitle = szTitleName;
		ofn.nMaxFileTitle = MAX_PATH;
		ofn.lpstrInitialDir = NULL;
		ofn.lpstrTitle = NULL;
		ofn.Flags = 0;
		ofn.nFileOffset = 0;
		ofn.nFileExtension = 0;
		ofn.lpstrDefExt = TEXT("bmp");
		ofn.lCustData = 0;
		ofn.lpfnHook = NULL;
		ofn.lpTemplateName = NULL;

		return 0;

	case WM_SIZE:
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);
		return 0;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDM_FILE_OPEN:

			// Show the File Open dialog box

			if (!GetOpenFileName(&ofn))
				return 0;

			// If there's an existing packed DIB, free the memory

			if (pPackedDib)
			{
				free(pPackedDib);
				pPackedDib = NULL;
			}

			// If there's an existing logical palette, delete it

			if (hPalette)
			{
				DeleteObject(hPalette);
				hPalette = NULL;
			}

			// Load the packed DIB into memory

			SetCursor(LoadCursor(NULL, IDC_WAIT));
			ShowCursor(TRUE);

			pPackedDib = PackedDibLoad(szFileName);

			ShowCursor(FALSE);
			SetCursor(LoadCursor(NULL, IDC_ARROW));

			if (pPackedDib)
			{
				// Create the palette from the DIB color table

				hPalette = PackedDibCreatePalette(pPackedDib);
			}
			else
			{
				MessageBox(hwnd, TEXT("Cannot load DIB file"),
					szAppName, 0);
			}
			InvalidateRect(hwnd, NULL, TRUE);
			return 0;
		}
		break;

	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		if (hPalette)
		{
			SelectPalette(hdc, hPalette, FALSE);
			RealizePalette(hdc);
		}

		if (pPackedDib)
			SetDIBitsToDevice(hdc,
			0,
			0,
			PackedDibGetWidth(pPackedDib),
			PackedDibGetHeight(pPackedDib),
			0,
			0,
			0,
			PackedDibGetHeight(pPackedDib),
			PackedDibGetBitsPtr(pPackedDib),
			pPackedDib,
			DIB_RGB_COLORS);
		EndPaint(hwnd, &ps);
		return 0;

	case WM_QUERYNEWPALETTE:
		if (!hPalette)
			return FALSE;

		hdc = GetDC(hwnd);
		SelectPalette(hdc, hPalette, FALSE);
		RealizePalette(hdc);
		InvalidateRect(hwnd, NULL, TRUE);

		ReleaseDC(hwnd, hdc);
		return TRUE;

	case WM_PALETTECHANGED:
		if (!hPalette || (HWND)wParam == hwnd)
			break;

		hdc = GetDC(hwnd);
		SelectPalette(hdc, hPalette, FALSE);
		RealizePalette(hdc);
		UpdateColors(hdc);

		ReleaseDC(hwnd, hdc);
		break;


	case WM_DESTROY:
		if (pPackedDib)
			free(pPackedDib);

		if (hPalette)
			DeleteObject(hPalette);

		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}
SHOWDIB3.RC (excerpts)

// Microsoft Visual C++ 生成的资源脚本。
//
#include "resource.h"

/
//
// Menu
//

SHOWDIB3 MENU DISCARDABLE
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&Open", IDM_FILE_OPEN
    END
END
RESOURCE.H (excerpts)

// Microsoft Visual C++ generated include file.
// Used by ShowDib3.rc
#define IDM_FILE_OPEN                   40001

        SHOWDIB3 的窗口过程用一个静态变量来存储指向紧凑 DIB 的指针,该指针来自于在文件打开命令中调用的 PACKEDIB.C 里的 PackedDibLoad 函数。在该命令的处理过程中,SHOWDIB3 还调用了 PackedDIbCreatePalette 函数来获得该 DIB 的可能的调色板。请注意,每当 SHOWDIB 准备加载一个新的 DIB 时,它首先释放以前 DIB 的内存,并伸出以前的 DIB 的调色板。在处理 WM_DESTROY 消息的过程中,最后一个 DIB 内存最终会被释放,其对应的调色板最终也会被删除。

        处理 WM_PAINT 消息非常简单:如果调色板存在,SHOWDIB3 就把它选进设备环境,并实现它。然后,它调用 SetDIBitsToDevice,把从 PACKEDIB 相关函数获取的信息(例如,DIB 的宽度、高度、像素位的指针等)传递给该函数。

        另外请记住,SHOWDIB3 是基于 DIB 中的颜色表来创建调色板的。如果在 DIB 中没有颜色表——在多数情况,像 16 位、24 位和 32 位 DIB 都没有,则不必创建调色板。当这个 DIB 在 8 位视频模式中显示时,它只用标准的 20 种保留色来显示。

        解决此问题有两个办法。第一个办法是使用一个“通用”的可适用于大多数图像的调色板。你可以自己构造这样的调色板。第二个办法则需要查看所有该 DIB 的像素位,来确定显示图像时所需的最佳颜色。很明显,这牵涉到更多的工作量(既有程序员的,也有处理器的),我将在本章结束前演示这种办法的实现方式。

16.3.2  通用调色板

        如下所示的 SHOWDIB4 程序构造了一个通用调色板,并用它来显示加载该程序的所有 DIB。除此以外,SHOWDIB4 和 SHOWDIB3 很相似。

/*-------------------------------------------------------
SHOWDIB4.C -- Displays DIB with "all-purpose" palette
(c) Charles Petzold, 1998
-------------------------------------------------------*/

#include 
#include "PackeDib.h"
#include "resource.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

TCHAR szAppName[] = TEXT("ShowDib4");

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	HWND     hwnd;
	MSG      msg;
	WNDCLASS wndclass;

	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName = szAppName;
	wndclass.lpszClassName = szAppName;

	if (!RegisterClass(&wndclass))
	{
		MessageBox(NULL, TEXT("This program requires Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	hwnd = CreateWindow(szAppName, TEXT("Show DIB #4: All-Purpose Palette"),
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hInstance, NULL);

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);

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

/*------------------------------------------------------------------------
CreateAllPurposePalette: Creates a palette suitable for a wide variety
of images; the palette has 247 entries, but 15 of them are
duplicates or match the standard 20 colors.
------------------------------------------------------------------------*/

HPALETTE CreateAllPurposePalette(void)
{
	HPALETTE hPalette;
	int          i, incr, R, G, B;
	LOGPALETTE * plp;

	plp = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) + 246 * sizeof(PALETTEENTRY));

	plp->palVersion = 0x0300;
	plp->palNumEntries = 247;

	// The following loop calculates 31 gray shades, but 3 of them
	//        will match the standard 20 colors

	for (i = 0, G = 0, incr = 8; G <= 0xFF; i++, G += incr)
	{
		plp->palPalEntry[i].peRed = (BYTE)G;
		plp->palPalEntry[i].peGreen = (BYTE)G;
		plp->palPalEntry[i].peBlue = (BYTE)G;
		plp->palPalEntry[i].peFlags = 0;

		incr = (incr == 9 ? 8 : 9);
	}

	// The following loop is responsible for 216 entries, but 8 of 
	//        them will match the standard 20 colors, and another
	//        4 of them will match the gray shades above.

	for (R = 0; R <= 0xFF; R += 0x33)
		for (G = 0; G <= 0xFF; G += 0x33)
			for (B = 0; B <= 0xFF; B += 0x33)
			{
				plp->palPalEntry[i].peRed = (BYTE)R;
				plp->palPalEntry[i].peGreen = (BYTE)G;
				plp->palPalEntry[i].peBlue = (BYTE)B;
				plp->palPalEntry[i].peFlags = 0;

				i++;
			}
	hPalette = CreatePalette(plp);

	free(plp);
	return hPalette;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static BITMAPINFO * pPackedDib;
	static HPALETTE     hPalette;
	static int          cxClient, cyClient;
	static OPENFILENAME ofn;
	static TCHAR        szFileName[MAX_PATH], szTitleName[MAX_PATH];
	static TCHAR        szFilter[] = TEXT("Bitmap Files (*.BMP)\0*.bmp\0")
		TEXT("All Files (*.*)\0*.*\0\0");
	HDC                 hdc;
	PAINTSTRUCT         ps;

	switch (message)
	{
	case WM_CREATE:
		ofn.lStructSize = sizeof(OPENFILENAME);
		ofn.hwndOwner = hwnd;
		ofn.hInstance = NULL;
		ofn.lpstrFilter = szFilter;
		ofn.lpstrCustomFilter = NULL;
		ofn.nMaxCustFilter = 0;
		ofn.nFilterIndex = 0;
		ofn.lpstrFile = szFileName;
		ofn.nMaxFile = MAX_PATH;
		ofn.lpstrFileTitle = szTitleName;
		ofn.nMaxFileTitle = MAX_PATH;
		ofn.lpstrInitialDir = NULL;
		ofn.lpstrTitle = NULL;
		ofn.Flags = 0;
		ofn.nFileOffset = 0;
		ofn.nFileExtension = 0;
		ofn.lpstrDefExt = TEXT("bmp");
		ofn.lCustData = 0;
		ofn.lpfnHook = NULL;
		ofn.lpTemplateName = NULL;

		// Create the All-Purpose Palette

		hPalette = CreateAllPurposePalette();
		return 0;

	case WM_SIZE:
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);
		return 0;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDM_FILE_OPEN:

			// Show the File Open dialog box

			if (!GetOpenFileName(&ofn))
				return 0;

			// If there's an existing packed DIB, free the memory

			if (pPackedDib)
			{
				free(pPackedDib);
				pPackedDib = NULL;
			}

			// Load the packed DIB into memory

			SetCursor(LoadCursor(NULL, IDC_WAIT));
			ShowCursor(TRUE);

			pPackedDib = PackedDibLoad(szFileName);

			ShowCursor(FALSE);
			SetCursor(LoadCursor(NULL, IDC_ARROW));

			if (!pPackedDib)
			{
				MessageBox(hwnd, TEXT("Cannot load DIB file"),
					szAppName, 0);
			}
			InvalidateRect(hwnd, NULL, TRUE);
			return 0;
		}
		break;

	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		if (pPackedDib)
		{
			SelectPalette(hdc, hPalette, FALSE);
			RealizePalette(hdc);

			SetDIBitsToDevice(hdc,
				0,
				0,
				PackedDibGetWidth(pPackedDib),
				PackedDibGetHeight(pPackedDib),
				0,
				0,
				0,
				PackedDibGetHeight(pPackedDib),
				PackedDibGetBitsPtr(pPackedDib),
				pPackedDib,
				DIB_RGB_COLORS);
		}
		EndPaint(hwnd, &ps);
		return 0;

	case WM_QUERYNEWPALETTE:
		hdc = GetDC(hwnd);
		SelectPalette(hdc, hPalette, FALSE);
		RealizePalette(hdc);
		InvalidateRect(hwnd, NULL, TRUE);

		ReleaseDC(hwnd, hdc);
		return TRUE;

	case WM_PALETTECHANGED:
		if ((HWND)wParam != hwnd)
		{
			hdc = GetDC(hwnd);
			SelectPalette(hdc, hPalette, FALSE);
			RealizePalette(hdc);
			UpdateColors(hdc);

			ReleaseDC(hwnd, hdc);
		}
		break;

	case WM_DESTROY:
		if (pPackedDib)
			free(pPackedDib);

		DeleteObject(hPalette);

		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}
SHOWDIB4.RC (excerpts)

// Microsoft Visual C++ 生成的资源脚本。
//
#include "resource.h"

/
//
// Menu
//

SHOWDIB4 MENU DISCARDABLE
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&Open", IDM_FILE_OPEN
    END
END
RESOURCE.H (excerpts)

// Microsoft Visual C++ generated include file.
// Used by ShowDib4.rc
#define IDM_FILE_OPEN                   40001

        在处理 WM_CREATE 消息的过程中,SHOWDIB4 调用 CreateAllPurposePalette 函数。它在整个程序的运行过程中保留此调色板,并在处理 WM_DESTROY 消息的过程中销毁此调色板。因为程序知道调色板总是存在的,所以它在处理 WM_PAINT、WM_QUERYNEWPALETTE 或 WM_PALETTECHANGED 消息时就不需要检查它是否存在。

        CreateAllPurposePalette 函数看起来好像是创建了一个包含 247 种颜色的逻辑调色板,这超过了程序通常使用的系统调色板所能提供的 236 种颜色。它确实是这样做了,但只是为了方便起见。其中的十五种颜色要么是重复的,要么就被映射到了标准的 20 种保留色上。

        CreateAllPurposePalette 首先创建 31 种灰度色调,它们的红色、绿色和蓝色值分别为 0x00、0x09、0x11、0x1A、0x22、0x2B、0x33、0x3C、0x44、0x4D、0x55、0x5E、0x66、0x6F、0x77、0x80、0x88、0x91、0x99、0xA2、0xAA、0xB3、0xBB、0xC4、0xCC、0xD5、0xDD、0xE6、0xEE、0xF9 和 0xFF。这些灰度色调中,第一个、最后一个和中间一个在 20 种保留色中。接下来该函数创建红色、绿色和蓝色值分别为 0x00、0x33、0x66、0x99、0xCC 和 0xFF 的所有组合。这些共有 216 种颜色,但其中八种是标准 20 种系统保留色中的颜色,另四种是前面计算过的灰度值的重复。如果将 PALETTEENTRY 结构的 peFlags 字段设置为 0,那么 Windows 就不会将重复的颜色加入到系统调色板中。

        很明显,一个不想计算 16 位、24 位或 32 位 DIB 的最佳调色板的程序可以继续使用 DIB 颜色表来显示 8 位 DIB。但 SHOWDIB4 并没有这样做,而是全部使用通用调色板。这是因为 SHOWDIB4 只是一个示范程序,你可以用它来跟 SHOWDIB3 显示的 8 位 DIB 作比较。只要看看一些彩色的人像 DIB,你就会得出结论,SHOWDIB4 没有足够多的颜色来精确地呈现皮肤色调。

        如果用 SHOWDIB4 中的 CreateAllPurposePalette 函数做实验,比如说减少逻辑调色板的大小到仅仅包含几个条目,你就会发现,当一个调色板被选入设备环境时,Windows 将只使用该调色板中的颜色,而不会使用系统调色板中的 20 种标准颜色

16.3.3  半色调调色板

        Windows API 包括一个通用调色板,程序可调用 CreateHalftonePalette 来得到它。你可以像使用 SHOWDIB4 中通过调用 CreateAllPurposePalette 得到的调色板那样来使用它,也可以额把它和称为 HALFTONE(半色调)的位图拉伸模式一起使用,该模式由 SetStretchBltMode 设置。SHOWDIB5 程序演示了如何使用半色调调色板。

/*--------------------------------------------------
SHOWDIB5.C -- Displays DIB with halftone palette
(c) Charles Petzold, 1998
--------------------------------------------------*/

#include 
#include "PackeDib.h"
#include "resource.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

TCHAR szAppName[] = TEXT("ShowDib5");

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	HWND     hwnd;
	MSG      msg;
	WNDCLASS wndclass;

	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName = szAppName;
	wndclass.lpszClassName = szAppName;

	if (!RegisterClass(&wndclass))
	{
		MessageBox(NULL, TEXT("This program requires Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	hwnd = CreateWindow(szAppName, TEXT("Show DIB #5: Halftone Palette"),
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hInstance, NULL);

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);

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

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static BITMAPINFO * pPackedDib;
	static HPALETTE     hPalette;
	static int          cxClient, cyClient;
	static OPENFILENAME ofn;
	static TCHAR        szFileName[MAX_PATH], szTitleName[MAX_PATH];
	static TCHAR        szFilter[] = TEXT("Bitmap Files (*.BMP)\0*.bmp\0")
		TEXT("All Files (*.*)\0*.*\0\0");
	HDC                 hdc;
	PAINTSTRUCT         ps;

	switch (message)
	{
	case WM_CREATE:
		ofn.lStructSize = sizeof(OPENFILENAME);
		ofn.hwndOwner = hwnd;
		ofn.hInstance = NULL;
		ofn.lpstrFilter = szFilter;
		ofn.lpstrCustomFilter = NULL;
		ofn.nMaxCustFilter = 0;
		ofn.nFilterIndex = 0;
		ofn.lpstrFile = szFileName;
		ofn.nMaxFile = MAX_PATH;
		ofn.lpstrFileTitle = szTitleName;
		ofn.nMaxFileTitle = MAX_PATH;
		ofn.lpstrInitialDir = NULL;
		ofn.lpstrTitle = NULL;
		ofn.Flags = 0;
		ofn.nFileOffset = 0;
		ofn.nFileExtension = 0;
		ofn.lpstrDefExt = TEXT("bmp");
		ofn.lCustData = 0;
		ofn.lpfnHook = NULL;
		ofn.lpTemplateName = NULL;

		// Create the All-Purpose Palette

		hdc = GetDC(hwnd);
		hPalette = CreateHalftonePalette(hdc);
		ReleaseDC(hwnd, hdc);
		return 0;

	case WM_SIZE:
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);
		return 0;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDM_FILE_OPEN:

			// Show the File Open dialog box

			if (!GetOpenFileName(&ofn))
				return 0;

			// If there's an existing packed DIB, free the memory

			if (pPackedDib)
			{
				free(pPackedDib);
				pPackedDib = NULL;
			}

			// Load the packed DIB into memory

			SetCursor(LoadCursor(NULL, IDC_WAIT));
			ShowCursor(TRUE);

			pPackedDib = PackedDibLoad(szFileName);

			ShowCursor(FALSE);
			SetCursor(LoadCursor(NULL, IDC_ARROW));

			if (!pPackedDib)
			{
				MessageBox(hwnd, TEXT("Cannot load DIB file"),
					szAppName, 0);
			}
			InvalidateRect(hwnd, NULL, TRUE);
			return 0;
		}
		break;

	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		if (pPackedDib)
		{
			// Set halftone stretch mode

			SetStretchBltMode(hdc, HALFTONE);
			SetBrushOrgEx(hdc, 0, 0, NULL);

			// Select and realize halftone palette

			SelectPalette(hdc, hPalette, FALSE);
			RealizePalette(hdc);

			// StretchDIBits rather than SetDIBitsToDevice

			StretchDIBits(hdc,
				0,
				0,
				PackedDibGetWidth(pPackedDib),
				PackedDibGetHeight(pPackedDib),
				0,
				0,
				PackedDibGetWidth(pPackedDib),
				PackedDibGetHeight(pPackedDib),
				PackedDibGetBitsPtr(pPackedDib),
				pPackedDib,
				DIB_RGB_COLORS,
				SRCCOPY);
		}
		EndPaint(hwnd, &ps);
		return 0;

	case WM_QUERYNEWPALETTE:
		hdc = GetDC(hwnd);
		SelectPalette(hdc, hPalette, FALSE);
		RealizePalette(hdc);
		InvalidateRect(hwnd, NULL, TRUE);

		ReleaseDC(hwnd, hdc);
		return TRUE;

	case WM_PALETTECHANGED:
		if ((HWND)wParam != hwnd)
		{
			hdc = GetDC(hwnd);
			SelectPalette(hdc, hPalette, FALSE);
			RealizePalette(hdc);
			UpdateColors(hdc);

			ReleaseDC(hwnd, hdc);
		}
		break;

	case WM_DESTROY:
		if (pPackedDib)
			free(pPackedDib);

		DeleteObject(hPalette);

		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}
SHOWDIB5.RC (excerpts)

// Microsoft Visual C++ 生成的资源脚本。
//
#include "resource.h"

/
//
// Menu
//

SHOWDIB5 MENU DISCARDABLE
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&Open", IDM_FILE_OPEN
    END
END
RESOURCE.H (excerpts)

// Microsoft Visual C++ generated include file.
// Used by ShowDib5.rc
#define IDM_FILE_OPEN                   40001

        SHOWDIB5 和 SHOWDIB4 程序类似,它不使用 DIB 颜色表,而是使用适合于更多图像的通用调色板。SHOWDIB5 使用 Windows 提供的逻辑调色板来达到这个目的,该调色板的句柄可用 CreateHalftonePalette 函数获取。

        此半色调调色板比起 SHOWDIB4 中 CreateAllPurposePalette 函数创建的调色板,效果提高并不多,实际上,如果单独使用它,结果会很相似。但是,如果你调用下面这两个函数,并使用 StretchDIBits 而不是 SetDIBitsToDevice 来显示 DIB,结果会使你惊喜:

SetStretchBltMode(hdc, HALFTONE);
SetBrushOrgEx(hdc, x, y, NULL); 
其中,x 和 y 是 DIB 左上角的设备坐标。皮肤色调比起使用 CreateAllPurposePalette 函数的时候,或是使用 CreateHalftonePalette 函数而不设置位图拉伸模式的时候要精确得多。 Windows 通过对半色调调色板的颜色使用一种抖动模式,来更好地在 8 位视频卡上模拟原始图像的颜色。当然,你可能也预料到了,缺点是它需要更多的处理时间。

16.3.4  索引调色板颜色

        现在已到了处理 SetDIBitsToDevice、StretchDIBits、CreateDIBtimap、SetDIBits、GetDIBits 和 CreateDIBSection 函数的参数 fClrUse 的时候。多数情况下,你可以把这个参数设为 DIB_RGB_COLORS(0)。但是,你也可以将它设为 DIB_PAL_COLORS。在这种情况下,BITMAPINFO 结构中的颜色表包含的应该是逻辑调色的 16 位索引值而不是 RGB 颜色值。此逻辑调色板是当前被送入作为函数的第一个参数而制定的设备环境中的那个调色板。事实上对 CreateDIBSection,只有在使用 DIB_PAL_COLORS 时,才需要把第一个参数设为非空的设备环境。

        DIB_PAL_COLORS 的作用是什么呢?它能帮助改善程序性能。假如你想通过调用 SetDIBitsToDevice 在 8 位视频模式中显示一个 8 位 DIB,那么 Windows 必须首先对 DIB 颜色表中的所有颜色和设备上可用的颜色进行最接近颜色搜索。然后,它可以设定一个小表,让它将 DIB 像素值映射到设备像素。最多时这意味着 256 个最接近颜色搜索。而如果 DIB 颜色表中包含的是被选入设备环境中的逻辑调色板的索引值,这就可以跳过这些最接近颜色搜索。

        SHOWDIB6 程序类似于 SHOWDIB3,不同之处仅在于它使用了调色板索引。

/*------------------------------------------------
SHOWDIB6.C -- Display DIB with palette indices
(c) Charles Petzold, 1998
------------------------------------------------*/

#include 
#include "PackeDib.h"
#include "resource.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

TCHAR szAppName[] = TEXT("ShowDib6");

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	HWND     hwnd;
	MSG      msg;
	WNDCLASS wndclass;

	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName = szAppName;
	wndclass.lpszClassName = szAppName;

	if (!RegisterClass(&wndclass))
	{
		MessageBox(NULL, TEXT("This program requires Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	hwnd = CreateWindow(szAppName, TEXT("Show DIB #6: Palette Indices"),
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hInstance, NULL);

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);

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

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static BITMAPINFO * pPackedDib;
	static HPALETTE     hPalette;
	static int          cxClient, cyClient;
	static OPENFILENAME ofn;
	static TCHAR        szFileName[MAX_PATH], szTitleName[MAX_PATH];
	static TCHAR        szFilter[] = TEXT("Bitmap Files (*.BMP)\0*.bmp\0")
		TEXT("All Files (*.*)\0*.*\0\0");
	HDC                 hdc;
	int                 i, iNumColors;
	PAINTSTRUCT         ps;
	WORD              * pwIndex;

	switch (message)
	{
	case WM_CREATE:
		ofn.lStructSize = sizeof(OPENFILENAME);
		ofn.hwndOwner = hwnd;
		ofn.hInstance = NULL;
		ofn.lpstrFilter = szFilter;
		ofn.lpstrCustomFilter = NULL;
		ofn.nMaxCustFilter = 0;
		ofn.nFilterIndex = 0;
		ofn.lpstrFile = szFileName;
		ofn.nMaxFile = MAX_PATH;
		ofn.lpstrFileTitle = szTitleName;
		ofn.nMaxFileTitle = MAX_PATH;
		ofn.lpstrInitialDir = NULL;
		ofn.lpstrTitle = NULL;
		ofn.Flags = 0;
		ofn.nFileOffset = 0;
		ofn.nFileExtension = 0;
		ofn.lpstrDefExt = TEXT("bmp");
		ofn.lCustData = 0;
		ofn.lpfnHook = NULL;
		ofn.lpTemplateName = NULL;

		return 0;

	case WM_SIZE:
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);
		return 0;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDM_FILE_OPEN:

			// Show the File Open dialog box

			if (!GetOpenFileName(&ofn))
				return 0;

			// If there's an existing packed DIB, free the memory

			if (pPackedDib)
			{
				free(pPackedDib);
				pPackedDib = NULL;
			}

			// If there's an existing logical palette, delete it

			if (hPalette)
			{
				DeleteObject(hPalette);
				hPalette = NULL;
			}

			// Load the packed DIB into memory

			SetCursor(LoadCursor(NULL, IDC_WAIT));
			ShowCursor(TRUE);

			pPackedDib = PackedDibLoad(szFileName);

			ShowCursor(FALSE);
			SetCursor(LoadCursor(NULL, IDC_ARROW));

			if (pPackedDib)
			{
				// Create the palette from the DIB color table

				hPalette = PackedDibCreatePalette(pPackedDib);

				// Replace DIB color table with indices

				if (hPalette)
				{
					iNumColors = PackedDibGetNumColors(pPackedDib);
					pwIndex = (WORD *)
						PackedDibGetColorTablePtr(pPackedDib);

					for (i = 0; i < iNumColors; i++)
						pwIndex[i] = (WORD)i;
				}
			}
			else
			{
				MessageBox(hwnd, TEXT("Cannot load DIB file"),
					szAppName, 0);
			}
			InvalidateRect(hwnd, NULL, TRUE);
			return 0;
		}
		break;

	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		if (hPalette)
		{
			SelectPalette(hdc, hPalette, FALSE);
			RealizePalette(hdc);
		}

		if (pPackedDib)
			SetDIBitsToDevice(hdc,
			0,
			0,
			PackedDibGetWidth(pPackedDib),
			PackedDibGetHeight(pPackedDib),
			0,
			0,
			0,
			PackedDibGetHeight(pPackedDib),
			PackedDibGetBitsPtr(pPackedDib),
			pPackedDib,
			DIB_PAL_COLORS);
		EndPaint(hwnd, &ps);
		return 0;

	case WM_QUERYNEWPALETTE:
		if (!hPalette)
			return FALSE;

		hdc = GetDC(hwnd);
		SelectPalette(hdc, hPalette, FALSE);
		RealizePalette(hdc);
		InvalidateRect(hwnd, NULL, TRUE);

		ReleaseDC(hwnd, hdc);
		return TRUE;

	case WM_PALETTECHANGED:
		if (!hPalette || (HWND)wParam == hwnd)
			break;

		hdc = GetDC(hwnd);
		SelectPalette(hdc, hPalette, FALSE);
		RealizePalette(hdc);
		UpdateColors(hdc);

		ReleaseDC(hwnd, hdc);
		break;


	case WM_DESTROY:
		if (pPackedDib)
			free(pPackedDib);

		if (hPalette)
			DeleteObject(hPalette);

		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}
SHOWDIB6.RC (excerpts)

// Microsoft Visual C++ 生成的资源脚本。
//
#include "resource.h"

/
//
// Menu
//

SHOWDIB6 MENU DISCARDABLE
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&Open", IDM_FILE_OPEN
    END
END
RESOURCE.H (excerpts)

// Microsoft Visual C++ generated include file.
// Used by ShowDib6.rc
#define IDM_FILE_OPEN                   40001

        SHOWDIB6 将 DIB 加载到内存,并从中创建一个调色板,然后它将 DIB 颜色表中的颜色替换为从 0 开始的 WORD 类型的索引值。PackedDibGetNumColors 函数指明有多少种颜色,而 PackedDibGetColorTablePtr 函数返回一个指向 DIB 颜色表开头的指针。

        请注意,这种技术只有在直接从该 DIB 的颜色表创建调色板时才可行。如果使用通用调色板,那么你需要自己进行最接近颜色搜索来产生填入 DIB 的索引值,但那样做就没什么意义了。

        如果使用了调色板索引值,在把该 DIB 存到磁盘前一定要替换 DIB 颜色表此外不要把包含调色板索引值的 DIB 放到剪贴板中。实际上,只在显示 DIB 之前才把调色板索引值填入,而在显示后立刻改回 RGB 颜色值的做法更安全。

16.3.5  调色板和位图对象

        SHOWDIB7 程序显示了如何将调色板和某些 DIB 一起使用,这些 DIB 被 CreateDIBitmap 函数转换成了 GDI 位图对象。

/*------------------------------------------
SHOWDIB7.C -- Shows DIB converted to DDB
(c) Charles Petzold, 1998
------------------------------------------*/

#include 
#include "PackeDib.h"
#include "resource.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

TCHAR szAppName[] = TEXT("ShowDib7");

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	HWND     hwnd;
	MSG      msg;
	WNDCLASS wndclass;

	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName = szAppName;
	wndclass.lpszClassName = szAppName;

	if (!RegisterClass(&wndclass))
	{
		MessageBox(NULL, TEXT("This program requires Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	hwnd = CreateWindow(szAppName, TEXT("Show DIB #7: Converted to DDB"),
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hInstance, NULL);

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);

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

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HBITMAP      hBitmap;
	static HPALETTE     hPalette;
	static int          cxClient, cyClient;
	static OPENFILENAME ofn;
	static TCHAR        szFileName[MAX_PATH], szTitleName[MAX_PATH];
	static TCHAR        szFilter[] = TEXT("Bitmap Files (*.BMP)\0*.bmp\0")
		TEXT("All Files (*.*)\0*.*\0\0");
	BITMAP              bitmap;
	BITMAPINFO        * pPackedDib;
	HDC                 hdc, hdcMem;
	PAINTSTRUCT         ps;

	switch (message)
	{
	case WM_CREATE:
		ofn.lStructSize = sizeof(OPENFILENAME);
		ofn.hwndOwner = hwnd;
		ofn.hInstance = NULL;
		ofn.lpstrFilter = szFilter;
		ofn.lpstrCustomFilter = NULL;
		ofn.nMaxCustFilter = 0;
		ofn.nFilterIndex = 0;
		ofn.lpstrFile = szFileName;
		ofn.nMaxFile = MAX_PATH;
		ofn.lpstrFileTitle = szTitleName;
		ofn.nMaxFileTitle = MAX_PATH;
		ofn.lpstrInitialDir = NULL;
		ofn.lpstrTitle = NULL;
		ofn.Flags = 0;
		ofn.nFileOffset = 0;
		ofn.nFileExtension = 0;
		ofn.lpstrDefExt = TEXT("bmp");
		ofn.lCustData = 0;
		ofn.lpfnHook = NULL;
		ofn.lpTemplateName = NULL;

		return 0;

	case WM_SIZE:
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);
		return 0;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDM_FILE_OPEN:

			// Show the File Open dialog box

			if (!GetOpenFileName(&ofn))
				return 0;

			// If there's an existing packed DIB, free the memory

			if (hBitmap)
			{
				DeleteObject(hBitmap);
				hBitmap = NULL;
			}

			// If there's an existing logical palette, delete it

			if (hPalette)
			{
				DeleteObject(hPalette);
				hPalette = NULL;
			}

			// Load the packed DIB into memory

			SetCursor(LoadCursor(NULL, IDC_WAIT));
			ShowCursor(TRUE);

			pPackedDib = PackedDibLoad(szFileName);

			ShowCursor(FALSE);
			SetCursor(LoadCursor(NULL, IDC_ARROW));

			if (pPackedDib)
			{
				// Create palette from the DIB and select it into DC

				hPalette = PackedDibCreatePalette(pPackedDib);

				hdc = GetDC(hwnd);

				if (hPalette)
				{
					SelectPalette(hdc, hPalette, FALSE);
					RealizePalette(hdc);
				}
				// Create the DDB from the DIB

				hBitmap = CreateDIBitmap(hdc,
					(PBITMAPINFOHEADER)pPackedDib,
					CBM_INIT,
					PackedDibGetBitsPtr(pPackedDib),
					pPackedDib,
					DIB_RGB_COLORS);
				ReleaseDC(hwnd, hdc);

				// Free the packed-DIB memory

				free(pPackedDib);
			}
			else
			{
				MessageBox(hwnd, TEXT("Cannot load DIB file"),
					szAppName, 0);
			}
			InvalidateRect(hwnd, NULL, TRUE);
			return 0;
		}
		break;

	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		if (hPalette)
		{
			SelectPalette(hdc, hPalette, FALSE);
			RealizePalette(hdc);
		}
		if (hBitmap)
		{
			GetObject(hBitmap, sizeof(BITMAP), &bitmap);

			hdcMem = CreateCompatibleDC(hdc);
			SelectObject(hdcMem, hBitmap);

			BitBlt(hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight,
				hdcMem, 0, 0, SRCCOPY);

			DeleteDC(hdcMem);
		}
		EndPaint(hwnd, &ps);
		return 0;

	case WM_QUERYNEWPALETTE:
		if (!hPalette)
			return FALSE;

		hdc = GetDC(hwnd);
		SelectPalette(hdc, hPalette, FALSE);
		RealizePalette(hdc);
		InvalidateRect(hwnd, NULL, TRUE);

		ReleaseDC(hwnd, hdc);
		return TRUE;

	case WM_PALETTECHANGED:
		if (!hPalette || (HWND)wParam == hwnd)
			break;

		hdc = GetDC(hwnd);
		SelectPalette(hdc, hPalette, FALSE);
		RealizePalette(hdc);
		UpdateColors(hdc);

		ReleaseDC(hwnd, hdc);
		break;


	case WM_DESTROY:
		if (hBitmap)
			DeleteObject(hBitmap);

		if (hPalette)
			DeleteObject(hPalette);

		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}
SHOWDIB7.RC (excerpts)

// Microsoft Visual C++ 生成的资源脚本。
//
#include "resource.h"

/
//
// Menu
//

SHOWDIB7 MENU DISCARDABLE
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&Open", IDM_FILE_OPEN
    END
END
RESOURCE.H (excerpts)

// Microsoft Visual C++ generated include file.
// Used by ShowDib7.rc
#define IDM_FILE_OPEN                   40001

        和前面的程序一样,SHOWDIB7 在处理打开文件这个菜单命令时得到一个紧凑 DIB 的指针。它还用这个紧凑 DIB 创建了一个调色板。还是在这个 WM_COMMAND 消息处理中,它得到视频显示的设备环境,把调色板选入,并实现调色板。然后 SHOWDIB7 调用 CreateDIBitmap 从该 DIB 创建了一个 DDB。如果调色板没有被选入设备环境并被实现,那么由 CreateDIBitmap 创建的 DDB 就不会使用逻辑调色板中加入的颜色。

        调用 CreateDIBitmap 之后,程序可以释放这个紧凑 DIB 所占用的内存。在 SHOWDIB7 中,pPackedDib 变量不是静态变量,而位图的句柄(hBitmap)和逻辑调色板的句柄(hPalette)则被存为静态变量。

        在处理 WM_PAINT 消息的过程中,程序再次将调色板选入设备环境中并实现。位图的宽度和高度来自 GetObject 函数。然后程序通过创建一个兼容的内存设备环境,把位图选入,再做 BitBlt,在客户区显示位图。创建(CreateDIBitmap)和显示 DDB 的时候必须使用同一个调色板。

        如果将一个位图复制到剪贴板,那么最好使用紧凑 DIB 的格式,这样方便 Windows 为感兴趣的程序提供位图对象。不过,如果需要将一个位图对象复制到剪贴板,则首先需要得到一个视频设备环境,并选择和实现一个调色板。这样 Windows 才能基于当前系统调色板将 DDB 转换成 DIB

16.3.6  调色板和 DIB 区块

        最后,SHOWDIB8 演示了如何通过 DIB 区块来使用调色板。

/*--------------------------------------------------
SHOWDIB8.C -- Shows DIB converted to DIB section
(c) Charles Petzold, 1998
--------------------------------------------------*/

#include 
#include "PackeDib.h"
#include "resource.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

TCHAR szAppName[] = TEXT("ShowDib8");

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	HWND     hwnd;
	MSG      msg;
	WNDCLASS wndclass;

	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName = szAppName;
	wndclass.lpszClassName = szAppName;

	if (!RegisterClass(&wndclass))
	{
		MessageBox(NULL, TEXT("This program requires Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	hwnd = CreateWindow(szAppName, TEXT("Show DIB #8: DIB Section"),
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hInstance, NULL);

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);

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

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HBITMAP      hBitmap;
	static HPALETTE     hPalette;
	static int          cxClient, cyClient;
	static OPENFILENAME ofn;
	static PBYTE        pBits;
	static TCHAR        szFileName[MAX_PATH], szTitleName[MAX_PATH];
	static TCHAR        szFilter[] = TEXT("Bitmap Files (*.BMP)\0*.bmp\0")
		TEXT("All Files (*.*)\0*.*\0\0");
	BITMAP              bitmap;
	BITMAPINFO        * pPackedDib;
	HDC                 hdc, hdcMem;
	PAINTSTRUCT         ps;

	switch (message)
	{
	case WM_CREATE:
		ofn.lStructSize = sizeof(OPENFILENAME);
		ofn.hwndOwner = hwnd;
		ofn.hInstance = NULL;
		ofn.lpstrFilter = szFilter;
		ofn.lpstrCustomFilter = NULL;
		ofn.nMaxCustFilter = 0;
		ofn.nFilterIndex = 0;
		ofn.lpstrFile = szFileName;
		ofn.nMaxFile = MAX_PATH;
		ofn.lpstrFileTitle = szTitleName;
		ofn.nMaxFileTitle = MAX_PATH;
		ofn.lpstrInitialDir = NULL;
		ofn.lpstrTitle = NULL;
		ofn.Flags = 0;
		ofn.nFileOffset = 0;
		ofn.nFileExtension = 0;
		ofn.lpstrDefExt = TEXT("bmp");
		ofn.lCustData = 0;
		ofn.lpfnHook = NULL;
		ofn.lpTemplateName = NULL;

		return 0;

	case WM_SIZE:
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);
		return 0;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDM_FILE_OPEN:

			// Show the File Open dialog box

			if (!GetOpenFileName(&ofn))
				return 0;

			// If there's an existing packed DIB, free the memory

			if (hBitmap)
			{
				DeleteObject(hBitmap);
				hBitmap = NULL;
			}

			// If there's an existing logical palette, delete it

			if (hPalette)
			{
				DeleteObject(hPalette);
				hPalette = NULL;
			}

			// Load the packed DIB into memory

			SetCursor(LoadCursor(NULL, IDC_WAIT));
			ShowCursor(TRUE);

			pPackedDib = PackedDibLoad(szFileName);

			ShowCursor(FALSE);
			SetCursor(LoadCursor(NULL, IDC_ARROW));

			if (pPackedDib)
			{
				// Create the DIB section from the DIB

				hBitmap = CreateDIBSection(NULL,
					pPackedDib,
					DIB_RGB_COLORS,
					(VOID**)&pBits,
					NULL, 0);

				// Copy the bits

				CopyMemory(pBits, PackedDibGetBitsPtr(pPackedDib),
					PackedDibGetBitsSize(pPackedDib));

				// Create palette from the DIB

				hPalette = PackedDibCreatePalette(pPackedDib);

				// Free the packed-DIB memory

				free(pPackedDib);
			}
			else
			{
				MessageBox(hwnd, TEXT("Cannot load DIB file"),
					szAppName, 0);
			}
			InvalidateRect(hwnd, NULL, TRUE);
			return 0;
		}
		break;

	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		if (hPalette)
		{
			SelectPalette(hdc, hPalette, FALSE);
			RealizePalette(hdc);
		}
		if (hBitmap)
		{
			GetObject(hBitmap, sizeof(BITMAP), &bitmap);

			hdcMem = CreateCompatibleDC(hdc);
			SelectObject(hdcMem, hBitmap);

			BitBlt(hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight,
				hdcMem, 0, 0, SRCCOPY);

			DeleteDC(hdcMem);
		}
		EndPaint(hwnd, &ps);
		return 0;

	case WM_QUERYNEWPALETTE:
		if (!hPalette)
			return FALSE;

		hdc = GetDC(hwnd);
		SelectPalette(hdc, hPalette, FALSE);
		RealizePalette(hdc);
		InvalidateRect(hwnd, NULL, TRUE);

		ReleaseDC(hwnd, hdc);
		return TRUE;

	case WM_PALETTECHANGED:
		if (!hPalette || (HWND)wParam == hwnd)
			break;

		hdc = GetDC(hwnd);
		SelectPalette(hdc, hPalette, FALSE);
		RealizePalette(hdc);
		UpdateColors(hdc);

		ReleaseDC(hwnd, hdc);
		break;


	case WM_DESTROY:
		if (hBitmap)
			DeleteObject(hBitmap);

		if (hPalette)
			DeleteObject(hPalette);

		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}
SHOWDIB8.RC (excerpts)

// Microsoft Visual C++ 生成的资源脚本。
//
#include "resource.h"

/
//
// Menu
//

SHOWDIB8 MENU DISCARDABLE
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&Open", IDM_FILE_OPEN
    END
END
RESOURCE.H (excerpts)

// Microsoft Visual C++ generated include file.
// Used by ShowDib8.rc
#define IDM_FILE_OPEN                   40001

        SHOWDIB7 和 SHOWDIB8 对 WM_PAINT 的处理完全相同:两个程序都使用静态变量来存储位图的句柄(hBitmap)和逻辑调色板的句柄(hPalette),都把调色板选入设备环境并实现它,都从 GetObject 函数获取位图的宽度和高度,都通过创建一个内存设备环境,把位图选入,再调用 BitBlt 来把位图显示到客户区。

        两个程序之间的最大区别是对打开文件菜单命令的处理上。得到紧凑 DIB 的指针并创建调色板后,SHOWDIB7 必须在调用 CreateDIBitmap 之前,把调色板选入视频设备环境中并实现它。而 SHOWDIB8 先取得紧凑 DIB 指针,然后调用 CreateDIBSection。因为 CreateDIBSection 不会将一个 DIB 转换为设备相关格式,所以不必把调色板选入视频设备环境。事实上,CreateDIBSection 以设备环境句柄作为第一个参数的唯一目的,就是允许使用 DIB_PAL_COLORS 标志。

        在调用 CreateDIBSection 之后,SHOWDIB8 把像素位从紧凑 DIB 复制到内存中,该内存的位置由刚才的 CreateDIBSection 函数所返回。然后 SHOWDIB8 调用 PackedDIbCreatePalette 函数来创建调色板。虽然此函数便于程序使用,但是 SHOWDIB8 也可以用 GetDIBColorTable 函数返回的信息来创建调色板。

你可能感兴趣的:(《Windows,程序设计》学习之旅,windows,编程,调色板管理器)