见招拆招《Windows程序设计》(十四)
相关的例子:下载>>> 作者:Zoologist 于2009-8-16上传 

调色板管理器

如果硬件允许,本章就没有存在的必要.尽管许多现代的显示卡提供24位颜色(也称「true color」或「数百万色」)或16位颜色(「增强色」或「数万种颜色」),一些老式显示卡每个像素只允许8位.这意味着仅有256种颜色.

我们用256种颜色能做什么呢?很明显,要显示真实世界的图像,仅16种颜色是不够的,至少要使用数千或数百万种颜色,256种颜色位于中间状态.是的,用256种颜色来显示真实世界的图像足够了,但需要根据特定的图像来指定这些颜色.这意味着操作系统不能简单地选择「标准」系列的256种颜色,就希望它们对每个应用程序都是理想的颜色.

这就是Windows调色板管理器所要涉及的全部内容.它用于指定程序在8位显示模式下执行时所需要的颜色.如果知道程序肯定不会在8位显示模式下执行,那么您也不需要使用调色板管理器.不过,由于补充了位图的一些细节,所以本章还是包含重要信息的.

使用调色板

传统上讲,调色板是画家用来混合颜色的板子.这个词也可以指画家在绘画过程中使用的所有颜色.在计算机图形中,调色板是在图形输出设备(例如视讯显示器)上可用的颜色范围.这个名词也可以指支持256色模式的显示卡上的对照表.

视频硬件

显示卡上的调色板对照表运作过程如下图所示:


 

在8位显示模式中,每个像素占8位.像素值查询包含256RGB值的对照表的地址.这些RGB值可以正好24位宽,或者小一点,通常是18位宽(即主要的红、绿和蓝各6位).每种颜色的值都输入到数字模拟转换器,以得到发送给监视器的红、绿和蓝三个模拟信号.

通常,软件可以用任意值来加载调色板对照表,但这对设备无关的窗口接口,例如Microsoft Windows,会有一些干扰.首先,Windows必须提供软件接口,以便在不直接干扰硬件的情况下,应用程序就可以存取调色板管理器.第二个问题更严重:因为所有的应用程序都共享同一个视讯显示器,而且同时执行,所以一个应用程序使用了调色板对照表可能会影响其它程序的使用.

这时就需要使用Windows调色板管理器(在Windows 3.0中提出)了.Windows保留了256种颜色中的20种,而允许应用程序修改其余的236种.(在某些情况下,应用程序最多可以改变256种颜色中的254种-只有黑色和白色除外-但这有一点麻烦).Windows为系统保留的20种颜色(有时称为20种「静态」颜色)如表16-1所示.

表16-1 256种颜色显示模式中的20种保留的颜色

像素位

RGB值

颜色名称

像素位

RGB值

颜色名称

00000000

00 00 00

11111111

FF FF FF

00000001

80 00 00

暗红

11111110

00 FF FF

00000010

00 80 00

暗绿

11111101

FF 00 FF

洋红

00000011

80 80 00

暗黄

11111100

00 00 FF

00000100

00 00 80

暗蓝

11111011

FF FF 00

00000101

80 00 80

暗洋红

11111010

00 FF 00

绿

00000110

00 80 80

暗青

11111001

FF 00 00

00000111

C0 C0 C0

亮灰

11111000

80 80 80

暗灰

00001000

C0 DC C0

美元绿

11110111

A0 A0 A4

中性灰

00001001

A6 CA F0

天蓝

11110110

FF FB F0

乳白色

在256种颜色显示模式下执行时,由Windows维护系统调色板,此调色板与显示卡上的硬件调色板对照表相同.内定的系统调色板如表16-1所示.应用程序可以通过指定「逻辑调色板(logical palettes)」来修改其余236种颜色.如果有多个应用程序使用逻辑调色板,那么Windows就给活动窗口最高优先权(我们知道,活动窗口有高亮显示标题列,并且显示在其它所有窗口的前面).我们将用一些简单的范例程序来检查它是如何工作的.

要执行本章其它部分的程序,您可能需要将显示卡切换成256色模式.在桌面上单擎鼠标右键,从菜单中选择「属性」,然后选择「设定」页面标签.

显示灰阶

程序16-1所示的GRAYS1程序没有使用Windows调色板管理器,而尝试用正常显示的65级种阶作为从黑到白的多种彩色的「来源」.

程序16-1 GRAYS1
        
GRAYS1.ASM
/*---------------------------------------------------------------------------
;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD

.DATA
szAppName TCHAR "Grays1",0

.DATA?
hInstance HINSTANCE ?
cxClient DWORD ?
cyClient DWORD ?
.CODE
START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND

mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc

mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0

push hInst
pop wndclass.hInstance

invoke LoadIcon,NULL, IDI_APPLICATION
mov wndclass.hIcon,eax

invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax

invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX

lea eax,szAppName
mov wndclass.lpszMenuName,eax
mov wndclass.lpszClassName,eax

mov wndclass.hIconSm,0

invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif


invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
CTXT("Shades of Gray #1"),
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax

invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd

StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:

mov eax,msg.wParam
ret
WinMain endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL hBrush:HBRUSH
LOCAL hdc:HDC
LOCAL i:DWORD
LOCAL ps :PAINTSTRUCT
LOCAL rect:RECT

.if uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax

mov eax,lParam
shr eax,16
mov cyClient,eax

xor eax,eax
ret

.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax

;Draw the fountain of grays
xor eax,eax
mov i,eax
loopi:
mov eax,i
mov ecx,cxClient
mul ecx
mov ecx,65
div ecx
mov rect.left,eax

xor eax,eax
mov rect.top,eax

mov eax,i
inc eax
mov ecx,cxClient
mul ecx
mov ecx,65
div ecx
mov rect.right,eax

mov eax,cyClient
mov rect.bottom,eax

mov eax,i
shl eax,2
.if eax>255
mov eax,255
.endif
mov ebx,eax
shl ebx,8
add eax,ebx
shl ebx,8
add eax,ebx
invoke CreateSolidBrush,eax
mov hBrush,eax
invoke FillRect,hdc,addr rect,hBrush
invoke DeleteObject,hBrush
inc i
.if i<65
jmp loopi
.endif

invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_DESTROY
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif

invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START

程序运行结果

在WM_PAINT消息处理期间,程序呼叫了65次FillRect函数,每次都使用不同灰阶建立的画刷.灰阶值是RGB值(0,0,0)、(4,4,4)、(8,8,8)等等,直到最后一个值(255,255,255).最后一个值来自CreateSolidBrush函数中的min宏.

如果在256色显示模式下执行该程序,您将看到从黑到白的65种灰阶,而且它们几乎都用混色着色.纯颜色只有黑色、暗灰色(128,128,128)、亮灰色(192,192,192)和白色.其它颜色是混合了这些纯颜色的多位模式.如果我们在显示行或文字,而不是用这65种灰阶填充区域,Windows将不使用混色而只使用这四种纯色.如果我们正在显示位图,则图像将用20种标准Windows颜色近似.通常,Windows在位图中不使用混色.

程序16-2所示的GRAYS2程序用较少的外部程序代码验证了调色板管理器中最重要的函数和消息.

程序16-2 GRAYS2
        
GRAYS2.ASM
;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD

.DATA
szAppName TCHAR "Grays2",0

.DATA?
hInstance HINSTANCE ?
hPalette HPALETTE ?
cxClient DWORD ?
cyClient DWORD ?
.CODE
START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND

mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc

mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0

push hInst
pop wndclass.hInstance

invoke LoadIcon,NULL, IDI_APPLICATION
mov wndclass.hIcon,eax

invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax

invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX

lea eax,szAppName
mov wndclass.lpszMenuName,eax
mov wndclass.lpszClassName,eax

mov wndclass.hIconSm,0

invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif


invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
CTXT("Shades of Gray #2"),
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax

invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd

StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:

mov eax,msg.wParam
ret
WinMain endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL hBrush:HBRUSH
LOCAL hdc:HDC
LOCAL i:DWORD
LOCAL plp:DWORD ;指向LOGPALETTE结构体的指针
;LOGPALETTE STRUCT
; palVersion WORD ?
; palNumEntries WORD ?
; palPalEntry PALETTEENTRY <>
;LOGPALETTE ENDS

;PALETTEENTRY STRUCT
; peRed BYTE ?
; peGreen BYTE ?
; peBlue BYTE ?
; peFlags BYTE ?
;PALETTEENTRY ENDS
LOCAL ps :PAINTSTRUCT
LOCAL rect:RECT

.if uMsg == WM_CREATE
;Set up a LOGPALETTE structure and create a palette
mov eax,sizeof (PALETTEENTRY)
shl eax,6
add eax,sizeof (LOGPALETTE)
invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,eax ;plp = malloc (sizeof (LOGPALETTE) + 64 * sizeof (PALETTEENTRY)) ;
mov plp,eax
mov esi,plp
mov WORD ptr [esi],300h
mov WORD ptr [esi+2],65

xor eax,eax
mov i,eax
add esi,4
loopi1:
mov eax,i
shl eax,2
.if eax>255
mov eax,255
.endif
mov [esi],al
mov [esi+1],al
mov [esi+2],al
mov BYTE ptr [esi+3],0
add esi,4
inc i
.if i<65
jmp loopi1
.endif
invoke CreatePalette,plp
mov hPalette,eax
invoke LocalFree,plp
xor eax,eax
ret

.elseif uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax

mov eax,lParam
shr eax,16
mov cyClient,eax

xor eax,eax
ret

.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax

;Select and realize the palette in the device context
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc

;Draw the fountain of grays
xor eax,eax
mov i,eax
loopi2:
mov eax,i
mov ecx,cxClient
mul ecx
shr eax,6
mov rect.left,eax

xor eax,eax
mov rect.top,eax

mov eax,i
inc eax
mov ecx,cxClient
mul ecx
shr eax,6
mov rect.right,eax

mov eax,cyClient
mov rect.bottom,eax

mov eax,i
shl eax,2
.if eax>255
mov eax,255
.endif
mov ebx,eax
shl ebx,8
add eax,ebx
shl ebx,8
add eax,ebx
add eax,02000000h
invoke CreateSolidBrush,eax
mov hBrush,eax
invoke FillRect,hdc,addr rect,hBrush
invoke DeleteObject,hBrush
inc i
.if i<65
jmp loopi2
.endif

invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_QUERYNEWPALETTE
.if hPalette==0
mov eax,FALSE
ret
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke InvalidateRect,hwnd, NULL, TRUE
invoke ReleaseDC,hwnd,hdc

mov eax,TRUE
ret
.elseif uMsg == WM_PALETTECHANGED
mov eax,wParam
.if (hPalette==0)||(eax==hwnd)
jmp ByDefault
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke UpdateColors,hdc
invoke ReleaseDC,hwnd,hdc
.elseif uMsg == WM_DESTROY
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif
ByDefault:
invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START

运行结果同1

通常,使用调色板管理器的第一步就是呼叫CreatePalette函数来建立逻辑调色板.逻辑调色板包含程序所需要的全部颜色-即236种颜色.GRAYS1程序在WM_CREATE消息处理期间处理此作业.它初始化LOGPALETTE(「logical palette:逻辑调色板」)结构的字段,并将这个结构的指针传递给CreatePalette函数.CreatePalette传回逻辑调色板的句柄,并将此句柄储存在静态变量hPalette中.

LOGPALETTE结构定义如下:

typedef struct
        
{
        
           WORD         palVersion ;
        
           WORD         palNumEntries ;
        
           PALETTEENTRY palPalEntry[1] ;
        
}
        
LOGPALETTE, * PLOGPALETTE ;
        

第一个字段通常设为0x0300,表示兼容Windows 3.0.第二个字段设定为调色板表中的项目数.LOGPALETTE结构中的第三个字段是一个PALETTEENTRY结构的数组,此结构也是一个调色板项目.PALETTEENTRY结构定义如下:

typedef struct
        
{
        
           BYTE peRed ;
        
           BYTE peGreen ;
        
           BYTE peBlue ;
        
         BYTE peFlags ;
        
}
        
PALETTEENTRY, * PPALETTEENTRY ;
        

每个PALETTEENTRY结构都定义了一个我们要在调色板中使用的RGB颜色值.

注意,LOGPALETTE中只能定义一个PALETTEENTRY结构的数组.您需要为LOGPALETTE结构和附加的PALETTEENTRY结构配置足够大的内存空间.GRAYS2需要65种灰阶,因此它为LOGPALETTE结构和64个附加的PALETTEENTRY结构配置了足够大的内存空间.GRAYS2将palNumEntries字段设定为65,然后从0到64循环,计算灰阶等级(一般是循环索引的4倍,但不超过255),将结构中的peRed、peGreen和peBlue字段设定为此灰阶等级.peFlags字段设为0.程序将指向这个内存块的指针传递给CreatePalette,在一个静态变量中储存该调色板句柄,然后释放内存.

逻辑调色板是GDI对象.程序应该删除它们建立的所有逻辑调色板.WndProc透过在WM_DESTROY消息处理期间呼叫DeleteObject,仔细地删除了逻辑调色板.

注意逻辑调色板是独立的设备内容.在真正使用之前,必须确保将其选进设备内容.在WM_PAINT消息处理期间,SelectPalette将逻辑调色板选进设备内容.除了含有第三个参数以外,此函数与SelectObject函数相似.通常,第三个参数设为FALSE.如果SelectPalette的第三个参数设为TRUE,那么调色板将始终是「背景调色板」,这意味着当其它所有程序都显现了各自的调色板之后,该调色板才可以获得仍位于系统调色板中的一个未使用项目.

在任何时候都只有一个逻辑调色板能选进设备内容.函数将传回前一个选进设备内容的逻辑调色板句柄.如果您希望将此逻辑调色板重新选进设备内容,则可以储存此句柄.

通过将颜色映像到系统调色板,RealizePalette函数使Windows在设备内容中「显现」逻辑调色板,而系统调色板是与显示卡实际的实际调色板相对应.实际工作在此函数呼叫期间进行.Windows必须决定呼叫函数的窗口是活动的还是非活动的,并尽可能将系统调色板已改变通知给其它窗口(我们将简要说明一下通知的程序).

回忆一下GRAYS1,它用RGB宏来指定纯色画刷的颜色.RGB宏建构一个32位长整数(记作COLORREF值),其中高字节是0,3个低字节是红、绿和蓝的亮度.

使用Windows调色板管理器的程序可以继续使用RGB颜色值来指定颜色.不过,这些RGB颜色值将不能存取逻辑调色板中的附加颜色.它们的作用与没有使用调色板管理器相同.要在逻辑调色板中使用附加的颜色,就要用到PALETTERGB宏.除了COLORREF值的高字节设为2而不是0以外,「调色板RGB」颜色与RGB颜色很相似.

下面是重要的规则:

例如,在GRAYS2中处理WM_PAINT期间,当您选择并显现了逻辑调色板之后,如果试图显示红色,则将显示灰阶.您必须用RGB颜色值来选择不在逻辑调色板中的颜色.

注意,GRAYS2从不检查视讯显示驱动程序是否支持调色板管理程序.在不支持调色板管理程序的显示模式(即所有非256种颜色的显示模式)下执行GRAYS2时,GRAYS2的功能与GRASY1相同.

调色板信息

如果程序在逻辑调色板中指定一种颜色,该颜色又是20种保留颜色之一,那么Windows将把逻辑调色板项目映像给该颜色.另外,如果两个或多个应用程序都在它们的逻辑调色板中指定了同一种颜色,那么这些应用程序将共享系统调色板项目.程序可以通过将PALETTEENTRY结构的peFlags字段指定为常数PC_NOCOLLAPSE来忽略该内定状态(其余两个可能的标记是PC_EXPLICIT(用于显示系统调色板)和PC_RESERVED(用于调色板动画),我将在本章的后面展示这两个标记).

要帮助组织系统调色板,Windows调色板管理器含有两个发送给主窗口的消息.

第一个是QM_QUERYNEWPALETTE.当主窗口活动时,该消息发送给主窗口.如果程序在您的窗口上绘画时使用了调色板管理器,则它必须处理该消息.GRAYS2展示具体的作法.程序获得设备内容句柄,并选进调色板,呼叫RealizePalette,然后使窗口失效以产生WM_PAINT消息.如果显现了逻辑调色板,则窗口消息处理程序从该消息传回TRUE,否则传回FALSE.

当系统调色板改成与WM_QUERYNEWPALETTE消息的结果相同时,Windows将WM_PALETTECHANGED消息发送给由目前活动的窗口来启动并终止处理窗口链的所有主窗口.这允许前台窗口有优先权.传递给窗口消息处理程序的wParam值是活动窗口的句柄.只有当wParam不等于程序的窗口句柄时,使用调色板管理器的程序才会处理该消息.

通常,在处理WM_PALETTECHANGED时,使用自订调色板的任何程序都呼叫SelectPalette和RealizePalette.后续的窗口在消息处理期间呼叫RealizePalette时,Windows首先检查逻辑调色板中的RGB颜色是否与已加载到系统调色板中的RGB颜色相匹配.如果两个程序需要相同的颜色,那么这两个程序就共同使用一个系统调色板项目.接下来,Windows检查未使用的系统调色板项目.如果都已使用,则逻辑调色板中的颜色从20种保留项目映像到最近的颜色.

如果不关心程序非活动时显示区域的外观,那么您不必处理WM_PALETTECHANGED消息.否则,您有两个选择.GRAYS2显示其中之一:在处理WM_QUERYNEWPALETTE消息时,它获得设备内容,选进调色板,然后呼叫RealizePalette.这时就可以在处理WM_QUERYNEWPALETTE时呼叫InvalidateRect了.相反地,GRAYS2呼叫UpdateColors.这个函数通常比重新绘制窗口更有效,同时它改变窗口中像素的值来帮助保护以前的颜色.

使用调色板管理器的许多程序都将让WM_QUERYNEWPALETTE和WM_PALETTECHANGED消息用GRAYS2所显示的方法来处理.

调色板索引方法

程序16-3所示的GRAYS3程序与GRAYS2非常相似,只是在处理WM_PAINT期间使用了呼叫PALETTEINDEX的宏,而不是PALETTERGB.

程序16-3 GRAYS3
        
GRAYS3.ASM
;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD

.DATA
szAppName TCHAR "Grays3",0

.DATA?
hInstance HINSTANCE ?
hPalette HPALETTE ?
cxClient DWORD ?
cyClient DWORD ?
.CODE
START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND

mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc

mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0

push hInst
pop wndclass.hInstance

invoke LoadIcon,NULL, IDI_APPLICATION
mov wndclass.hIcon,eax

invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax

invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX

lea eax,szAppName
mov wndclass.lpszMenuName,eax
mov wndclass.lpszClassName,eax

mov wndclass.hIconSm,0

invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif


invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
CTXT("Shades of Gray #3"),
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax

invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd

StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:

mov eax,msg.wParam
ret
WinMain endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL hBrush:HBRUSH
LOCAL hdc:HDC
LOCAL i:DWORD
LOCAL plp:DWORD ;指向LOGPALETTE结构体的指针
;LOGPALETTE STRUCT
; palVersion WORD ?
; palNumEntries WORD ?
; palPalEntry PALETTEENTRY <>
;LOGPALETTE ENDS

;PALETTEENTRY STRUCT
; peRed BYTE ?
; peGreen BYTE ?
; peBlue BYTE ?
; peFlags BYTE ?
;PALETTEENTRY ENDS
LOCAL ps :PAINTSTRUCT
LOCAL rect:RECT

.if uMsg == WM_CREATE
;Set up a LOGPALETTE structure and create a palette
mov eax,sizeof (LOGPALETTE)
shl eax,6
add eax,sizeof (LOGPALETTE)
invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,eax ;plp = malloc (sizeof (LOGPALETTE) + 64 * sizeof (PALETTEENTRY)) ;
mov plp,eax
mov esi,plp
mov WORD ptr [esi],300h
mov WORD ptr [esi+2],65

xor eax,eax
mov i,eax
add esi,4
loopi1:
mov eax,i
shl eax,2
.if eax>255
mov eax,255
.endif
mov [esi],al
mov [esi+1],al
mov [esi+2],al
mov BYTE ptr [esi+3],0
add esi,4
inc i
.if i<65
jmp loopi1
.endif
invoke CreatePalette,plp
mov hPalette,eax
invoke LocalFree,plp
xor eax,eax
ret

.elseif uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax

mov eax,lParam
shr eax,16
mov cyClient,eax

xor eax,eax
ret

.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax

;Select and realize the palette in the device context
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc

;Draw the fountain of grays
xor eax,eax
mov i,eax
loopi2:
mov eax,i
mov ecx,cxClient
mul ecx
mov ecx,65
shr eax,6
mov rect.left,eax

xor eax,eax
mov rect.top,eax

mov eax,i
inc eax
mov ecx,cxClient
mul ecx
shr eax,6
mov rect.right,eax

mov eax,cyClient
mov rect.bottom,eax

;#define PALETTEINDEX(i) ((COLORREF)(0x01000000 | (DWORD)(WORD)(i)))
mov eax,i
add eax,01000000h
invoke CreateSolidBrush,eax
mov hBrush,eax
invoke FillRect,hdc,addr rect,hBrush
invoke DeleteObject,hBrush
inc i
.if i<65
jmp loopi2
.endif

invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_QUERYNEWPALETTE
.if hPalette==0
mov eax,FALSE
ret
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke InvalidateRect,hwnd, NULL, TRUE
invoke ReleaseDC,hwnd,hdc

mov eax,TRUE
ret
.elseif uMsg == WM_PALETTECHANGED
mov eax,wParam
.if (hPalette==0)||(eax==hwnd)
jmp ByDefault
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke UpdateColors,hdc
invoke ReleaseDC,hwnd,hdc
.elseif uMsg == WM_DESTROY
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif
ByDefault:
invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START

运行结果同1        

「调色板」索引的颜色不同于调色板RGB颜色,其高字节是1,而低字节的值是目前在设备内容中选择的、逻辑调色板中的索引.在GRAYS3中,逻辑调色板有65个项目,用于这些项目的索引从0到64.值

PALETTEINDEX (0)
        

指黑色,

PALETTEINDEX (32)
        

指灰色,而

PALETTEINDEX (64)
        

指白色.

因为Windows不需要执行最近颜色的搜索,所以使用调色板索引比使用RGB值更有效.

查询调色板支持

您可以容易地验证:当Windows在16位或24位显示模式下执行时,GRAYS2和GRAYS3程序执行良好.但是在某些情况下,要使用调色板管理器的Windows应用程序可能要先确定设备驱动程序是否支持它.这时,您可以呼叫GetDeviceCaps,并以视讯显示的设备内容句柄和PASTERCAPS作为参数.函数将传回由一系列标志位组成的整数.通过在传回值和常数RC_PALETTE之间执行位操作来检验支持的调色板:

RC_PALETTE & GetDeviceCaps (hdc, RASTERCAPS)
        

如果此值非零,则视讯显示器设备驱动程序将支持调色板操作.在这种情况之下,来自GetDeviceCaps的其它三个重要项目也是可用的.函数呼叫

GetDeviceCaps (hdc, SIZEPALETTE)
        

将传回在显示卡上调色板表的总尺寸.这与同时显示的颜色总数相同.因为调色板管理器只用于每像素8位的视讯显示模式,所以此值将是256.

函数呼叫

GetDeviceCaps (hdc, NUMRESERVED)
        

传回在调色板表中的颜色数,该表是设备驱动程序为系统保留的,此值是20.不呼叫调色板管理器,这些只是Windows应用程序在256色显示模式下使用的纯色.要使用其余的236种颜色,程序必须使用调色板管理器函数.

一个附加项目也可用:

GetDeviceCaps (hdc, COLORRES)
        

此值告诉您加载到硬件调色板表的RGB颜色值分辨率(以位计).这些是进入数字模拟转换器的位.某些视讯显示卡只使用6位ADC,所以该值是18.其余使用8位的ADC,所以值是24.

Windows程序注意颜色分辨率并因此采取一些动作是很有用的.例如,如果该颜色分辨率是18,那么程序将不可能要求到128种灰阶,因为只有64个离散的灰阶可用.要求到128种灰阶就不必用多余的项目来填充硬件调色板表.

系统调色板

我在前面提过,Windows系统调色板直接与显示卡上的硬件调色板查询表相符(然而,硬件调色板查询表可能比系统调色板的颜色分辨率低).程序可以通过呼叫下面的函数来获得系统调色板中的某些或全部的RGB项目:

GetSystemPaletteEntries (hdc, uStart, uNum, &pe) ;
        

只有显示卡模式支持调色板操作时,该函数才能执行.第二个和第三个参数是无正负号整数,显示第一个调色板项目的索引和调色板项目数.最后一个参数是指向PALETTEENTRY型态的指针.

您可以在几种情况下使用该函数.程序可以定义PALETTEENTRY结构如下:

PALETTEENTRY pe ;
        

然后可按下面的方法多次呼叫GetSystemPaletteEntries:

GetSystemPaletteEntries (hdc, i, 1, &pe) ;
        

其中的i从0到某个值,该值小于从GetDeviceCaps(带有SIZEPALETTE索引255)传回的值.或者,程序要获得所有的系统调色板项目,可以通过定义指向PALETTEENTRY结构的指针,然后重新配置足够的内存块,以储存与调色板大小指定同样多的PALETTEENTRY结构.

GetSystemPaletteEntries函数确实允许您检验硬件调色板表.系统调色板中的项目按像素值增加的顺序排列,这些值用于表示视讯显示缓冲区中的颜色.我将简单地讨论一下具体作法.

其它调色板函数

我们在前面看过,Windows程序能够改变系统调色板,但只是间接改变:第一步建立逻辑调色板,它基本上是程序要使用的RGB颜色值数组.CreatePalette函数不会导致系统调色板或者显示卡调色板表的任何变化.逻辑调色板必须在任何事情发生之前就选进设备内容并显现.

程序可以通过呼叫

GetPaletteEntries (hPalette, uStart, uNum, &pe) ;
        

来查询逻辑调色板中的RGB颜色值.您可以按使用GetSystemPaletteEntries的方法来使用此函数.但是要注意,第一个参数是逻辑调色板的句柄,而不是设备内容的句柄.

建立逻辑调色板以后,让您改变其中的值的相应函数是:

SetPaletteEntries (hPalette, uStart, uNum, &pe) ;
        

另外,记住呼叫此函数不引起系统调色板的任何变化-即使目前调色板选进了设备内容.此函数也不改变逻辑调色板的尺寸.要改变逻辑调色板的尺寸,请使用ResizePalette.

下面的函数接受RGB颜色引用值作为最后的参数,并将索引传回给逻辑调色板,该逻辑调色板与和它最接近的RGB颜色值相对应:

iIndex = GetNearestPaletteIndex (hPalette, cr) ;
        

第二个参数是COLORREF值.如果希望的话,呼叫GetPaletteEntries就可以获得逻辑调色板中实际的RGB颜色值.

如果程序在8位显示模式下需要多于236种自订颜色,则可以呼叫GetSystemPaletteUse.这允许程序设定254种自订颜色;系统仅保留黑色和白色.不过,程序仅在最大化充满全屏幕时才允许这样,而且它还将一些系统颜色设为黑色和白色,以便标题列和菜单等仍然可见.

位映像操作问题

从前面的几期介绍中可以了解到,GDI允许使用不同的「绘画模式」或「位映像操作」来画线并填充区域.用SetROP2设定绘画模式,其中的「2」表示两个对象之间的二元(binary)位映像操作.三元位映像操作用于处理BitBlt和类似功能.这些位映像操作决定了正在画的对象像素与表面像素的结合方式.例如,您可以画一条直线,以便在线的像素与显示的像素按位异或的方式相结合.

位映像操作就是在像素位上照着各个位的顺序进行操作.改变调色板会影响到这些位映像操作.位映像操作的操作对象是像素位,而这些像素位可能与实际颜色没有关联.

透过执行GRAYS2或GRAYS3程序,您自己就可以得出这个结论.调整尺寸时,拖动顶部或底部的边界穿过窗口,Windows利用反转背景像素位的位映像操作来显示拖动尺寸的边界,其目的是使拖动尺寸边界总是可见的.但在GRAYS2和GRAYS3程序中,您将看到各种随机变换的颜色,这些颜色恰好与对应于调色板表中未使用的项目,那是反转显示像素位的结果.可视颜色没有反转-只有像素位反转了.

正如您在表16-1中所看到的一样,20种标准保留颜色位于系统调色板的顶部和底部,以便位映像操作的结果仍然正常.然而,一旦您开始修改调色板-尤其是替换了保留颜色-那么颜色对象的位映像操作就变得没有意义了.

唯一保证的是位映像操作将用黑色和白色运作.黑色是系统调色板中的第一个项目(所有的像素位都设为0),而白色是最后的项目(所有的像素位都设为1).这两个项目不能改变.如果需要预知在颜色对象上进行位映像操作的结果,则可以先获得系统调色板表,然后查看不同像素位值的RGB颜色值.

查看系统调色板

在Windows下执行的程序将处理逻辑调色板,为使逻辑调色板更好地服务于所有使用逻辑调色板的程序,Windows将在系统调色板中设定颜色.该系统调色板复制了显示卡的硬件对照表内容.这样,查看系统调色板有助于调适调色盘应用程序.

因为对于这个问题有三种截然不同的处理方式,所以我将向您展示三个程序,以显示系统调色板的内容.

SYSPAL1程序,如程序16-4所示,使用了前面所讲的GetSystemPaletteEntries函数.

程序16-4 SYSPAL1
        
SYSPAL1.ASM
       
;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD

.DATA
szAppName TCHAR "SYSPAL1",0

.DATA?
hInstance HINSTANCE ?
cxClient DWORD ?
cyClient DWORD ?
;在C语言中,SIZE定义如下
;typedef SIZE SIZEL;
;typedef struct tagSIZEL
; {
; LONG cx;
; LONG cy;
; } SIZEL;
sizeChar SIZEL <?>
.CODE
START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND

mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc

mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0

push hInst
pop wndclass.hInstance

invoke LoadIcon,NULL, IDI_APPLICATION
mov wndclass.hIcon,eax

invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax

invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX

lea eax,szAppName
mov wndclass.lpszMenuName,eax
mov wndclass.lpszClassName,eax

mov wndclass.hIconSm,0

invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif


invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
CTXT("System Palette #1"),
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax

invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd

StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:

mov eax,msg.wParam
ret
WinMain endp

CheckDisplay proc hwnd:HWND
LOCAL hdc:HDC
LOCAL iPalSize:DWORD

invoke GetDC,hwnd
mov hdc,eax

invoke GetDeviceCaps,hdc, SIZEPALETTE
mov iPalSize,eax

invoke ReleaseDC,hwnd, hdc

.if (iPalSize != 256)
invoke MessageBox,hwnd,CTEXT ("This program requires that the video display mode have a 256-color palette."),
addr szAppName, MB_ICONERROR
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
CheckDisplay Endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL hdc:HDC
LOCAL hPalette:HPALETTE
LOCAL i,x,y:DWORD
LOCAL ps :PAINTSTRUCT
LOCAL pe[256]:PALETTEENTRY
LOCAL szBuffer[16]:TCHAR

.if uMsg == WM_CREATE
invoke CheckDisplay,hwnd
.if eax==0
mov eax,-1
ret
.endif
invoke GetDC,hwnd
mov hdc,eax

invoke GetStockObject,SYSTEM_FIXED_FONT
invoke SelectObject,hdc,eax
invoke GetTextExtentPoint32,hdc, CTEXT ("FF-FF-FF"), 10,addr sizeChar

invoke ReleaseDC,hwnd, hdc

xor eax,eax
ret
.elseif uMsg == WM_DISPLAYCHANGE
invoke CheckDisplay,hwnd
.if eax!=0
invoke DestroyWindow,hwnd
.endif
xor eax,eax
ret
.elseif uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax

mov eax,lParam
shr eax,16
mov cyClient,eax

xor eax,eax
ret

.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
invoke GetStockObject,SYSTEM_FIXED_FONT
invoke SelectObject,hdc,eax
invoke GetSystemPaletteEntries,hdc, 0, 256,addr pe

;Draw the fountain of grays
xor eax,eax
mov i,eax
mov x,eax
mov y,eax
loopi:
lea esi,pe
mov eax,i
shl eax,2
add esi,eax

mov eax,[esi]
shr eax,8
mov ebx,eax
and ebx,0FFh ;pe[i].peBlue
shr eax,8
mov ecx,eax
and ecx,0FFh ;pe[i].peGreen
shr eax,8
mov edx,eax
and edx,0FFh ;pe[i].peGreen

invoke wsprintf,addr szBuffer, CTEXT ("%02X-%02X-%02X"),edx, ecx, ebx
invoke lstrlen,addr szBuffer
mov ebx,eax
invoke TextOut,hdc, x, y,addr szBuffer, ebx

mov eax,x
add eax,sizeChar.x
mov x,eax
add eax,sizeChar.x
.if (eax> cxClient)
mov x,0
mov eax,y
add eax,sizeChar.y
mov y,eax
.if (eax > cyClient)
jmp BreakLoop
.endif

.endif
inc i
.if i<65
jmp loopi
.endif
BreakLoop:
invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_PALETTECHANGED
invoke InvalidateRect,hwnd,NULL,FALSE
xor eax,eax
ret
.elseif uMsg == WM_DESTROY
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif

invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START

运行结果(注意:必须在256色下才能运行.如果不是,请调整显示属性)

与SYSPAL系列中的其它程序一样,除非带有SIZEPALETTE参数的GetDeviceCaps传回值为256,否则SYSPAL1不会执行.

注意无论SYSPAL1的显示区域什么时候收到WM_PALETTECHANGED消息,它都是无效的.在合并WM_PAINT消息处理期间,SYSPAL1呼叫GetSystemPaletteEntries,并用一个含256个PALETTEENTRY结构的数组作为参数.RGB值作为文字字符串显示在显示区域.程序执行时,注意20种保留颜色是RGB值列表中的前10个和后10个,这与表16-1所示相同.

当SYSPAL1显示有用的信息时,它与实际看到的256种颜色不同.那就是SYSPAL2的作业,如程序16-5所示.

程序16-5 SYSPAL2
        
SYSPAL2.ASM

;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD

.DATA
szAppName TCHAR "SysPal2",0

.DATA?
hInstance HINSTANCE ?
hPalette HPALETTE ?
cxClient DWORD ?
cyClient DWORD ?
;在C语言中,SIZE定义如下
;typedef SIZE SIZEL;
;typedef struct tagSIZEL
; {
; LONG cx;
; LONG cy;
; } SIZEL;
sizeChar SIZEL <?>
.CODE
START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND

mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc

mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0

push hInst
pop wndclass.hInstance

invoke LoadIcon,NULL, IDI_APPLICATION
mov wndclass.hIcon,eax

invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax

invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX

lea eax,szAppName
mov wndclass.lpszMenuName,eax
mov wndclass.lpszClassName,eax

mov wndclass.hIconSm,0

invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif


invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
CTXT("System Palette #2"),
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax

invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd

StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:

mov eax,msg.wParam
ret
WinMain endp

CheckDisplay proc hwnd:HWND
LOCAL hdc:HDC
LOCAL iPalSize:DWORD

invoke GetDC,hwnd
mov hdc,eax

invoke GetDeviceCaps,hdc, SIZEPALETTE
mov iPalSize,eax

invoke ReleaseDC,hwnd, hdc

.if (iPalSize != 256)
invoke MessageBox,hwnd,CTEXT ("This program requires that the video display mode have a 256-color palette."),
addr szAppName, MB_ICONERROR
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
CheckDisplay Endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL hBrush:HBRUSH
LOCAL hdc:HDC
LOCAL i,x,y:DWORD
LOCAL plp:DWORD ;指向LOGPALETTE结构体的指针
;LOGPALETTE STRUCT
; palVersion WORD ?
; palNumEntries WORD ?
; palPalEntry PALETTEENTRY <>
;LOGPALETTE ENDS
LOCAL ps :PAINTSTRUCT
LOCAL rect:RECT

.if uMsg == WM_CREATE
invoke CheckDisplay,hwnd
.if eax==0
mov eax,-1
ret
.endif
mov eax,sizeof PALETTEENTRY
mov ecx,255
mul ecx
add eax,sizeof LOGPALETTE
invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,eax
mov plp,eax

mov esi,plp
mov WORD ptr [esi],300h
mov WORD ptr [esi+2],256

xor eax,eax
mov i,eax
add esi,4
Loopi:
mov eax,i
mov [esi],al
xor al,al
mov [esi+1],al
mov [esi+2],al
mov BYTE ptr [esi+3],PC_EXPLICIT
add esi,4

inc i
cmp i,256
jNz Loopi

invoke CreatePalette,plp
mov hPalette,eax

invoke LocalFree,plp

xor eax,eax
ret
.elseif uMsg == WM_DISPLAYCHANGE
invoke CheckDisplay,hwnd
.if eax!=0
invoke DestroyWindow,hwnd
.endif
xor eax,eax
ret
.elseif uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax

mov eax,lParam
shr eax,16
mov cyClient,eax

xor eax,eax
ret
.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc

mov y,0
LoopY:
mov x,0
LoopX:
;#define PALETTEINDEX(i) ((COLORREF)(0x01000000 | (DWORD)(WORD)(i)))
mov eax,y
shl eax,4
add eax,x
add eax,01000000h
invoke CreateSolidBrush,eax
mov hBrush,eax

mov eax,y ;(y+1) * cyClient / 16
inc eax
mov ecx,cyClient
mul ecx
shr eax,4
push eax

mov eax,x ; (x + 1) * cxClient / 16
inc eax
mov ecx,cxClient
mul ecx
shr eax,4
push eax

mov eax,y ;y * cyClient / 16
mov ecx,cyClient
mul ecx
shr eax,4
push eax

mov eax,x ;x * cxClient / 16
mov ecx,cxClient
mul ecx
shr eax,4
push eax

lea eax,rect
push eax
call SetRect

invoke FillRect,hdc,addr rect, hBrush
invoke DeleteObject,hBrush

inc x
cmp x,16
jNz LoopX
inc y
cmp y,16
jNz LoopY

invoke EndPaint,hwnd,addr ps
xor eax,eax
ret

.elseif uMsg == WM_PALETTECHANGED
mov eax,wParam
.if eax!=hwnd
invoke InvalidateRect,hwnd,NULL,FALSE
.endif
xor eax,eax
ret
.elseif uMsg == WM_DESTROY
invoke DeleteObject,hPalette
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif

invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START

程序运行结果(必须在256色下)


SYSPAL2在WM_CREATE消息处理期间建立了逻辑调色板.但是请注意:逻辑调色板中所有的256个值都是从0到255的调色板索引,并且peFlags字段是PC_EXPLICIT.该标志位是这样定义的:「逻辑调色板项目的较低字组指定了一个硬件调色板索引.此标志位允许应用程序显示硬件调色板的内容.」该标志位就是专为我们要做的这件事情而设计的.

在WM_PAINT消息处理期间,SYSPAL2将该调色板选进设备内容并显现它.这不会引起系统调色板的任何重组,而是允许程序使用PALETTEINDEX宏来指定系统调色板中的颜色.按此方法,SYSPAL2显示了256个矩形.另外,当您执行该程序时,注意顶行和底行的前10种和后10种颜色是20种保留颜色,如表16-1所示.当您执行使用自己逻辑调色板的程序时,显示就改变了.

事实上,所有的GDI函数都直接或间接地指定颜色作为RGB值.在GDI内部,这将转换成与那个颜色相关的像素位.在某些显示模式中(例如,16位或24位颜色模式),这些转换是相当直接的.在其它显示模式中(4位或8位颜色),这可能涉及最接近颜色的搜索.

然而,有两个GDI函数让您直接指定像素位中的颜色.当然在这种方式中使用的这两个函数都与设备高度相关.它们太依赖设备了,以至于它们可以直接显示视讯显示卡上实际的调色板对照表.这两个函数是BitBlt和StretchBlt.

程序16-6所示的SYSPAL3程序显示了使用StretchBlt显示系统调色板中颜色的方法.

程序16-6 SYSPAL3
        
SYSPAL3.ASM
;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD

.DATA
szAppName TCHAR "SysPal3",0

.DATA?
hInstance HINSTANCE ?
hBitmap HBITMAP ?
cxClient DWORD ?
cyClient DWORD ?
.CODE
START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND

mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc

mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0

push hInst
pop wndclass.hInstance

invoke LoadIcon,NULL, IDI_APPLICATION
mov wndclass.hIcon,eax

invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax

invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX

lea eax,szAppName
mov wndclass.lpszMenuName,eax
mov wndclass.lpszClassName,eax

mov wndclass.hIconSm,0

invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif


invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
CTXT("System Palette #3"),
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax

invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd

StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:

mov eax,msg.wParam
ret
WinMain endp

CheckDisplay proc hwnd:HWND
LOCAL hdc:HDC
LOCAL iPalSize:DWORD

invoke GetDC,hwnd
mov hdc,eax

invoke GetDeviceCaps,hdc, SIZEPALETTE
mov iPalSize,eax

invoke ReleaseDC,hwnd, hdc

.if (iPalSize != 256)
invoke MessageBox,hwnd,CTEXT ("This program requires that the video display mode have a 256-color palette."),
addr szAppName, MB_ICONERROR
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
CheckDisplay Endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL bits[256]:BYTE
LOCAL hdc,hdcMem:HDC
LOCAL i:DWORD
LOCAL ps :PAINTSTRUCT

.if uMsg == WM_CREATE
invoke CheckDisplay,hwnd
.if eax==0
mov eax,-1
ret
.endif

xor eax,eax
mov i,eax
lea esi,bits
Loopi:
mov eax,i
mov [esi],al
inc esi

inc i
cmp i,256
jNz Loopi

invoke CreateBitmap,16,16,1,8,addr bits
mov hBitmap,eax

xor eax,eax
ret
.elseif uMsg == WM_DISPLAYCHANGE
invoke CheckDisplay,hwnd
.if eax!=0
invoke DestroyWindow,hwnd
.endif
xor eax,eax
ret
.elseif uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax

mov eax,lParam
shr eax,16
mov cyClient,eax

xor eax,eax
ret
.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
invoke CreateCompatibleDC,hdc
mov hdcMem,eax
invoke SelectObject,hdcMem,hBitmap
invoke StretchBlt,hdc,0,0, cxClient, cyClient,hdcMem, 0, 0, 16, 16, SRCCOPY
invoke DeleteDC,hdcMem

invoke EndPaint,hwnd,addr ps
xor eax,eax
ret

.elseif uMsg == WM_DESTROY
invoke DeleteObject,hBitmap
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif

invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START


在WM_CREATE消息处理期间,SYSPAL3使用CreateBitmap来建立16×16的每像素8位的位图.该函数的最后一个参数是包括数值0到255的256字节数组.这些是256种可能的像素位值.
在处理WM_PAINT消息的程序中,程序将这个位图选进内存设备内容,用StretchBlt来显示并填充该显示区域.Windows仅将位图中的图素位传输到视讯显示器硬件,从而允许这些图素位存取调色板对照表中的256个项目.
程序的显示区域甚至不必使接收WM_PALETTECHANGED消息无效-对于对照表的任何修改都会立即影响到SYSPAL3的显示.

调色板动画

在本节的标题中看到「动画」一词,并开始考虑屏幕周围执行的「计算机宠物」时,您的眼前可能会为之一亮.是的,您可以使用Windows调色板管理器作一些动画,而且是有一定专业水平的动画.

通常,Windows下的动画就是快速连续地显示一系列位图.调色板动画与这种方法有很大的区别.您透过在屏幕上绘制您所需要的每件东西开始,然后您处理调色板来改变这些对象的颜色,可能是画一些相对于屏幕背景来说是不可见的图像.您用这种方法就可以获得动画效果,而不必重画任何东西.调色板动画的速度是相当快的.

对于调色板动画,最初的建立工作与我们前面看见的有些不同:对于动画期间要修改的每种RGB颜色值,PALETTEENTRY结构的peFlags字段必须设定为PC_RESERVED.

通常,就像我们所看到的一样,在建立逻辑调色板时,您将peFlags标记设为0.这允许GDI将多个逻辑调色板中同样的颜色映像到相同的系统调色板项目.例如,假设两个Windows程序都建立了包含RGB项目10-10-10的逻辑调色板,那么在系统调色板表中,Windows只需要一个10-10-10项目.但如果这两个程序中的一个使用调色板动画,那您就不要再让GDI使用调色板了.调色板动画意味着速度非常快-而且如果不重画,它也只可能提高速度.当使用调色板动画的程序修改调色板时,它不会影响其它程序,或者迫使GDI重组系统调色板表.PC_RESERVED的peFlags值为单个逻辑调色板储存系统调色板项目.

使用调色板动画时,通常您可以在WM_PAINT消息处理期间呼叫SelectPalette和RealizePalette,使用PALETTEINDEX宏来指定颜色.该宏将一个索引带进逻辑调色板表.

对于动画,您可能要通过改变调色板来响应WM_TIMER消息.要改变逻辑调色板中的RGB颜色值,请使用一个PALETTEENTRY结构的数组来呼叫函数AnimatePalette.此函数速度很快,因为它只需要改变系统调色板以及显示卡硬件调色板表中的项目.

跳动的球

程序16-7显示了BOUNCE程序的组件,但还有一个程序可显示跳动的球.为了简单起见,根据显示区域的大小将球画成了椭圆形.因为本章有几个调色板动画程序,所以PALANIM.C(「调色板动画」)文件包含一些通用内容.

程序16-7 BOUNCE
         
BOUNCE.ASM

;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
ID_TIMER equ 1
.DATA
szAppName TCHAR "Bounce",0
szTitle TCHAR "Bounce: Palette Animation Demo",0
bLeftToRight BOOL TRUE
.DATA?
hInstance HINSTANCE ?
hPalette HPALETTE ?
cxClient DWORD ?
cyClient DWORD ?
plp DWORD ?
;指向LOGPALETTE结构体的指针
;LOGPALETTE STRUCT
; palVersion WORD ?
; palNumEntries WORD ?
; palPalEntry PALETTEENTRY <>
;LOGPALETTE ENDS

;PALETTEENTRY STRUCT
; peRed BYTE ?
; peGreen BYTE ?
; peBlue BYTE ?
; peFlags BYTE ?
;PALETTEENTRY ENDS
iBall DWORD ?
szBuffer db 100 dup(?)
.CODE

CreateRoutine proc hwnd:HWND
LOCAL hPaletteCR:HPALETTE
LOCAL i:DWORD

mov eax,sizeof (PALETTEENTRY)
mov ecx,33
mul ecx
add eax,sizeof (LOGPALETTE)
invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,eax ;plp = malloc (sizeof (LOGPALETTE) + 64 * sizeof (PALETTEENTRY)) ;
mov plp,eax
mov esi,plp
mov WORD ptr [esi],300h
mov WORD ptr [esi+2],34

xor eax,eax
mov i,eax
add esi,4
loopi1:
mov BYTE PTR [esi],255
.if i==0
mov al,0
.elseif
mov al,255
.endif
mov [esi+1],al
mov [esi+2],al
.if i==33
mov al,0
.elseif
mov al,PC_RESERVED
.endif
mov BYTE ptr [esi+3],al
add esi,4
inc i
.if i<34
jmp loopi1
.endif
invoke CreatePalette,plp
mov hPaletteCR,eax
invoke SetTimer,hwnd, ID_TIMER, 50, NULL
mov eax,hPaletteCR
ret
CreateRoutine endp

PaintRoutine proc hdc:HDC,cxClientPR:DWORD,cyClientPR:DWORD
LOCAL hBrush:HBRUSH
LOCAL i, x1, x2, y1, y2:DWORD
LOCAL rect:RECT

; Draw window background using palette index 33
invoke SetRect,addr rect, 0, 0, cxClientPR, cyClientPR
;#define PALETTEINDEX(i) ((COLORREF)(0x01000000 | (DWORD)(WORD)(i)))
mov eax,01000021h
invoke CreateSolidBrush,eax
mov hBrush,eax

;mov ebx,eax
;invoke wsprintf,addr szBuffer,CTXT("%x"),ebx
;invoke MessageBox,NULL,addr szBuffer,NULL,MB_APPLMODAL


invoke FillRect,hdc,addr rect, hBrush
invoke DeleteObject,hBrush

;Draw the 33 balls
invoke GetStockObject,NULL_PEN
invoke SelectObject,hdc,eax

xor eax,eax
mov i,eax
loopi2:
mov eax,i ;x1 = i * cxClientPR / 33
mov ecx,cxClientPR
mul ecx
xor edx,edx
mov ecx,33
div ecx
mov x1,eax

mov eax,i ;x2 = (i + 1)* cxClientPR / 33
inc eax
mov ecx,cxClientPR
mul ecx
xor edx,edx
mov ecx,33
div ecx
mov x2,eax

.if (i < 9)
mov eax,i ;y1 = i * cyClientPR / 9
mov ecx,cyClientPR
mul ecx
xor edx,edx
mov ecx,9
div ecx
mov y1,eax

mov eax,i ;y2 = (i + 1) * cyClientPR / 9
inc eax
mov ecx,cyClientPR
mul ecx
xor edx,edx
mov ecx,9
div ecx
mov y2,eax
.elseif (i < 17)
mov eax,16
sub eax,i ;y1 = (16 - i) * cyClientPR / 9
mov ecx,cyClientPR
mul ecx
xor edx,edx
mov ecx,9
div ecx
mov y1,eax

mov eax,17
sub eax,i ;y2 = (17 - i) * cyClientPR / 9
mov ecx,cyClientPR
mul ecx
xor edx,edx
mov ecx,9
div ecx
mov y2,eax
.elseif (i < 25)
mov eax,i
sub eax,16 ;y1 = (i - 16) * cyClientPR / 9
mov ecx,cyClientPR
mul ecx
xor edx,edx
mov ecx,9
div ecx
mov y1,eax

mov eax,i
sub eax,15 ;y2 = (i - 15) * cyClientPR / 9
mov ecx,cyClientPR
mul ecx
xor edx,edx
mov ecx,9
div ecx
mov y2,eax
.else
mov eax,32
sub eax,i ;y1 = (32 - i) * cyClientPR / 9
mov ecx,cyClientPR
mul ecx
xor edx,edx
mov ecx,9
div ecx
mov y1,eax

mov eax,33
sub eax,i ;y2 = (33 - i) * cyClientPR / 9
mov ecx,cyClientPR
mul ecx
xor edx,edx
mov ecx,9
div ecx
mov y2,eax
.endif
mov eax,i
add eax,01000000h
invoke CreateSolidBrush,eax
mov hBrush,eax
invoke SelectObject,hdc, hBrush
invoke Ellipse,hdc, x1, y1, x2, y2
invoke GetStockObject,WHITE_BRUSH
invoke SelectObject,hdc,eax
invoke DeleteObject,eax

inc i
.if i<33
jNz loopi2
.endif

ret
PaintRoutine endp

TimerRoutine proc hdc:HDC,hPaletteTR:HPALETTE
; Set old ball to white
mov esi,plp
add esi,4
mov eax,iBall
shl eax,2
add esi,eax
;PALETTEENTRY STRUCT
; peRed BYTE ?
; peGreen BYTE ?
; peBlue BYTE ?
; peFlags BYTE ?
;PALETTEENTRY ENDS
mov BYTE PTR [esi+1],255
mov BYTE PTR [esi+2],255

.if bLeftToRight!=0 ; iBall += (bLeftToRight ? 1 : -1)
mov eax,1
.else
mov eax,-1
.endif
add iBall,eax

.if bLeftToRight!=0 ; iBall == (bLeftToRight ? 33 : -1)
mov eax,33
.else
mov eax,-1
.endif

.if iBall==eax
.if bLeftToRight!=0
mov eax,31
.else
mov eax,1
.endif
mov iBall,eax
mov eax,bLeftToRight
xor eax,TRUE
mov bLeftToRight,eax
.endif
; Set new ball to red
mov esi,plp
add esi,4
mov eax,iBall
shl eax,2
add esi,eax
;PALETTEENTRY STRUCT
; peRed BYTE ?
; peGreen BYTE ?
; peBlue BYTE ?
; peFlags BYTE ?
;PALETTEENTRY ENDS
mov BYTE PTR [esi+1],0
mov BYTE PTR [esi+2],0

; Animate the palette
mov esi,plp
add esi,4
invoke AnimatePalette,hPaletteTR, 0, 33, esi

ret
TimerRoutine endp

DestroyRoutine proc hwnd:HWND,hPaletteDR:HPALETTE
invoke KillTimer,hwnd, ID_TIMER
invoke DeleteObject,hPaletteDR
invoke LocalFree,plp
ret
DestroyRoutine endp


START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND

mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc

mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0

push hInst
pop wndclass.hInstance

invoke LoadIcon,NULL, IDI_APPLICATION
mov wndclass.hIcon,eax

invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax

invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX

lea eax,szAppName
mov wndclass.lpszMenuName,eax
lea eax,szAppName
mov wndclass.lpszClassName,eax

mov wndclass.hIconSm,0

invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif

invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
ADDR szTitle,
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax
.if hWnd==0
xor eax,eax
ret
.endif

invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd

StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:

mov eax,msg.wParam
ret
WinMain endp

CheckDisplay proc hwnd:HWND
LOCAL hdc:HDC
LOCAL iPalSize:DWORD

invoke GetDC,hwnd
mov hdc,eax

invoke GetDeviceCaps,hdc, SIZEPALETTE
mov iPalSize,eax

invoke ReleaseDC,hwnd, hdc

.if (iPalSize != 256)
invoke MessageBox,hwnd,CTEXT ("This program requires that the video display mode have a 256-color palette."),
addr szAppName, MB_ICONERROR
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
CheckDisplay Endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL hdc:HDC
LOCAL ps :PAINTSTRUCT

.if uMsg == WM_CREATE
invoke CheckDisplay,hwnd
.if eax==0
mov eax,-1
ret
.endif

invoke CreateRoutine,hwnd
mov hPalette,eax

xor eax,eax
ret
.elseif uMsg == WM_DISPLAYCHANGE
invoke CheckDisplay,hwnd
.if eax==0
invoke DestroyWindow,hwnd
.endif
xor eax,eax
ret
.elseif uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax

mov eax,lParam
shr eax,16
mov cyClient,eax

xor eax,eax
ret
.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
invoke SelectPalette,hdc,hPalette,FALSE
invoke RealizePalette,hdc
invoke PaintRoutine,hdc,cxClient,cyClient
invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_TIMER
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke TimerRoutine,hdc, hPalette
invoke ReleaseDC,hwnd, hdc

xor eax,eax
ret
.elseif uMsg == WM_QUERYNEWPALETTE
.if hPalette==0
mov eax,FALSE
ret
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke InvalidateRect,hwnd, NULL, TRUE
invoke ReleaseDC,hwnd, hdc
mov eax,TRUE
ret
.elseif uMsg == WM_PALETTECHANGED
mov eax,wParam
.if (hPalette==0)||(eax==hwnd)
jmp ByDefault
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke UpdateColors,hdc
invoke ReleaseDC,hwnd, hdc
jmp ByDefault
.elseif uMsg == WM_DESTROY
invoke DestroyRoutine,hwnd, hPalette
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif
ByDefault:
invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START

程序运行结果是一个在窗口中跳动的小球(注意只能在256色下运行)

 

除非Windows处于支持调色板的显示模式下,否则调色板动画将不能工作.因此,PALANIM.ASM通过呼叫CheckDisplay函数(与SYSPAL程序中的函数相同)来开始处理WM_CREATE.

PALANIM.AMS中重要的四个函数:在WM_CREATE消息处理期间呼叫CreateRoutine(在BOUNCE中用于建立逻辑调色板).在呼叫PaintRoutine和TimerRoutine之前,首先获得设备内容,并将其选进逻辑调色板.在呼叫PaintRoutine之前,它也显现调色板.期望TimerRoutine呼叫AnimatePalette.尽管AnimatePalette需要从设备内容中选择调色板,但它不需要呼叫RealizePalette.

BOUNCE中的球按「W」路线在显示区域中来回跳动.显示区域背景是白色,球是红色.任何时候,都可以在33个不重迭的位置之一看见球.这需要34个调色板项目:一个用于背景,其它33个用于不同位置的球.在CreateRoutine中,BOUNCE初始化PALETTEENTRY结构的一个数组,将第一个调色板项目(与球在左上角的位置对应)设定为红色,其它的设定为白色.注意,对于除背景以外的所有项目,peFlags字段都设定为PC_RESERVED(背景是最后的一个调色板项目).BOUNCE通过将Windows定时器的间隔设定为50毫秒来终止CreateRoutine.

BOUNCE在PaintRoutine完成所有的绘画工作.窗口背景用一个实心画刷和调色板索引33所指定的颜色来绘制.33个球的颜色是依据从0到32的调色板索引的颜色.当BOUNCE第一次在显示区域内绘画时,0的调色板索引映像成红色,其它调色板索引映像到白色.这导致球出现在左上角.

当WndProc处理WM_TIMER消息并呼叫TimerRoutine时,动画就发生了.TimerRoutine通过呼叫AnimatePalette来结束,语法如下:

AnimatePalette (hPalette, uStart, uNum, &pe) ;
        

其中,第一个参数是调色板句柄,最后一个参数是指向数组的指针,该数组由一个或多个PALETTEENTRY结构组成.该函数改变逻辑调色板中从uStart项目到uNum项目之间的若干项目.逻辑调色板中新的uStart项目是PALETTEENTRY结构中的第一个成员.当心!uStart参数是进入原始逻辑调色板表的索引,而不是进入PALETTEENTRY数组的索引.

为了方便起见,BOUNCE使用PALETTEENTRY结构的数组,该结构是建立逻辑调色板时使用的LOGPALETTE结构的一部分.球的目前位置(从0到32)储存在静态变量iBall中.在TimerRoutine期间,BOUNCE将PALETTEENTRY成员设为白色.然后计算球的下一个位置,并将该元素设为红色.用下面的呼叫来改变调色板:

AnimatePalette (hPalette, 0, 33, plp->palPalEntry) ;
        

GDI改变33逻辑调色板项目中的第一个(尽管实际上只改变了两个),使它与系统调色板表中的变化相对应,然后修改显示卡上的硬件调色板表.这样,不用重画球就开始移动了.

BOUNCE执行时,您会发现同时执行SYSPAL2或SYSPAL3效果会更好.

尽管AnimatePalette执行得非常快,但是当只有一两个项目改变时,您还应该尽量避免改变所有的逻辑调色板项目.这在BOUNCE中有点复杂,因为球要来回地跳-iBall要先增加,然后再减少.一种方法是使用两个变量:分别称为iBallOld(设定球的目前位置)和iBallMin(iBall和iBallOld中较小的).然后您就可以像下面这样呼叫AnimatePalette来改变两个项目了:

iBallMin = min (iBall, iBallOld) ;
        
AnimatePalette (hPal, iBallMin, 2, plp->palPalEntry + iBallMin) ;
        

还有另一种方法:我们先假定您定义了一个PALETTEENTRY结构:

PALETTEENTRY pe ;
        

在TimerRoutine期间,您将PALETTEENTRY字段设为白色,并呼叫AnimatePalette来改变逻辑调色板中iBall位置的一个项目:

pe.peRed           = 255 ;
        
           pe.peGreen            = 255 ;
        
           pe.peBlue             = 255 ;
        
           pe.peFlags            = PC_RESERVED ;
        
           AnimatePalette (hPalette, iBall, 1, &pe) ;
        

然后计算显示在BOUNCE中的iBall的新值,将PALETTEENTRY结构的字段定义为红色,然后再次呼叫AnimatePalette:

pe.peRed           = 255 ;
        
           pe.peGreen            = 0 ;
        
           pe.peBlue             = 0 ;
        
           pe.peFlags            = PC_RESERVED ;
        
           AnimatePalette (hPalette, iBall, 1, &pe) ;
        

尽管跳动的球是对动画的一个传统的简单说明,但它实际上并不适合调色板动画,因为必须先画出球的所有可能位置.调色板动画更适合于显示运动的重复图案.

一个项目的调色板动画

调色板动画中一个更有趣的方面就是,可以只使用一个调色板项目来完成一些有趣的技术.例如程序16-8所示的FADER程序.这个程序也需要前面的PALANIM.C文件.

程序16-8 FADER
        
FADER.ASM

;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
ID_TIMER equ 1
.DATA
szAppName TCHAR "Fader",0
szTitle TCHAR "Fader: Palette Animation Demo",0
szTextFAD TCHAR "Fade In and Out ",0
bLeftToRight BOOL TRUE
bFadeIn BOOL TRUE
.DATA?
hInstance HINSTANCE ?
hPalette HPALETTE ?
cxClient DWORD ?
cyClient DWORD ?
;LOGPALETTE STRUCT
; palVersion WORD ?
; palNumEntries WORD ?
; palPalEntry PALETTEENTRY <>
;LOGPALETTE ENDS

;PALETTEENTRY STRUCT
; peRed BYTE ?
; peGreen BYTE ?
; peBlue BYTE ?
; peFlags BYTE ?
;PALETTEENTRY ENDS
iBall DWORD ?
szBuffer db 100 dup(?)
lp LOGPALETTE <>
.CODE

CreateRoutine proc hwnd:HWND
LOCAL hPaletteCR:HPALETTE

mov lp.palVersion,0300h
mov lp.palNumEntries,1
mov lp.palPalEntry.peRed,255
mov lp.palPalEntry.peGreen,255
mov lp.palPalEntry.peBlue,255
mov lp.palPalEntry.peFlags, PC_RESERVED

invoke CreatePalette,ADDR lp
mov hPaletteCR,eax

invoke SetTimer,hwnd, ID_TIMER, 50, NULL
mov eax,hPaletteCR
ret
CreateRoutine endp

PaintRoutine proc hdc:HDC,cxClientPR:DWORD,cyClientPR:DWORD
LOCAL x,y:DWORD
LOCAL sizeText:SIZEL

invoke SetTextColor,hdc,01000000h
invoke lstrlen,addr szTextFAD
mov ebx,eax
invoke GetTextExtentPoint32 ,hdc,addr szTextFAD, ebx, addr sizeText

xor eax,eax
mov x,eax
loopx:
xor eax,eax
mov y,eax
loopy:
invoke lstrlen,addr szTextFAD
mov ebx,eax
invoke TextOut,hdc, x, y,addr szTextFAD, ebx

mov eax,sizeText.y
add y,eax
mov eax,y
.if eax<cyClient
jNz loopy
.endif
mov eax,sizeText.x
add x,eax
mov eax,x
.if eax<cxClientPR
jNz loopx
.endif

ret
PaintRoutine endp

TimerRoutine proc hdc:HDC,hPaletteTR:HPALETTE
.if bFadeIn!=FALSE
sub lp.palPalEntry.peRed,4
sub lp.palPalEntry.peGreen,4
.if lp.palPalEntry.peRed == 3
mov bFadeIn,FALSE
.endif
.else
add lp.palPalEntry.peRed,4
add lp.palPalEntry.peGreen,4
.if lp.palPalEntry.peRed == 255
mov bFadeIn,TRUE
.endif
.endif
lea ebx,lp
add ebx,4
invoke AnimatePalette,hPaletteTR, 0, 1, ebx
ret
TimerRoutine endp

DestroyRoutine proc hwnd:HWND,hPaletteDR:HPALETTE
invoke KillTimer,hwnd, ID_TIMER
invoke DeleteObject,hPaletteDR
ret
DestroyRoutine endp


START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND

mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc

mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0

push hInst
pop wndclass.hInstance

invoke LoadIcon,NULL, IDI_APPLICATION
mov wndclass.hIcon,eax

invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax

invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX

lea eax,szAppName
mov wndclass.lpszMenuName,eax
lea eax,szAppName
mov wndclass.lpszClassName,eax

mov wndclass.hIconSm,0

invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif

invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
ADDR szTitle,
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax
.if hWnd==0
xor eax,eax
ret
.endif

invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd

StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:

mov eax,msg.wParam
ret
WinMain endp

CheckDisplay proc hwnd:HWND
LOCAL hdc:HDC
LOCAL iPalSize:DWORD

invoke GetDC,hwnd
mov hdc,eax

invoke GetDeviceCaps,hdc, SIZEPALETTE
mov iPalSize,eax

invoke ReleaseDC,hwnd, hdc

.if (iPalSize != 256)
invoke MessageBox,hwnd,CTEXT ("This program requires that the video display mode have a 256-color palette."),
addr szAppName, MB_ICONERROR
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
CheckDisplay Endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL hdc:HDC
LOCAL ps :PAINTSTRUCT

.if uMsg == WM_CREATE
invoke CheckDisplay,hwnd
.if eax==0
mov eax,-1
ret
.endif

invoke CreateRoutine,hwnd
mov hPalette,eax

xor eax,eax
ret
.elseif uMsg == WM_DISPLAYCHANGE
invoke CheckDisplay,hwnd
.if eax==0
invoke DestroyWindow,hwnd
.endif
xor eax,eax
ret
.elseif uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax

mov eax,lParam
shr eax,16
mov cyClient,eax

xor eax,eax
ret
.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
invoke SelectPalette,hdc,hPalette,FALSE
invoke RealizePalette,hdc
invoke PaintRoutine,hdc,cxClient,cyClient
invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_TIMER
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke TimerRoutine,hdc, hPalette
invoke ReleaseDC,hwnd, hdc

xor eax,eax
ret
.elseif uMsg == WM_QUERYNEWPALETTE
.if hPalette==0
mov eax,FALSE
ret
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke InvalidateRect,hwnd, NULL, TRUE
invoke ReleaseDC,hwnd, hdc
mov eax,TRUE
ret
.elseif uMsg == WM_PALETTECHANGED
mov eax,wParam
.if (hPalette==0)||(eax==hwnd)
jmp ByDefault
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke UpdateColors,hdc
invoke ReleaseDC,hwnd, hdc
jmp ByDefault
.elseif uMsg == WM_DESTROY
invoke DestroyRoutine,hwnd, hPalette
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif
ByDefault:
invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START

程序运行结果(必须在256色下),文字颜色由浅变深,再由深变浅.

FADER在显示区域上显示满了文字字符串「Fade In And Out」.文字首先显示为白色,这对于白色背景的窗口来说是看不出来的.通过使用调色板动画,FADER慢慢地将文字的颜色改为蓝色,然后再改回白色,这样一遍一遍地重复.文字就有渐现渐隐的显示效果了.

FADER用CreateRoutine函数建立了逻辑调色板,它只需要一个调色板项目,并将颜色初始化为白色-红色、绿色和蓝色值都设为255.在PaintRoutine中(您可能想起,当逻辑调色板选进设备内容并显现以后,PALANIM呼叫过此函数),FADER呼叫SetTextColor将文字颜色设定为PALETTEINDEX(0).这意味着文字颜色设定为调色板表格中的第一个项目,此项目初始为白色.然后FADER用「Fade In And Out」文字字符串填充显示区域.这时,窗口背景是白色,文字也是白色,所以文字不可见.

在TimerRoutine函数中,FADER通过改变PALETTEENTRY结构并将其传递给AnimatePalette来完成调色板动画.最初,对每一个WM_TIMER消息,程序都将红色和绿色值减4,直到等于3;然后将这些值加4,直到等于255.这将使文字颜色逐渐从白色变到蓝色,然后又回到白色.

程序16-9所示的ALLCOLOR程序只用了逻辑调色板的一个项目来显示显示卡可以着色的所有颜色.当然,程序不是同时显示这些颜色,而是连续显示.如果显示卡有18位的分辨率(这时能有262144种不同的颜色),那么在两种颜色间隔55毫秒的速度下,只需要4小时就可以在屏幕上看到所有的颜色.

程序16-9 ALLCOLOR
        
ALLCOLOR.ASM

;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
ID_TIMER equ 1
.DATA
szAppName TCHAR "AllColor",0
szTitle TCHAR "AllColor: Palette Animation Demo",0

ilncr DWORD ?
pe PALETTEENTRY <0>

bRedUp BOOL TRUE
bGreenUp BOOL TRUE
bBlueUp BOOL TRUE
.DATA?
hInstance HINSTANCE ?
hPalette HPALETTE ?
cxClient DWORD ?
cyClient DWORD ?

.CODE

CreateRoutine proc hwnd:HWND
LOCAL hdc:HDC
LOCAL hPaletteCR:HPALETTE
LOCAL lp:LOGPALETTE

; Determine the color resolution and set iIncr
invoke GetDC,hwnd
mov hdc,eax

; iIncr = 1 << (8 - GetDeviceCaps (hdc, COLORRES) / 3)
invoke GetDeviceCaps,hdc, COLORRES
xor edx,edx
mov ecx,3
div ecx
mov ecx,8
sub ecx,eax
mov eax,1
shl eax,cl
mov ilncr,eax

invoke ReleaseDC,hwnd, hdc

mov lp.palVersion,0300h
mov lp.palNumEntries,1
mov lp.palPalEntry.peRed,0
mov lp.palPalEntry.peGreen,0
mov lp.palPalEntry.peBlue,0
mov lp.palPalEntry.peFlags, PC_RESERVED

invoke CreatePalette,ADDR lp
mov hPaletteCR,eax

;Save global for less typing
mov eax, lp.palPalEntry[0]
mov pe,eax

invoke SetTimer,hwnd, ID_TIMER, 10, NULL
mov eax,hPaletteCR
ret
CreateRoutine endp

DisplayRGB proc hdc:HDC, ppe:DWORD ;指向 PALETTEENTRY 的指针
LOCAL szBuffer[16]:TCHAR
mov esi,ppe
mov eax,[esi]
xor ebx,ebx
mov bl,al
shr eax,8
xor ecx,ecx
mov cl,al
shr eax,8
xor edx,edx
mov dl,al
invoke wsprintf,addr szBuffer, CTEXT (" %02X-%02X-%02X "),ebx,ecx,edx
invoke lstrlen,addr szBuffer
mov ebx,eax
invoke TextOut,hdc, 0, 0,addr szBuffer, ebx
ret
DisplayRGB endp

PaintRoutine proc hdc:HDC,cxClientPR:DWORD,cyClientPR:DWORD
LOCAL hBrush:HBRUSH
LOCAL rect:RECT

; Draw Palette Index 0 on entire window
mov eax,01000000h
invoke CreateSolidBrush,eax
mov hBrush,eax

invoke SetRect,addr rect, 0, 0, cxClientPR, cyClientPR
invoke FillRect,hdc,addr rect, hBrush
invoke GetStockObject,WHITE_BRUSH
invoke SelectObject,hdc,eax
invoke DeleteObject,eax

; Display the RGB value
invoke DisplayRGB,hdc,addr pe

ret
PaintRoutine endp

TimerRoutine proc hdc:HDC,hPaletteTR:HPALETTE
;Define new color value
; pe.peBlue += (bBlueUp ? iIncr : -iIncr) ;
xor eax,eax
.if bBlueUp!=0
mov eax,ilncr
.else
sub eax,ilncr
.endif
add pe.peBlue,al

.if bBlueUp!=0
xor eax,eax
.else
mov eax,256
sub eax,ilncr
.endif
.if pe.peBlue==al
; pe.peBlue = (bBlueUp ? 256 - iIncr : 0)
.if bBlueUp!=0
mov eax,256
sub eax,ilncr
.else
xor eax,eax
.endif
mov pe.peBlue,al

mov eax,bBlueUp
xor eax,TRUE
mov bBlueUp,eax


;pe.peGreen += (bGreenUp ? iIncr : -iIncr) ;
xor eax,eax
.if bGreenUp!=0
mov eax,ilncr
.else
sub eax,ilncr
.endif
add pe.peGreen,al


.if bGreenUp!=0 ;if (pe.peGreen == (BYTE) (bGreenUp ? 0 : 256 - iIncr))
xor eax,eax
.else
mov eax,256
sub eax,ilncr
.endif
.if pe.peGreen==al
.if bGreenUp!=0
mov eax,256
sub eax,ilncr
.else
xor eax,eax
.endif
mov pe.peGreen,al

mov eax,bGreenUp
xor eax,TRUE
mov bGreenUp,eax

.if bRedUp!=0
mov eax,ilncr
.else
xor eax,eax
sub eax,ilncr
.endif
add pe.peRed,al

.if bRedUp!=0
xor eax,eax
.else
mov eax,256
sub eax,ilncr
.endif
.if pe.peRed==al
.if bRedUp!=0
mov eax,256
sub eax,ilncr
.else
xor eax,eax
.endif
mov pe.peRed,al

mov eax,bRedUp
xor eax,TRUE
mov bRedUp,eax
.endif
.endif
.endif


;Animate the palette
invoke AnimatePalette,hPaletteTR, 0, 1,addr pe
invoke DisplayRGB,hdc,addr pe
ret
TimerRoutine endp

DestroyRoutine proc hwnd:HWND,hPaletteDR:HPALETTE
invoke KillTimer,hwnd, ID_TIMER
invoke DeleteObject,hPaletteDR
ret
DestroyRoutine endp


START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND

mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc

mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0

push hInst
pop wndclass.hInstance

invoke LoadIcon,NULL, IDI_APPLICATION
mov wndclass.hIcon,eax

invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax

invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX

lea eax,szAppName
mov wndclass.lpszMenuName,eax
lea eax,szAppName
mov wndclass.lpszClassName,eax

mov wndclass.hIconSm,0

invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif

invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
ADDR szTitle,
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax
.if hWnd==0
xor eax,eax
ret
.endif

invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd

StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:

mov eax,msg.wParam
ret
WinMain endp

CheckDisplay proc hwnd:HWND
LOCAL hdc:HDC
LOCAL iPalSize:DWORD

invoke GetDC,hwnd
mov hdc,eax

invoke GetDeviceCaps,hdc, SIZEPALETTE
mov iPalSize,eax

invoke ReleaseDC,hwnd, hdc

.if (iPalSize != 256)
invoke MessageBox,hwnd,CTEXT ("This program requires that the video display mode have a 256-color palette."),
addr szAppName, MB_ICONERROR
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
CheckDisplay Endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL hdc:HDC
LOCAL ps :PAINTSTRUCT

.if uMsg == WM_CREATE
invoke CheckDisplay,hwnd
.if eax==0
mov eax,-1
ret
.endif

invoke CreateRoutine,hwnd
mov hPalette,eax

xor eax,eax
ret
.elseif uMsg == WM_DISPLAYCHANGE
invoke CheckDisplay,hwnd
.if eax==0
invoke DestroyWindow,hwnd
.endif
xor eax,eax
ret
.elseif uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax

mov eax,lParam
shr eax,16
mov cyClient,eax

xor eax,eax
ret
.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
invoke SelectPalette,hdc,hPalette,FALSE
invoke RealizePalette,hdc
invoke PaintRoutine,hdc,cxClient,cyClient
invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_TIMER
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke TimerRoutine,hdc, hPalette
invoke ReleaseDC,hwnd, hdc

xor eax,eax
ret
.elseif uMsg == WM_QUERYNEWPALETTE
.if hPalette==0
mov eax,FALSE
ret
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke InvalidateRect,hwnd, NULL, TRUE
invoke ReleaseDC,hwnd, hdc
mov eax,TRUE
ret
.elseif uMsg == WM_PALETTECHANGED
mov eax,wParam
.if (hPalette==0)||(eax==hwnd)
jmp ByDefault
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke UpdateColors,hdc
invoke ReleaseDC,hwnd, hdc
jmp ByDefault
.elseif uMsg == WM_DESTROY
invoke DestroyRoutine,hwnd, hPalette
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif
ByDefault:
invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START

程序运行结果如下(必须在256色下)

在结构上,ALLCOLOR与FADER非常相似.在CreateRoutine中,ALLCOLOR只用一个设为黑色的调色板项目(PALETTEENTRY结构的red、green和blue字段设为0)来建立调色板.在PaintRoutine中,ALLCOLOR用PALETTEINDEX(0)建立实心画刷,并呼叫FillRect来用此画刷为整个显示区域着色.

在TimerRoutine中,ALLCOLOR通过改变PALETTEENTRY颜色并呼叫AnimatePalette来启动调色板.我编写ALLCOLOR程序,以便颜色变化顺畅.首先,蓝色值渐渐增加.达到最大时,绿色值增加,而蓝色值渐渐减少.红色、绿色和蓝色值的增加和减少取决于iIncr变量.在CreateRoutine期间,这将根据用COLORRES参数从GetDeviceCaps传回的值来计算.例如,如果GetDeviceCaps传回18,那么iIncr设为4-获得所有颜色所需要的最小值.

ALLCOLOR还在显示区域的左上角显示目前的RGB颜色值.我最初添加这个程序代码是出于测试目的,但是现在证明它是有用的,所以我保留了它.

工程应用程序

在工程应用程序中,动画对于显示机械或电的作用过程很有用.在计算机屏幕上显示内燃引擎虽然简单,但是动画可以使它变得更加生动,且更清楚地显示其工作程序.

使用调色板动画的一个好范例就是显示流体通过管子的过程.这是一个例子,图像不必十分精确-实际上,如果图像很精确(就像看透明的管子),则很难说明管子里的流体是如何运动的.这时用符号会更好一些.程序16-10所示的PIPES程序是此技术的简单示范:在显示区域有两个水平的管子,流体在上面的管子里从左向右流动,而在下面的管子里从右向左移动.

程序16-10 PIPES

PIPES.ASM
;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
ID_TIMER equ 1
.DATA
szAppName TCHAR "PIPES",0
szTitle TCHAR "Pipes: Palette Animation Demo",0

plp DWORD ? ;指向 LOGPALETTE 结构体的指针

.DATA?
hInstance HINSTANCE ?
hPalette HPALETTE ?
cxClient DWORD ?
cyClient DWORD ?

iIndex DWORD ?

.CODE

CreateRoutine proc hwnd:HWND
LOCAL hPaletteCR:HPALETTE
LOCAL i:DWORD

mov eax,sizeof (PALETTEENTRY)
mov ecx,32
mul ecx
add eax,sizeof (LOGPALETTE)
invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,eax ;plp = malloc (sizeof (LOGPALETTE) + 32 * sizeof (PALETTEENTRY))
mov plp,eax

; Initialize the fields of the LOGPALETTE structure
mov esi,plp
mov WORD ptr [esi],0300h
mov WORD ptr [esi+2],16

mov i,0
mov esi,plp
add esi,4
Loopi:
mov eax,i
mov ecx,20h
mul ecx
.if eax>255
mov al,255
.endif
mov ebx,i
shl ebx,2
mov BYTE PTR [esi+ebx],al
mov BYTE PTR [esi+ebx+1],0
mov BYTE PTR [esi+ebx+2],al
mov BYTE PTR [esi+ebx+3],PC_RESERVED

mov ebx,i ;plp->palPalEntry[16 - i] = plp->palPalEntry[i]
shl ebx,2
mov eax,[esi+ebx]
mov ebx,16
sub ebx,i
shl ebx,2
mov [esi+ebx],eax

mov ebx,i ;plp->palPalEntry[16 + i] = plp->palPalEntry[i]
shl ebx,2
mov eax,[esi+ebx]
mov ebx,16
add ebx,i
shl ebx,2
mov [esi+ebx],eax

mov ebx,i ;plp->palPalEntry[32 - i] = plp->palPalEntry[i]
shl ebx,2
mov eax,[esi+ebx]
mov ebx,32
sub ebx,i
shl ebx,2
mov [esi+ebx],eax

inc i
.if i<=8
jmp Loopi
.endif

invoke CreatePalette,plp
mov hPaletteCR,eax

invoke SetTimer,hwnd, ID_TIMER, 100, NULL
mov eax,hPaletteCR
ret
CreateRoutine endp

PaintRoutine proc hdc:HDC,cxClientPR:DWORD,cyClientPR:DWORD
LOCAL hBrush:HBRUSH
LOCAL i:DWORD
LOCAL rect:RECT

; Draw window background
invoke SetRect,addr rect, 0, 0, cxClientPR, cyClientPR
invoke GetStockObject,WHITE_BRUSH
invoke SelectObject,hdc, eax
mov hBrush,eax
invoke FillRect,hdc,addr rect, hBrush

;Draw the interiors of the pipes
mov i,0
Loopi2:
mov eax,i
mov ebx,eax
shr ebx,4
shl ebx,4
sub eax,ebx
add eax,01000000h
invoke CreateSolidBrush,eax
mov hBrush ,eax
invoke SelectObject,hdc, hBrush

mov eax,127 ;rect.left = (127 - i) * cxClient / 128
sub eax,i
mov ecx,cxClientPR
mul ecx
shr eax,7
mov rect.left,eax

mov eax,128 ;rect.right = (128 - i) * cxClient / 128
sub eax,i
mov ecx,cxClientPR
mul ecx
shr eax,7
mov rect.right,eax

mov eax,cyClientPR ;rect.top = 4 * cyClient / 14
mov ecx,4
mul ecx
xor edx,edx
mov ecx,14
div ecx
mov rect.top,eax

mov eax,cyClientPR ;rect.bottom = 5 * cyClient / 14
mov ecx,5
mul ecx
xor edx,edx
mov ecx,14
div ecx
mov rect.bottom,eax

invoke FillRect,hdc,addr rect, hBrush

mov eax,i ;rect.left = i * cxClient / 128
mov ecx,cxClientPR
mul ecx
shr eax,7
mov rect.left,eax

mov eax,i ;rect.right = ( i + 1) * cxClient / 128
inc eax
mov ecx,cxClientPR
mul ecx
shr eax,7
mov rect.right,eax

mov eax,cyClientPR ;rect.top = 9 * cyClient / 14
mov ecx,9
mul ecx
xor edx,edx
mov ecx,14
div ecx
mov rect.top,eax

mov eax,cyClientPR ;rect.bottom = 10 * cyClient / 14
mov ecx,10
mul ecx
xor edx,edx
mov ecx,14
div ecx
mov rect.bottom,eax

invoke FillRect,hdc,addr rect, hBrush


invoke GetStockObject,WHITE_BRUSH
invoke SelectObject,hdc,eax
invoke DeleteObject,eax

inc i
.if i<128
jmp Loopi2
.endif

; Draw the edges of the pipes
mov eax,cyClientPR
mov ecx,4
mul ecx
xor edx,edx
mov ecx,14
div ecx
mov ebx,eax
invoke MoveToEx,hdc, 0, ebx, NULL
invoke LineTo,hdc, cxClientPR,ebx

mov eax,cyClientPR
mov ecx,5
mul ecx
xor edx,edx
mov ecx,14
div ecx
mov ebx,eax
invoke MoveToEx,hdc, 0,ebx, NULL
invoke LineTo,hdc, cxClientPR,ebx

mov eax,cyClientPR
mov ecx,9
mul ecx
xor edx,edx
mov ecx,14
div ecx
mov ebx,eax
invoke MoveToEx,hdc, 0,ebx, NULL
invoke LineTo,hdc, cxClientPR, ebx

mov eax,cyClientPR
mov ecx,10
mul ecx
xor edx,edx
mov ecx,14
div ecx
mov ebx,eax
invoke MoveToEx,hdc, 0, ebx, NULL
invoke LineTo,hdc, cxClientPR, ebx
ret
PaintRoutine endp

TimerRoutine proc hdc:HDC,hPaletteTR:HPALETTE
mov esi,plp
add esi,4
mov eax,esi
mov ebx,iIndex
shl ebx,2
add eax,ebx
invoke AnimatePalette,hPaletteTR, 0, 16,eax

mov eax,iIndex
inc eax
mov ebx,eax
shr ebx,4
shl ebx,4
sub eax,ebx
mov iIndex,eax
ret
TimerRoutine endp

DestroyRoutine proc hwnd:HWND,hPaletteDR:HPALETTE
invoke KillTimer,hwnd, ID_TIMER
invoke DeleteObject,hPaletteDR
invoke LocalFree,plp
ret
DestroyRoutine endp


START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND

mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc

mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0

push hInst
pop wndclass.hInstance

invoke LoadIcon,NULL, IDI_APPLICATION
mov wndclass.hIcon,eax

invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax

invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX

lea eax,szAppName
mov wndclass.lpszMenuName,eax
lea eax,szAppName
mov wndclass.lpszClassName,eax

mov wndclass.hIconSm,0

invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif

invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
ADDR szTitle,
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax
.if hWnd==0
xor eax,eax
ret
.endif

invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd

StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:

mov eax,msg.wParam
ret
WinMain endp

CheckDisplay proc hwnd:HWND
LOCAL hdc:HDC
LOCAL iPalSize:DWORD

invoke GetDC,hwnd
mov hdc,eax

invoke GetDeviceCaps,hdc, SIZEPALETTE
mov iPalSize,eax

invoke ReleaseDC,hwnd, hdc

.if (iPalSize != 256)
invoke MessageBox,hwnd,CTEXT ("This program requires that the video display mode have a 256-color palette."),
addr szAppName, MB_ICONERROR
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
CheckDisplay Endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL hdc:HDC
LOCAL ps :PAINTSTRUCT

.if uMsg == WM_CREATE
invoke CheckDisplay,hwnd
.if eax==0
mov eax,-1
ret
.endif

invoke CreateRoutine,hwnd
mov hPalette,eax

xor eax,eax
ret
.elseif uMsg == WM_DISPLAYCHANGE
invoke CheckDisplay,hwnd
.if eax==0
invoke DestroyWindow,hwnd
.endif
xor eax,eax
ret
.elseif uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax

mov eax,lParam
shr eax,16
mov cyClient,eax

xor eax,eax
ret
.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
invoke SelectPalette,hdc,hPalette,FALSE
invoke RealizePalette,hdc
invoke PaintRoutine,hdc,cxClient,cyClient
invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_TIMER
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke TimerRoutine,hdc, hPalette
invoke ReleaseDC,hwnd, hdc

xor eax,eax
ret
.elseif uMsg == WM_QUERYNEWPALETTE
.if hPalette==0
mov eax,FALSE
ret
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke InvalidateRect,hwnd, NULL, TRUE
invoke ReleaseDC,hwnd, hdc
mov eax,TRUE
ret
.elseif uMsg == WM_PALETTECHANGED
mov eax,wParam
.if (hPalette==0)||(eax==hwnd)
jmp ByDefault
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke UpdateColors,hdc
invoke ReleaseDC,hwnd, hdc
jmp ByDefault
.elseif uMsg == WM_DESTROY
invoke DestroyRoutine,hwnd, hPalette
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif
ByDefault:
invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START


程序运行结果(必须在256色下)

PIPES为动画使用了16个调色板项目,而您可能会使用更少的项目.最小化时,真正需要的是有足够的项目来显示流动的方向.用三个调色板项目要比用一个静态箭头好.

程序16-11所示的TUNNEL程序是这组程序中最贪心的程序,它为动画使用了128个调色板项目,但是从效果来看,值得这样做.

程序16-11 TUNNEL
        
TUNNEL.ASM
        
;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
ID_TIMER equ 1
.DATA
szAppName TCHAR "Tunnel",0
szTitle TCHAR "Tunnel: Palette Animation Demo",0

plp DWORD ? ;指向 LOGPALETTE 结构体的指针

.DATA?
hInstance HINSTANCE ?
hPalette HPALETTE ?
cxClient DWORD ?
cyClient DWORD ?

iLevel DWORD ?

.CODE

CreateRoutine proc hwnd:HWND
LOCAL byGrayLevel:BYTE
LOCAL hPaletteCR:HPALETTE
LOCAL i:DWORD

mov eax,sizeof (PALETTEENTRY)
mov ecx,255
mul ecx
add eax,sizeof (LOGPALETTE)
invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,eax ;plp = malloc (sizeof (LOGPALETTE) + 32 * sizeof (PALETTEENTRY))
mov plp,eax

; Initialize the fields of the LOGPALETTE structure
mov esi,plp
mov WORD ptr [esi],0300h
mov WORD ptr [esi+2],128

mov i,0
mov esi,plp
add esi,4
Loopi:
.if (i < 64)
mov eax,i
shl eax,2
mov byGrayLevel,al
.else
mov eax,128
sub eax,i
shl eax,2
.if eax>255
mov al,255
.endif
mov byGrayLevel,al
.endif

mov ebx,i
shl ebx,2
mov al,byGrayLevel
mov BYTE PTR [esi+ebx],al
mov BYTE PTR [esi+ebx+1],al
mov BYTE PTR [esi+ebx+2],al
mov BYTE PTR [esi+ebx+3],PC_RESERVED

mov ebx,i
add ebx,128
shl ebx,2
mov al,byGrayLevel
mov BYTE PTR [esi+ebx],al
mov BYTE PTR [esi+ebx+1],al
mov BYTE PTR [esi+ebx+2],al
mov BYTE PTR [esi+ebx+3],PC_RESERVED

inc i
.if i<128
jmp Loopi
.endif

invoke CreatePalette,plp
mov hPaletteCR,eax

invoke SetTimer,hwnd, ID_TIMER, 50, NULL
mov eax,hPaletteCR
ret
CreateRoutine endp

PaintRoutine proc hdc:HDC,cxClientPR:DWORD,cyClientPR:DWORD
LOCAL hBrush:HBRUSH
LOCAL i:DWORD
LOCAL rect:RECT

mov i,0
Loopi2:
; Use a RECT structure for each of 128 rectangles
mov eax,i
mov ecx,cxClientPR
mul ecx
xor edx,edx
mov ecx,255
div ecx
mov rect.left,eax

mov eax,i
mov ecx,cyClientPR
mul ecx
xor edx,edx
mov ecx,255
div ecx
mov rect.top,eax

mov eax,i
mov ecx,cxClientPR
mul ecx
xor edx,edx
mov ecx,255
div ecx
mov ebx,cxClientPR
sub ebx,eax
mov rect.right,ebx

mov eax,i
mov ecx,cyClientPR
mul ecx
xor edx,edx
mov ecx,255
div ecx
mov ebx,cyClientPR
sub ebx,eax
mov rect.bottom,ebx

mov eax,i
add eax,01000000h
invoke CreateSolidBrush,eax
mov hBrush,eax

; Fill the rectangle and delete the brush
invoke FillRect,hdc,addr rect, hBrush
invoke DeleteObject,hBrush

inc i
.if i<128
jmp Loopi2
.endif

ret
PaintRoutine endp

TimerRoutine proc hdc:HDC,hPaletteTR:HPALETTE
mov eax,iLevel
inc eax
mov ebx,eax
shr ebx,7
shl ebx,7
sub eax,ebx
mov iLevel,eax

mov esi,plp
add esi,4
mov eax,esi
mov ebx,iLevel
shl ebx,2
add eax,ebx
invoke AnimatePalette,hPaletteTR, 0, 128,eax

ret
TimerRoutine endp

DestroyRoutine proc hwnd:HWND,hPaletteDR:HPALETTE
invoke KillTimer,hwnd, ID_TIMER
invoke DeleteObject,hPaletteDR
invoke LocalFree,plp
ret
DestroyRoutine endp


START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND

mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc

mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0

push hInst
pop wndclass.hInstance

invoke LoadIcon,NULL, IDI_APPLICATION
mov wndclass.hIcon,eax

invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax

invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX

lea eax,szAppName
mov wndclass.lpszMenuName,eax
lea eax,szAppName
mov wndclass.lpszClassName,eax

mov wndclass.hIconSm,0

invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif

invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
ADDR szTitle,
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax
.if hWnd==0
xor eax,eax
ret
.endif

invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd

StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:

mov eax,msg.wParam
ret
WinMain endp

CheckDisplay proc hwnd:HWND
LOCAL hdc:HDC
LOCAL iPalSize:DWORD

invoke GetDC,hwnd
mov hdc,eax

invoke GetDeviceCaps,hdc, SIZEPALETTE
mov iPalSize,eax

invoke ReleaseDC,hwnd, hdc

.if (iPalSize != 256)
invoke MessageBox,hwnd,CTEXT ("This program requires that the video display mode have a 256-color palette."),
addr szAppName, MB_ICONERROR
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
CheckDisplay Endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL hdc:HDC
LOCAL ps :PAINTSTRUCT

.if uMsg == WM_CREATE
invoke CheckDisplay,hwnd
.if eax==0
mov eax,-1
ret
.endif

invoke CreateRoutine,hwnd
mov hPalette,eax

xor eax,eax
ret
.elseif uMsg == WM_DISPLAYCHANGE
invoke CheckDisplay,hwnd
.if eax==0
invoke DestroyWindow,hwnd
.endif
xor eax,eax
ret
.elseif uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax

mov eax,lParam
shr eax,16
mov cyClient,eax

xor eax,eax
ret
.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
invoke SelectPalette,hdc,hPalette,FALSE
invoke RealizePalette,hdc
invoke PaintRoutine,hdc,cxClient,cyClient
invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_TIMER
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke TimerRoutine,hdc, hPalette
invoke ReleaseDC,hwnd, hdc

xor eax,eax
ret
.elseif uMsg == WM_QUERYNEWPALETTE
.if hPalette==0
mov eax,FALSE
ret
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke InvalidateRect,hwnd, NULL, TRUE
invoke ReleaseDC,hwnd, hdc
mov eax,TRUE
ret
.elseif uMsg == WM_PALETTECHANGED
mov eax,wParam
.if (hPalette==0)||(eax==hwnd)
jmp ByDefault
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke UpdateColors,hdc
invoke ReleaseDC,hwnd, hdc
jmp ByDefault
.elseif uMsg == WM_DESTROY
invoke DestroyRoutine,hwnd, hPalette
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif
ByDefault:
invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START

运行结果(必须在256色下)

TUNNEL在128个调色板项目中使用64种移动的灰阶-从黑到白,再从白到黑-表现在隧道旅行的效果.

调色板和真实世界图像

当然,尽管我们已经完成了许多有趣的事:连续显示色彩的底纹、做了调色板动画,但调色板管理器的真正目的是允许在8位显示模式下显示真实世界中的图像.对于本章的其余部分,我们正好研究一下.正如您所期望的,在使用packed DIB、GDI位图对象和DIB区块时,必须按照不同的方法来使用调色板.下面的六个程序阐明了用调色板来处理位图的各种技术.

调色板和Packed DIB

程序16-13 SHOWDIB3

        
SHOWDIB3.ASM

;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc
Include comdlg32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
IncludeLib comdlg32.lib

include macro.asm

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
IDM_FILE_OPEN equ 40001

.DATA
szAppName TCHAR "ShowDib3",0

.DATA?
hInstance HINSTANCE ?
hBitmap HBITMAP ?
pPackedDib DWORD ? ;指向 BITMAPINFO 结构体的指针
hPalette HPALETTE ?
cxClient DWORD ?
cyClient DWORD ?
ofn OPENFILENAME <?>
szFileName TCHAR MAX_PATH dup(?)
szTitleName TCHAR MAX_PATH dup(?)
.CODE

; PackedDibLoad: Load DIB File as Packed-Dib Memory Block
PackedDibLoad proc szFN:PTSTR
LOCAL bmfh:BITMAPFILEHEADER
LOCAL pbmi:DWORD ;BITMAPINFO * pbmi ;
LOCAL bSuccess:BOOL
LOCAL dwPackedDibSize, dwBytesRead:DWORD
LOCAL hFile:HANDLE

; Open the file: read access, prohibit write access
invoke CreateFile,szFN, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL
mov hFile,eax
.if hFile == INVALID_HANDLE_VALUE
mov eax,NULL
ret
.endif

; Read in the BITMAPFILEHEADER
invoke ReadFile,hFile,addr bmfh, sizeof (BITMAPFILEHEADER),addr dwBytesRead, NULL
mov bSuccess,eax

mov eax,dwBytesRead
mov bx,bmfh.bfType
.if (bSuccess==0) || ( eax != sizeof (BITMAPFILEHEADER)) || ( ebx != "MB")
invoke CloseHandle,hFile
mov eax,NULL
ret
.endif


; Allocate memory for the packed DIB & read it in
mov eax,bmfh.bfSize
sub eax,sizeof (BITMAPFILEHEADER)
mov dwPackedDibSize,eax

invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,dwPackedDibSize
mov pbmi,eax

invoke ReadFile,hFile, pbmi, dwPackedDibSize,addr dwBytesRead, NULL
mov bSuccess,eax
invoke CloseHandle,hFile


mov eax,dwBytesRead
.if (bSuccess==0) || (eax != dwPackedDibSize)
invoke LocalFree,pbmi
mov eax,NULL
ret
.endif

mov eax,pbmi
ret
PackedDibLoad endp

; Functions to get information from packed DIB
PackedDibGetWidth proc pPackedDibPGW:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGW
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
;return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcWidth
mov bx,[esi+4]
xor eax,eax
mov ax,bx
ret
.else
;return pPackedDib->bmiHeader.biWidth
mov eax,[esi+4]
ret
.endif
PackedDibGetWidth endp

PackedDibGetHeight proc pPackedDibPGH:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGH
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
mov bx,[esi+6] ;return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcHeight
xor eax,eax
mov ax,bx
ret

ret
.else
;return abs (pPackedDib->bmiHeader.biHeight) ;
mov eax,[esi+8]
ret
.endif
PackedDibGetHeight endp

PackedDibGetBitCount proc pPackedDibPGB:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGB
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
mov bx,[esi+10] ; return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcBitCount
xor eax,eax
mov ax,bx
ret
.else
; return pPackedDib->bmiHeader.biBitCount
mov bx,[esi+14]
xor eax,eax
mov ax,bx
ret
.endif
PackedDibGetBitCount endp

PackedDibGetRowLength proc pPackedDibPGR:DWORD ;指向 BITMAPINFO 结构体的指针
;return ((PackedDibGetWidth (pPackedDib)*PackedDibGetBitCount (pPackedDib) + 31) & ~31) >> 3
invoke PackedDibGetWidth,pPackedDibPGR
push eax
invoke PackedDibGetBitCount,pPackedDibPGR
mov ebx,eax
pop eax
mul ebx
add eax,31
mov ecx,31
neg ecx
and eax,ecx
shr eax,3
ret
PackedDibGetRowLength endp


PackedDibGetInfoHeaderSize proc pPackedDibPHS:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPHS
xor eax,eax
mov ax,[esi]
.if (ax == sizeof (BITMAPCOREHEADER))
;return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcSize
mov eax,[esi]
ret
.else
.if (ax == sizeof (BITMAPINFOHEADER))
;return pPackedDib->bmiHeader.biSize + (pPackedDib->bmiHeader.biCompression == BI_BITFIELDS ? 12 : 0) ;
mov eax,[esi+16]
.if eax== BI_BITFIELDS
mov ebx,12
.else
mov ebx,0
.endif
mov eax,[esi]
add eax,ebx
ret
.else
mov eax,[esi] ;return pPackedDib->bmiHeader.biSize ;
ret
.endif
.endif
PackedDibGetInfoHeaderSize endp

; PackedDibGetColorsUsed returns value in information header;
; could be 0 to indicate non-truncated color table!
PackedDibGetColorsUsed proc pPackedDibPGC:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGC
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
xor eax,eax
ret
.else ;return pPackedDib->bmiHeader.biClrUsed
mov eax,[esi+32]
ret
.endif
PackedDibGetColorsUsed endp

;PackedDibGetNumColors is actual number of entries in color table
PackedDibGetNumColors proc pPackedDibPGNC:DWORD ;指向 BITMAPINFO 结构体的指针
LOCAL iNumColors:DWORD
invoke PackedDibGetColorsUsed,pPackedDibPGNC
mov iNumColors,eax

invoke PackedDibGetBitCount,pPackedDibPGNC
.if (iNumColors == 0) && (eax < 16)
; iNumColors =1 << PackedDibGetBitCount (pPackedDib) ;
invoke PackedDibGetBitCount,pPackedDibPGNC
mov ecx,eax
mov eax,1
shl eax,cl
mov iNumColors,eax
.endif
mov eax,iNumColors
ret
PackedDibGetNumColors endp

PackedDibGetColorTableSize proc pPackedDibPGCTS:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGCTS
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
;return PackedDibGetNumColors (pPackedDib) * sizeof (RGBTRIPLE)
invoke PackedDibGetNumColors,pPackedDibPGCTS
mov ecx,sizeof (RGBTRIPLE)
mul ecx
ret
.else
;return PackedDibGetNumColors (pPackedDib) * sizeof (RGBQUAD)
invoke PackedDibGetNumColors,pPackedDibPGCTS
mov ecx,sizeof (RGBQUAD)
mul ecx
ret
.endif
PackedDibGetColorTableSize endp

PackedDibGetColorTablePtr proc pPackedDibPGCTP:DWORD ;指向 BITMAPINFO 结构体的指针
invoke PackedDibGetNumColors,pPackedDibPGCTP
.if ( eax == 0)
xor eax,eax
ret
.endif
;return (RGBQUAD *) (((BYTE *)pPackedDib) + PackedDibGetInfoHeaderSize (pPackedDib))
invoke PackedDibGetInfoHeaderSize,pPackedDibPGCTP


mov esi,pPackedDibPGCTP
add eax,esi
ret
PackedDibGetColorTablePtr endp

PackedDibGetColorTableEntry proc pPackedDibPDGT:DWORD,i:DWORD
invoke PackedDibGetNumColors,pPackedDibPDGT
.if eax==0
xor eax,eax
.endif

mov esi,pPackedDibPDGT
mov eax,[esi]
.if eax==sizeof (BITMAPCOREHEADER)
invoke PackedDibGetColorTablePtr,pPackedDibPDGT
mov ebx,i
shl ebx,2
add eax,ebx
ret
.else
invoke PackedDibGetColorTablePtr,pPackedDibPDGT
mov ebx,i
shl ebx,2
add eax,ebx
ret
.endif
PackedDibGetColorTableEntry endp

; PackedDibGetBitsPtr finally!
PackedDibGetBitsPtr proc pPackedDibPDPP:DWORD ;BITMAPINFO *
invoke PackedDibGetInfoHeaderSize,pPackedDibPDPP
push eax
invoke PackedDibGetColorTableSize,pPackedDibPDPP
pop ebx
add eax,ebx
add eax,pPackedDibPDPP
ret
PackedDibGetBitsPtr endp

; PackedDibGetBitsSize can be calculated from the height and row length
; if it's not explicitly in the biSizeImage field
PackedDibGetBitsSize proc pPackedDibPGBS:DWORD ;BITMAPINFO *
mov esi,pPackedDibPGBS
mov eax,[esi]

mov ebx,[esi+20]
.if (eax!=sizeof (BITMAPCOREHEADER)) && (ebx!=0)
mov eax,ebx
ret
.endif

invoke PackedDibGetHeight,pPackedDibPGBS
push eax
invoke PackedDibGetRowLength,pPackedDibPGBS
pop ecx
mul ecx
ret
PackedDibGetBitsSize endp

; PackedDibCreatePalette creates logical palette from PackedDib
PackedDibCreatePalette proc pPackedDibPDCP:DWORD ;BITMAPINFO *
LOCAL hPalettePDCP:HPALETTE
LOCAL i, iNumColors:DWORD
LOCAL plp:DWORD ;指向 LOGPALETTE 结构体的指针
LOCAL prgb:DWORD ;指向 RGBQUAD 结构体的指针
;RGBQUAD STRUCT
; rgbBlue BYTE ?
; rgbGreen BYTE ?
; rgbRed BYTE ?
; rgbReserved BYTE ?
;RGBQUAD ENDS

invoke PackedDibGetNumColors,pPackedDibPDCP
mov iNumColors,eax
.if eax==0
mov eax,NULL
ret
.endif

mov eax,sizeof (LOGPALETTE)
mov ecx,iNumColors
dec ecx
mul ecx
mov ecx,sizeof (PALETTEENTRY)
mul ecx
invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,eax
mov plp,eax ;plp = malloc (sizeof (LOGPALETTE) * (iNumColors - 1) * sizeof (PALETTEENTRY))
mov esi,plp
mov WORD ptr [esi],300h ;plp->palVersion = 0x0300
mov eax,iNumColors
mov WORD ptr [esi+2],ax ;plp->palNumEntries = iNumColors

add esi,4
mov i,0
LoopII:
push esi
invoke PackedDibGetColorTableEntry,pPackedDibPDCP,i
mov prgb,eax

mov edi,prgb ;其实一样的
mov al,[edi]
pop esi
mov [esi],al
mov al,[edi+1]
mov [esi+1],al

mov al,[edi+2]
mov [esi+2],al

mov BYTE ptr [esi+3],0
add esi,4
inc i

mov eax,i
.if eax<iNumColors
jmp LoopII
.endif


invoke CreatePalette,plp
mov hPalettePDCP,eax


invoke LocalFree,plp

mov eax,hPalettePDCP
ret
PackedDibCreatePalette endp

START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND

mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc

mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0

push hInst
pop wndclass.hInstance

invoke LoadIcon,NULL, IDI_APPLICATION
mov wndclass.hIcon,eax

invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax

invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX

lea eax,szAppName
mov wndclass.lpszMenuName,eax
mov wndclass.lpszClassName,eax

mov wndclass.hIconSm,0

invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif


invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
CTXT("Show DIB #3: Native Palette"),
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax

invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd

StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:

mov eax,msg.wParam
ret
WinMain endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL hdc:HDC
LOCAL ps :PAINTSTRUCT

.if uMsg == WM_CREATE
mov eax,sizeof (OPENFILENAME)
mov ofn.lStructSize,eax
mov eax,hwnd
mov ofn.hwndOwner,eax
mov eax,NULL
mov ofn.hInstance,eax
mov ofn.lpstrFilter,CTEXT("Bitmap Files (*.BMP)\0*.bmp\0 All Files (*.*)\0*.*\0\0")
mov eax,NULL
mov ofn.lpstrCustomFilter,eax
xor eax,eax
mov ofn.nMaxCustFilter,eax
mov ofn.nFilterIndex,eax
lea eax,szFileName
mov ofn.lpstrFile,eax ; Set in Open and Close functions
mov eax,MAX_PATH
mov ofn.nMaxFile,eax
mov eax,NULL
mov ofn.lpstrFileTitle,eax ;Set in Open and Close functions
mov eax,MAX_PATH
mov ofn.nMaxFileTitle,eax
mov eax,NULL
mov ofn.lpstrInitialDir,eax
mov ofn.lpstrTitle,eax
mov eax,0
mov ofn.Flags,eax ; Set in Open and Close functions
mov ofn.nFileOffset,ax
mov ofn.nFileExtension,ax
mov ofn.lpstrDefExt,CTEXT ("bmp")
mov eax,0
mov ofn.lCustData,eax
mov eax,NULL
mov ofn.lpfnHook,eax
mov ofn.lpTemplateName,eax

xor eax,eax
ret
.elseif uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax

mov eax,lParam
shr eax,16
mov cyClient,eax

xor eax,eax
ret
.elseif uMsg == WM_COMMAND
mov eax,wParam
.if ax==IDM_FILE_OPEN
; Show the File Open dialog box
invoke GetOpenFileName,addr ofn
.if eax==0
xor eax,eax
ret
.endif
; If there's an existing packed DIB, free the memory
.if (pPackedDib!=0)
invoke LocalFree,pPackedDib
mov pPackedDib,NULL
.endif

; If there's an existing logical palette, delete it
.if (hPalette!=0)
invoke DeleteObject,hPalette
mov hPalette,NULL
.endif

; Load the packed DIB into memory
invoke LoadCursor,NULL, IDC_WAIT
invoke SetCursor,eax
invoke ShowCursor,TRUE
invoke PackedDibLoad,addr szFileName
mov pPackedDib,eax

invoke ShowCursor,FALSE
invoke LoadCursor,NULL, IDC_ARROW
invoke SetCursor,eax

.if pPackedDib!=0
; Create the palette from the DIB color table
invoke PackedDibCreatePalette,pPackedDib
mov hPalette,eax

.elseif
invoke MessageBox,hwnd, CTEXT ("Cannot load DIB file"),addr szAppName, 0
.endif
invoke InvalidateRect,hwnd, NULL, TRUE
xor eax,eax
ret
.endif

.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
.if hPalette!=0
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
.endif

.if pPackedDib!=0
invoke PackedDibGetWidth,pPackedDib
mov ebx,eax
push ebx
invoke PackedDibGetHeight,pPackedDib
mov ecx,eax
push ecx
invoke PackedDibGetBitsPtr,pPackedDib

pop ecx
pop ebx
invoke SetDIBitsToDevice,hdc, 0,0,
ebx,ecx,
0,0,0,ecx,
eax,
pPackedDib,
DIB_PAL_COLORS

.endif
invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_QUERYNEWPALETTE
.if hPalette==0
mov eax,FALSE
ret
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke InvalidateRect,hwnd, NULL, TRUE
invoke ReleaseDC,hwnd, hdc

mov eax,TRUE
ret
.elseif uMsg == WM_PALETTECHANGED
mov eax,wParam
.if (hPalette==0) || (eax == hwnd)
jmp WM_PALETTECHANGED_END
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke UpdateColors,hdc
invoke ReleaseDC,hwnd, hdc
.elseif uMsg == WM_DESTROY
.if pPackedDib!=0
invoke LocalFree,pPackedDib
.endif
.if hPalette!=0
invoke DeleteObject,hPalette
.endif
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif
WM_PALETTECHANGED_END:
invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START

SHOWDIB3.RC (摘录)
        
//Microsoft Developer Studio generated resource script.
        
#include "resource.h"
        
#include "afxres.h"
        
/////////////////////////////////////////////////////////////////////////////
        
// Menu
        
SHOWDIB3 MENU DISCARDABLE
        
BEGIN
        
   POPUP "&File"
        
   BEGIN
        
       MENUITEM "&Open",                 IDM_FILE_OPEN
        
   END
        
END
        
RESOURCE.H (摘录)
        
// Microsoft Developer Studio generated include file.
        
// Used by ShowDib3.rc
        
#define IDM_FILE_OPEN         40001
        

SHOWDIB3中的窗口消息处理程序将packed DIB指针作为静态变量来维护,窗口消息处理程序在「File Open」命令期间呼叫PACKEDIB.C中的PackedDibLoad函数时获得了此指标.在处理此命令的过程中,SHOWDIB3也呼叫PackedDibCreatePalette来获得可能用于DIB的调色板.注意,无论SHOWDIB3什么时候准备加载新的DIB,都应先释放前一个DIB的内存,并删除前一个DIB的调色板.在处理WM_DESTROY消息的程序中,最后的DIB最后释放,最后的调色板最后删除.

处理WM_PAINT消息很简单:如果存在调色板,则SHOWDIB3将它选进设备内容并显现它.然后它呼叫SetDIBitsToDevice,并传递有关DIB的函数信息(例如宽、高和指向DIB像素位的指针),这些信息从PACKEDIB中的函数获得.

另外,请记住SHOWDIB3依据DIB中的颜色表建立了调色板.如果在DIB中没有颜色表-通常是16位、24位和32位DIB的情况-就不建立调色板.在8位显示模式下显示DIB时,它只能用标准保留的20种颜色显示.

对这个问题有两种解决方法:第一种是简单地使用「通用」调色板,这种调色板适用于许多图形.您也可以自己建立调色板.第二种解决方法是分析DIB的像素位,并决定要显示图像的最佳颜色.很明显,第二种方法将涉及更多的工作(对于程序写作者和处理器都是如此),但是我将在本章结束之前告诉您如何使用第二种方法.

「通用」调色板

程序16-14所示的SHOWDIB4程序建立了一个通用的调色板,它用于显示加载到程序中的所有DIB.另外,SHOWDIB4与SHOWDIB3非常相似.

程序16-14 SHOWDIB4
        
SHOWDIB4.ASM
;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc
Include comdlg32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
IncludeLib comdlg32.lib

include macro.asm

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
IDM_FILE_OPEN equ 40001

.DATA
szAppName TCHAR "SHOWDIB4",0

.DATA?
hInstance HINSTANCE ?
hBitmap HBITMAP ?
pPackedDib DWORD ? ;指向 BITMAPINFO 结构体的指针
hPalette HPALETTE ?
cxClient DWORD ?
cyClient DWORD ?
ofn OPENFILENAME <?>
szFileName TCHAR MAX_PATH dup(?)
szTitleName TCHAR MAX_PATH dup(?)
szBuffer db 100 dup(?)

.CODE

; PackedDibLoad: Load DIB File as Packed-Dib Memory Block
PackedDibLoad proc szFN:PTSTR
LOCAL bmfh:BITMAPFILEHEADER
LOCAL pbmi:DWORD ;BITMAPINFO * pbmi ;
LOCAL bSuccess:BOOL
LOCAL dwPackedDibSize, dwBytesRead:DWORD
LOCAL hFile:HANDLE

; Open the file: read access, prohibit write access
invoke CreateFile,szFN, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL
mov hFile,eax
.if hFile == INVALID_HANDLE_VALUE
mov eax,NULL
ret
.endif

; Read in the BITMAPFILEHEADER
invoke ReadFile,hFile,addr bmfh, sizeof (BITMAPFILEHEADER),addr dwBytesRead, NULL
mov bSuccess,eax

mov eax,dwBytesRead
mov bx,bmfh.bfType
.if (bSuccess==0) || ( eax != sizeof (BITMAPFILEHEADER)) || ( ebx != "MB")
invoke CloseHandle,hFile
mov eax,NULL
ret
.endif


; Allocate memory for the packed DIB & read it in
mov eax,bmfh.bfSize
sub eax,sizeof (BITMAPFILEHEADER)
mov dwPackedDibSize,eax

invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,dwPackedDibSize
mov pbmi,eax

invoke ReadFile,hFile, pbmi, dwPackedDibSize,addr dwBytesRead, NULL
mov bSuccess,eax
invoke CloseHandle,hFile


mov eax,dwBytesRead
.if (bSuccess==0) || (eax != dwPackedDibSize)
invoke LocalFree,pbmi
mov eax,NULL
ret
.endif

mov eax,pbmi
ret
PackedDibLoad endp

; Functions to get information from packed DIB
PackedDibGetWidth proc pPackedDibPGW:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGW
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
;return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcWidth
mov bx,[esi+4]
xor eax,eax
mov ax,bx
ret
.else
;return pPackedDib->bmiHeader.biWidth
mov eax,[esi+4]
ret
.endif
PackedDibGetWidth endp

PackedDibGetHeight proc pPackedDibPGH:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGH
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
mov bx,[esi+6] ;return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcHeight
xor eax,eax
mov ax,bx
ret

ret
.else
;return abs (pPackedDib->bmiHeader.biHeight) ;
mov eax,[esi+8]
ret
.endif
PackedDibGetHeight endp

PackedDibGetBitCount proc pPackedDibPGB:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGB
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
mov bx,[esi+10] ; return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcBitCount
xor eax,eax
mov ax,bx
ret
.else
; return pPackedDib->bmiHeader.biBitCount
mov bx,[esi+14]
xor eax,eax
mov ax,bx
ret
.endif
PackedDibGetBitCount endp

PackedDibGetRowLength proc pPackedDibPGR:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGR
mov eax,[esi]
;return ((PackedDibGetWidth (pPackedDib)*PackedDibGetBitCount (pPackedDib) + 31) & ~31) >> 3
invoke PackedDibGetWidth,pPackedDibPGR
push eax
invoke PackedDibGetBitCount,pPackedDibPGR
mov ebx,eax
pop eax
add eax,ebx
add eax,31
mov ecx,31
neg ecx
and eax,ecx
shr eax,3
ret
PackedDibGetRowLength endp


PackedDibGetInfoHeaderSize proc pPackedDibPHS:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPHS
xor eax,eax
mov ax,[esi]
.if (ax == sizeof (BITMAPCOREHEADER))
;return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcSize
mov eax,[esi]
ret
.else
.if (ax == sizeof (BITMAPINFOHEADER))
;return pPackedDib->bmiHeader.biSize + (pPackedDib->bmiHeader.biCompression == BI_BITFIELDS ? 12 : 0) ;
mov eax,[esi+16]
.if eax== BI_BITFIELDS
mov ebx,12
.else
mov ebx,0
.endif
mov eax,[esi]
add eax,ebx
ret
.else
mov eax,[esi] ;return pPackedDib->bmiHeader.biSize ;
ret
.endif
.endif
PackedDibGetInfoHeaderSize endp

; PackedDibGetColorsUsed returns value in information header;
; could be 0 to indicate non-truncated color table!
PackedDibGetColorsUsed proc pPackedDibPGC:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGC
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
xor eax,eax
ret
.else ;return pPackedDib->bmiHeader.biClrUsed
mov eax,[esi+32]
ret
.endif
PackedDibGetColorsUsed endp

;PackedDibGetNumColors is actual number of entries in color table
PackedDibGetNumColors proc pPackedDibPGNC:DWORD ;指向 BITMAPINFO 结构体的指针
LOCAL iNumColors:DWORD
invoke PackedDibGetColorsUsed,pPackedDibPGNC
mov iNumColors,eax

invoke PackedDibGetBitCount,pPackedDibPGNC
.if (iNumColors == 0) && (eax < 16)
; iNumColors =1 << PackedDibGetBitCount (pPackedDib) ;
invoke PackedDibGetBitCount,pPackedDibPGNC
mov ecx,eax
mov eax,1
shl eax,cl
mov iNumColors,eax
.endif
mov eax,iNumColors
ret
PackedDibGetNumColors endp

PackedDibGetColorTableSize proc pPackedDibPGCTS:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGCTS
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
;return PackedDibGetNumColors (pPackedDib) * sizeof (RGBTRIPLE)
invoke PackedDibGetNumColors,pPackedDibPGCTS
mov ecx,sizeof (RGBTRIPLE)
mul ecx
ret
.else
;return PackedDibGetNumColors (pPackedDib) * sizeof (RGBQUAD)
invoke PackedDibGetNumColors,pPackedDibPGCTS
mov ecx,sizeof (RGBQUAD)
mul ecx
ret
.endif
PackedDibGetColorTableSize endp

PackedDibGetColorTablePtr proc pPackedDibPGCTP:DWORD ;指向 BITMAPINFO 结构体的指针
invoke PackedDibGetNumColors,pPackedDibPGCTP
.if ( eax == 0)
xor eax,eax
ret
.endif
;return (RGBQUAD *) (((BYTE *)pPackedDib) + PackedDibGetInfoHeaderSize (pPackedDib))
invoke PackedDibGetInfoHeaderSize,pPackedDibPGCTP

mov esi,pPackedDibPGCTP
add eax,esi
ret
PackedDibGetColorTablePtr endp

PackedDibGetColorTableEntry proc pPackedDibPDGT:DWORD,i:DWORD
invoke PackedDibGetNumColors,pPackedDibPDGT
.if eax==0
xor eax,eax
.endif


mov esi,pPackedDibPDGT
mov eax,[esi]
.if eax==sizeof (BITMAPCOREHEADER)
invoke PackedDibGetColorTablePtr,pPackedDibPDGT
add eax,i
ret
.else
invoke PackedDibGetColorTablePtr,pPackedDibPDGT
add eax,i

ret
.endif
PackedDibGetColorTableEntry endp

; PackedDibGetBitsPtr finally!
PackedDibGetBitsPtr proc pPackedDibPDPP:DWORD ;BITMAPINFO *
invoke PackedDibGetInfoHeaderSize,pPackedDibPDPP
push eax
invoke PackedDibGetColorTableSize,pPackedDibPDPP
pop ebx
add eax,ebx
add eax,pPackedDibPDPP
ret
PackedDibGetBitsPtr endp

; PackedDibGetBitsSize can be calculated from the height and row length
; if it's not explicitly in the biSizeImage field
PackedDibGetBitsSize proc pPackedDibPGBS:DWORD ;BITMAPINFO *
mov esi,pPackedDibPGBS
mov eax,[esi]

mov ebx,[esi+20]
.if (eax!=sizeof (BITMAPCOREHEADER)) && (ebx!=0)
mov eax,ebx
ret
.endif

invoke PackedDibGetHeight,pPackedDibPGBS
push eax
invoke PackedDibGetRowLength,pPackedDibPGBS
pop ecx
mul ecx
ret
PackedDibGetBitsSize endp

; PackedDibCreatePalette creates logical palette from PackedDib
PackedDibCreatePalette proc pPackedDibPDCP:DWORD ;BITMAPINFO *
LOCAL hPalettePDCP:HPALETTE
LOCAL i, iNumColors:DWORD
LOCAL plp:DWORD ;指向 LOGPALETTE 结构体的指针
LOCAL prgb:DWORD ;指向 RGBQUAD 结构体的指针
;RGBQUAD STRUCT
; rgbBlue BYTE ?
; rgbGreen BYTE ?
; rgbRed BYTE ?
; rgbReserved BYTE ?
;RGBQUAD ENDS

invoke PackedDibGetNumColors,pPackedDibPDCP
mov iNumColors,eax
.if eax==0
mov eax,NULL
ret
.endif

mov eax,sizeof (LOGPALETTE)
mov ecx,iNumColors
dec ecx
mul ecx
mov ecx,sizeof (PALETTEENTRY)
mul ecx
invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,eax
mov plp,eax ;plp = malloc (sizeof (LOGPALETTE) * (iNumColors - 1) * sizeof (PALETTEENTRY))
mov esi,plp
mov WORD ptr [esi],300h ;plp->palVersion = 0x0300
mov eax,iNumColors
mov WORD ptr [esi+2],ax ;plp->palNumEntries = iNumColors

add esi,4
mov i,0
LoopII:
push esi
invoke PackedDibGetColorTableEntry,pPackedDibPDCP,i
mov prgb,eax



mov edi,prgb ;其实一样的
mov al,[edi]
pop esi
mov [esi],al
;invoke Beep,1000,100
mov al,[edi+1]

mov [esi+1],al

mov al,[edi+2]
mov [esi+2],al

mov BYTE ptr [esi+3],0
add esi,4
inc i

mov eax,i
.if eax<iNumColors
jmp LoopII
.endif


invoke CreatePalette,plp
mov hPalettePDCP,eax


invoke LocalFree,plp

mov eax,hPalettePDCP
ret
PackedDibCreatePalette endp

; 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.
CreateAllPurposePalette proc
LOCAL hPaletteCAPP:HPALETTE
LOCAL i, incr, R, G, B:DWORD
LOCAL plp:DWORD ;指向LOGPALETTE结构体的指针

;plp = malloc (sizeof (LOGPALETTE) + 246 * sizeof (PALETTEENTRY))
mov eax,sizeof (PALETTEENTRY)
mov ecx,246
mul ecx
add eax,sizeof (LOGPALETTE)

invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,eax
mov plp,eax
mov esi,plp
mov WORD ptr [esi],300h ;plp->palVersion = 0x0300
mov WORD ptr [esi+2],247 ;plp->palNumEntries = 247

;The following loop calculates 31 gray shades, but 3 of them
;will match the standard 20 colors
xor eax,eax
mov i,eax
mov G,eax
add esi,4
LoopIG:
mov ebx,G
mov [esi],bl
mov [esi+1],bl
mov [esi+2],bl
mov BYTE PTR [esi+3],0

.if incr==9
mov eax,8
.else
mov eax,9
.endif
mov incr,eax

add esi,4
mov eax,incr
add G,eax
inc i
.if G<0FFh
jmp LoopIG
.endif

; 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.
mov esi,plp
add esi,4
xor eax,eax
mov R,eax
LoopR:
xor eax,eax
mov G,eax

LoopG:
xor eax,eax
mov B,eax
LoopB:
mov eax,B
mov [esi],al
mov eax,G
mov [esi+1],al
mov eax,R
mov [esi+2],al
mov BYTE PTR [esi+3],0
add B,33h
inc i
.if B<0FFh
jmp LoopB
.endif

add G,33h
.if G<0FFh
jmp LoopG
.endif

add R,33h
.if R<0FFh
jmp LoopR
.endif

invoke CreatePalette,plp
mov hPaletteCAPP,eax

invoke LocalFree,plp
mov eax,hPaletteCAPP
ret
CreateAllPurposePalette endp

START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND

mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc

mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0

push hInst
pop wndclass.hInstance

invoke LoadIcon,NULL, IDI_APPLICATION
mov wndclass.hIcon,eax

invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax

invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX

lea eax,szAppName
mov wndclass.lpszMenuName,eax
mov wndclass.lpszClassName,eax

mov wndclass.hIconSm,0

invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif


invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
CTXT("Show DIB #4: All-Purpose Palette"),
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax

invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd

StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:

mov eax,msg.wParam
ret
WinMain endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL hdc:HDC
LOCAL ps :PAINTSTRUCT

.if uMsg == WM_CREATE
mov eax,sizeof (OPENFILENAME)
mov ofn.lStructSize,eax
mov eax,hwnd
mov ofn.hwndOwner,eax
mov eax,NULL
mov ofn.hInstance,eax
mov ofn.lpstrFilter,CTEXT("Bitmap Files (*.BMP)\0*.bmp\0 All Files (*.*)\0*.*\0\0")
mov eax,NULL
mov ofn.lpstrCustomFilter,eax
xor eax,eax
mov ofn.nMaxCustFilter,eax
mov ofn.nFilterIndex,eax
lea eax,szFileName
mov ofn.lpstrFile,eax ; Set in Open and Close functions
mov eax,MAX_PATH
mov ofn.nMaxFile,eax
mov eax,NULL
mov ofn.lpstrFileTitle,eax ;Set in Open and Close functions
mov eax,MAX_PATH
mov ofn.nMaxFileTitle,eax
mov eax,NULL
mov ofn.lpstrInitialDir,eax
mov ofn.lpstrTitle,eax
mov eax,0
mov ofn.Flags,eax ; Set in Open and Close functions
mov ofn.nFileOffset,ax
mov ofn.nFileExtension,ax
mov ofn.lpstrDefExt,CTEXT ("bmp")
mov eax,0
mov ofn.lCustData,eax
mov eax,NULL
mov ofn.lpfnHook,eax
mov ofn.lpTemplateName,eax

;Create the All-Purpose Palette
invoke CreateAllPurposePalette
mov hPalette,eax

xor eax,eax
ret
.elseif uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax

mov eax,lParam
shr eax,16
mov cyClient,eax

xor eax,eax
ret
.elseif uMsg == WM_COMMAND
mov eax,wParam
.if ax==IDM_FILE_OPEN
; Show the File Open dialog box
invoke GetOpenFileName,addr ofn
.if eax==0
xor eax,eax
ret
.endif
; If there's an existing packed DIB, free the memory
.if (pPackedDib!=0)
invoke LocalFree,pPackedDib
mov pPackedDib,NULL
.endif

; If there's an existing logical palette, delete it
.if (hPalette!=0)
invoke DeleteObject,hPalette
mov hPalette,NULL
.endif

; Load the packed DIB into memory
invoke LoadCursor,NULL, IDC_WAIT
invoke SetCursor,eax

invoke ShowCursor,TRUE
invoke PackedDibLoad,addr szFileName
mov pPackedDib,eax

invoke ShowCursor,FALSE
invoke LoadCursor,NULL, IDC_ARROW
invoke SetCursor,eax

.if pPackedDib==0
invoke MessageBox,hwnd, CTEXT ("Cannot load DIB file"),addr szAppName, 0
.endif
invoke InvalidateRect,hwnd, NULL, TRUE
xor eax,eax
ret
.endif

.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
.if pPackedDib!=0
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke PackedDibGetWidth,pPackedDib
mov ebx,eax
push ebx
invoke PackedDibGetHeight,pPackedDib
mov ecx,eax
push ecx
invoke PackedDibGetBitsPtr,pPackedDib
;push eax
;mov ecx,eax
;invoke wsprintf,addr szBuffer,CTXT("%x"),ecx
;invoke MessageBox,NULL,addr szBuffer,NULL,MB_APPLMODAL
;pop eax
pop ecx
pop ebx
invoke SetDIBitsToDevice,hdc, 0,0,
ebx,ecx,
0,0,0,ecx,
eax,
pPackedDib,
DIB_RGB_COLORS

.endif
invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_QUERYNEWPALETTE
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke InvalidateRect,hwnd, NULL, TRUE
invoke ReleaseDC,hwnd, hdc

mov eax,TRUE
ret
.elseif uMsg == WM_PALETTECHANGED
mov eax,wParam
.if (eax != hwnd)
invoke GetDC,hwnd
mov hdc,eax
.endif

invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke UpdateColors,hdc
invoke ReleaseDC,hwnd, hdc
.elseif uMsg == WM_DESTROY
.if pPackedDib!=0
invoke LocalFree,pPackedDib
.endif
invoke DeleteObject,hBitmap
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif
invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START

SHOWDIB4.RC (摘录)
        
//Microsoft Developer Studio generated resource script.
        
#include "resource.h"
        
#include "afxres.h"
        
/////////////////////////////////////////////////////////////////////////////
        
// Menu
        
SHOWDIB4 MENU DISCARDABLE
        
BEGIN
        
   POPUP "&Open"
        
   BEGIN
        
                          MENUITEM "&File",                    IDM_FILE_OPEN
        
   END
        
END
        
RESOURCE.H (摘录)
        
// Microsoft Developer Studio 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个项目.的确如此,不过这样做很方便.这些项目中有15个被复制或者映射到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种颜色,但是其中8种颜色复制了标准的20种保留颜色,而另外4个复制了前面计算的灰阶.如果将PALETTEENTRY结构的peFlags字段设为0,则Windows将不把复制的项目放进系统调色板.

显然地,实际的程序不希望计算16位、24位或者32位DIB的最佳调色板,程序将继续使用DIB颜色表来显示8位DIB.SHOWDIB4不完成这项工作,它只对每件事都使用通用调色板.因为SHOWDIB4是一个展示程序,而且您可以与SHOWDIB3显示的8位DIB进行比较.如果看一些人像的彩色DIB,那么您可能会得出这样的结论:SHOWDIB4没有足够的颜色来精确地表示鲜艳的色调.

如果用SHOWDIB4中的CreateAllPurposePalette函数来试验(可能是通过将逻辑调色板的大小减少到只有几个项目的方法),您将发现当调色板选进设备内容时,Windows将只使用调色板中的颜色,而不使用标准的20种颜色调色板的颜色.

中间色调色板

Windows API包括一个通用调色板,程序可以通过呼叫CreateHalftonePalette来获得该调色板.使用此调色板的方法与使用从SHOWDIB4中的CreateAllPurposePalette获得调色板的方法相同,或者您也可以与位图缩放模式中的HALFTONE设定-用SetStretchBltMode设定-一起使用.程序16-15所示的SHOWDIB5程序展示了使用中间色调色板的方法.

程序16-15  SHOWDIB5
        
SHOWDIB5.ASM
        
;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc
Include comdlg32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
IncludeLib comdlg32.lib

include macro.asm

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
IDM_FILE_OPEN equ 40001

.DATA
szAppName TCHAR "SHOWDIB5",0

.DATA?
hInstance HINSTANCE ?
hBitmap HBITMAP ?
pPackedDib DWORD ? ;指向 BITMAPINFO 结构体的指针
hPalette HPALETTE ?
cxClient DWORD ?
cyClient DWORD ?
ofn OPENFILENAME <?>
szFileName TCHAR MAX_PATH dup(?)
szTitleName TCHAR MAX_PATH dup(?)
szBuffer db 100 dup(?)

.CODE

; PackedDibLoad: Load DIB File as Packed-Dib Memory Block
PackedDibLoad proc szFN:PTSTR
LOCAL bmfh:BITMAPFILEHEADER
LOCAL pbmi:DWORD ;BITMAPINFO * pbmi ;
LOCAL bSuccess:BOOL
LOCAL dwPackedDibSize, dwBytesRead:DWORD
LOCAL hFile:HANDLE

; Open the file: read access, prohibit write access
invoke CreateFile,szFN, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL
mov hFile,eax
.if hFile == INVALID_HANDLE_VALUE
mov eax,NULL
ret
.endif

; Read in the BITMAPFILEHEADER
invoke ReadFile,hFile,addr bmfh, sizeof (BITMAPFILEHEADER),addr dwBytesRead, NULL
mov bSuccess,eax

mov eax,dwBytesRead
mov bx,bmfh.bfType
.if (bSuccess==0) || ( eax != sizeof (BITMAPFILEHEADER)) || ( ebx != "MB")
invoke CloseHandle,hFile
mov eax,NULL
ret
.endif


; Allocate memory for the packed DIB & read it in
mov eax,bmfh.bfSize
sub eax,sizeof (BITMAPFILEHEADER)
mov dwPackedDibSize,eax

invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,dwPackedDibSize
mov pbmi,eax

invoke ReadFile,hFile, pbmi, dwPackedDibSize,addr dwBytesRead, NULL
mov bSuccess,eax
invoke CloseHandle,hFile


mov eax,dwBytesRead
.if (bSuccess==0) || (eax != dwPackedDibSize)
invoke LocalFree,pbmi
mov eax,NULL
ret
.endif

mov eax,pbmi
ret
PackedDibLoad endp

; Functions to get information from packed DIB
PackedDibGetWidth proc pPackedDibPGW:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGW
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
;return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcWidth
mov bx,[esi+4]
xor eax,eax
mov ax,bx
ret
.else
;return pPackedDib->bmiHeader.biWidth
mov eax,[esi+4]
ret
.endif
PackedDibGetWidth endp

PackedDibGetHeight proc pPackedDibPGH:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGH
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
mov bx,[esi+6] ;return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcHeight
xor eax,eax
mov ax,bx
ret

ret
.else
;return abs (pPackedDib->bmiHeader.biHeight) ;
mov eax,[esi+8]
ret
.endif
PackedDibGetHeight endp

PackedDibGetBitCount proc pPackedDibPGB:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGB
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
mov bx,[esi+10] ; return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcBitCount
xor eax,eax
mov ax,bx
ret
.else
; return pPackedDib->bmiHeader.biBitCount
mov bx,[esi+14]
xor eax,eax
mov ax,bx
ret
.endif
PackedDibGetBitCount endp

PackedDibGetRowLength proc pPackedDibPGR:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGR
mov eax,[esi]
;return ((PackedDibGetWidth (pPackedDib)*PackedDibGetBitCount (pPackedDib) + 31) & ~31) >> 3
invoke PackedDibGetWidth,pPackedDibPGR
push eax
invoke PackedDibGetBitCount,pPackedDibPGR
mov ebx,eax
pop eax
add eax,ebx
add eax,31
mov ecx,31
neg ecx
and eax,ecx
shr eax,3
ret
PackedDibGetRowLength endp


PackedDibGetInfoHeaderSize proc pPackedDibPHS:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPHS
xor eax,eax
mov ax,[esi]
.if (ax == sizeof (BITMAPCOREHEADER))
;return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcSize
mov eax,[esi]
ret
.else
.if (ax == sizeof (BITMAPINFOHEADER))
;return pPackedDib->bmiHeader.biSize + (pPackedDib->bmiHeader.biCompression == BI_BITFIELDS ? 12 : 0) ;
mov eax,[esi+16]
.if eax== BI_BITFIELDS
mov ebx,12
.else
mov ebx,0
.endif
mov eax,[esi]
add eax,ebx
ret
.else
mov eax,[esi] ;return pPackedDib->bmiHeader.biSize ;
ret
.endif
.endif
PackedDibGetInfoHeaderSize endp

; PackedDibGetColorsUsed returns value in information header;
; could be 0 to indicate non-truncated color table!
PackedDibGetColorsUsed proc pPackedDibPGC:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGC
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
xor eax,eax
ret
.else ;return pPackedDib->bmiHeader.biClrUsed
mov eax,[esi+32]
ret
.endif
PackedDibGetColorsUsed endp

;PackedDibGetNumColors is actual number of entries in color table
PackedDibGetNumColors proc pPackedDibPGNC:DWORD ;指向 BITMAPINFO 结构体的指针
LOCAL iNumColors:DWORD
invoke PackedDibGetColorsUsed,pPackedDibPGNC
mov iNumColors,eax

invoke PackedDibGetBitCount,pPackedDibPGNC
.if (iNumColors == 0) && (eax < 16)
; iNumColors =1 << PackedDibGetBitCount (pPackedDib) ;
invoke PackedDibGetBitCount,pPackedDibPGNC
mov ecx,eax
mov eax,1
shl eax,cl
mov iNumColors,eax
.endif
mov eax,iNumColors
ret
PackedDibGetNumColors endp

PackedDibGetColorTableSize proc pPackedDibPGCTS:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGCTS
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
;return PackedDibGetNumColors (pPackedDib) * sizeof (RGBTRIPLE)
invoke PackedDibGetNumColors,pPackedDibPGCTS
mov ecx,sizeof (RGBTRIPLE)
mul ecx
ret
.else
;return PackedDibGetNumColors (pPackedDib) * sizeof (RGBQUAD)
invoke PackedDibGetNumColors,pPackedDibPGCTS
mov ecx,sizeof (RGBQUAD)
mul ecx
ret
.endif
PackedDibGetColorTableSize endp

PackedDibGetColorTablePtr proc pPackedDibPGCTP:DWORD ;指向 BITMAPINFO 结构体的指针
invoke PackedDibGetNumColors,pPackedDibPGCTP
.if ( eax == 0)
xor eax,eax
ret
.endif
;return (RGBQUAD *) (((BYTE *)pPackedDib) + PackedDibGetInfoHeaderSize (pPackedDib))
invoke PackedDibGetInfoHeaderSize,pPackedDibPGCTP


mov esi,pPackedDibPGCTP
add eax,esi
ret
PackedDibGetColorTablePtr endp

PackedDibGetColorTableEntry proc pPackedDibPDGT:DWORD,i:DWORD
invoke PackedDibGetNumColors,pPackedDibPDGT
.if eax==0
xor eax,eax
.endif


mov esi,pPackedDibPDGT
mov eax,[esi]
.if eax==sizeof (BITMAPCOREHEADER)
invoke PackedDibGetColorTablePtr,pPackedDibPDGT
add eax,i
ret
.else
invoke PackedDibGetColorTablePtr,pPackedDibPDGT
add eax,i

ret
.endif
PackedDibGetColorTableEntry endp

; PackedDibGetBitsPtr finally!
PackedDibGetBitsPtr proc pPackedDibPDPP:DWORD ;BITMAPINFO *
invoke PackedDibGetInfoHeaderSize,pPackedDibPDPP
push eax
invoke PackedDibGetColorTableSize,pPackedDibPDPP
pop ebx
add eax,ebx
add eax,pPackedDibPDPP
ret
PackedDibGetBitsPtr endp

; PackedDibGetBitsSize can be calculated from the height and row length
; if it's not explicitly in the biSizeImage field
PackedDibGetBitsSize proc pPackedDibPGBS:DWORD ;BITMAPINFO *
mov esi,pPackedDibPGBS
mov eax,[esi]

mov ebx,[esi+20]
.if (eax!=sizeof (BITMAPCOREHEADER)) && (ebx!=0)
mov eax,ebx
ret
.endif

invoke PackedDibGetHeight,pPackedDibPGBS
push eax
invoke PackedDibGetRowLength,pPackedDibPGBS
pop ecx
mul ecx
ret
PackedDibGetBitsSize endp

; PackedDibCreatePalette creates logical palette from PackedDib
PackedDibCreatePalette proc pPackedDibPDCP:DWORD ;BITMAPINFO *
LOCAL hPalettePDCP:HPALETTE
LOCAL i, iNumColors:DWORD
LOCAL plp:DWORD ;指向 LOGPALETTE 结构体的指针
LOCAL prgb:DWORD ;指向 RGBQUAD 结构体的指针
;RGBQUAD STRUCT
; rgbBlue BYTE ?
; rgbGreen BYTE ?
; rgbRed BYTE ?
; rgbReserved BYTE ?
;RGBQUAD ENDS

invoke PackedDibGetNumColors,pPackedDibPDCP
mov iNumColors,eax
.if eax==0
mov eax,NULL
ret
.endif

mov eax,sizeof (LOGPALETTE)
mov ecx,iNumColors
dec ecx
mul ecx
mov ecx,sizeof (PALETTEENTRY)
mul ecx
invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,eax
mov plp,eax ;plp = malloc (sizeof (LOGPALETTE) * (iNumColors - 1) * sizeof (PALETTEENTRY))
mov esi,plp
mov WORD ptr [esi],300h ;plp->palVersion = 0x0300
mov eax,iNumColors
mov WORD ptr [esi+2],ax ;plp->palNumEntries = iNumColors

add esi,4
mov i,0
LoopII:
push esi
invoke PackedDibGetColorTableEntry,pPackedDibPDCP,i
mov prgb,eax



mov edi,prgb ;其实一样的
mov al,[edi]
pop esi
mov [esi],al
;invoke Beep,1000,100
mov al,[edi+1]

mov [esi+1],al

mov al,[edi+2]
mov [esi+2],al

mov BYTE ptr [esi+3],0
add esi,4
inc i

mov eax,i
.if eax<iNumColors
jmp LoopII
.endif


invoke CreatePalette,plp
mov hPalettePDCP,eax


invoke LocalFree,plp

mov eax,hPalettePDCP
ret
PackedDibCreatePalette endp

START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND

mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc

mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0

push hInst
pop wndclass.hInstance

invoke LoadIcon,NULL, IDI_APPLICATION
mov wndclass.hIcon,eax

invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax

invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX

lea eax,szAppName
mov wndclass.lpszMenuName,eax
mov wndclass.lpszClassName,eax

mov wndclass.hIconSm,0

invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif


invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
CTXT("Show DIB #5: Halftone Palette"),
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax

invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd

StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:

mov eax,msg.wParam
ret
WinMain endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL hdc:HDC
LOCAL ps :PAINTSTRUCT

.if uMsg == WM_CREATE
mov eax,sizeof (OPENFILENAME)
mov ofn.lStructSize,eax
mov eax,hwnd
mov ofn.hwndOwner,eax
mov eax,NULL
mov ofn.hInstance,eax
mov ofn.lpstrFilter,CTEXT("Bitmap Files (*.BMP)\0*.bmp\0 All Files (*.*)\0*.*\0\0")
mov eax,NULL
mov ofn.lpstrCustomFilter,eax
xor eax,eax
mov ofn.nMaxCustFilter,eax
mov ofn.nFilterIndex,eax
lea eax,szFileName
mov ofn.lpstrFile,eax ; Set in Open and Close functions
mov eax,MAX_PATH
mov ofn.nMaxFile,eax
mov eax,NULL
mov ofn.lpstrFileTitle,eax ;Set in Open and Close functions
mov eax,MAX_PATH
mov ofn.nMaxFileTitle,eax
mov eax,NULL
mov ofn.lpstrInitialDir,eax
mov ofn.lpstrTitle,eax
mov eax,0
mov ofn.Flags,eax ; Set in Open and Close functions
mov ofn.nFileOffset,ax
mov ofn.nFileExtension,ax
mov ofn.lpstrDefExt,CTEXT ("bmp")
mov eax,0
mov ofn.lCustData,eax
mov eax,NULL
mov ofn.lpfnHook,eax
mov ofn.lpTemplateName,eax

; Create the All-Purpose Palette
invoke GetDC,hwnd
mov hdc,eax

invoke CreateHalftonePalette,hdc
mov hPalette,eax

invoke ReleaseDC,hwnd,hdc

xor eax,eax
ret
.elseif uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax

mov eax,lParam
shr eax,16
mov cyClient,eax

xor eax,eax
ret
.elseif uMsg == WM_COMMAND
mov eax,wParam
.if ax==IDM_FILE_OPEN
; Show the File Open dialog box
invoke GetOpenFileName,addr ofn
.if eax==0
xor eax,eax
ret
.endif
; If there's an existing packed DIB, free the memory
.if (pPackedDib!=0)
invoke LocalFree,pPackedDib
mov pPackedDib,NULL
.endif

; Load the packed DIB into memory
invoke LoadCursor,NULL, IDC_WAIT
invoke SetCursor,eax

invoke ShowCursor,TRUE
invoke PackedDibLoad,addr szFileName
mov pPackedDib,eax

invoke ShowCursor,FALSE
invoke LoadCursor,NULL, IDC_ARROW
invoke SetCursor,eax

.if pPackedDib==0
invoke MessageBox,hwnd, CTEXT ("Cannot load DIB file"),addr szAppName, 0
.endif
invoke InvalidateRect,hwnd, NULL, TRUE
xor eax,eax
ret
.endif

.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
.if hPalette!=0
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
.endif

.if pPackedDib!=0
; Set halftone stretch mode
invoke SetStretchBltMode,hdc, HALFTONE
invoke SetBrushOrgEx,hdc, 0, 0, NULL

; Select and realize halftone palette

invoke SelectPalette ,hdc, hPalette, FALSE
invoke RealizePalette,hdc

; StretchDIBits rather than SetDIBitsToDevice

invoke PackedDibGetWidth,pPackedDib
mov ebx,eax
push ebx
invoke PackedDibGetHeight,pPackedDib
mov ecx,eax
push ecx
invoke PackedDibGetBitsPtr,pPackedDib
;push eax
;mov ecx,eax
;invoke wsprintf,addr szBuffer,CTXT("%x"),ecx
;invoke MessageBox,NULL,addr szBuffer,NULL,MB_APPLMODAL
;pop eax
pop ecx
pop ebx
invoke StretchDIBits,hdc, 0,0,
ebx,ecx,
0,0,
ebx,ecx,
eax,
pPackedDib,
DIB_RGB_COLORS,
SRCCOPY

.endif
invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_QUERYNEWPALETTE
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke InvalidateRect,hwnd, NULL, TRUE
invoke ReleaseDC,hwnd, hdc

mov eax,TRUE
ret
.elseif uMsg == WM_PALETTECHANGED
mov eax,wParam
.if (eax != hwnd)
invoke GetDC,hwnd
mov hdc,eax
.endif

invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke UpdateColors,hdc
invoke ReleaseDC,hwnd, hdc
.elseif uMsg == WM_DESTROY
.if pPackedDib!=0
invoke LocalFree,pPackedDib
.endif
invoke DeleteObject,hBitmap
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif
WM_PALETTECHANGED_END:
invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START

SHOWDIB5.RC (摘录)
        
//Microsoft Developer Studio generated resource script.
        
#include "resource.h"
        
#include "afxres.h"
        
/////////////////////////////////////////////////////////////////////////////
        
// Menu
        
SHOWDIB5 MENU DISCARDABLE
        
BEGIN
        
   POPUP "&Open"
        
 BEGIN
        
                          MENUITEM "&File",             IDM_FILE_OPEN
        
   END
        
END
        
RESOURCE.H (摘录)
        
// Microsoft Developer Studio generated include file.
        
// Used by ShowDib5.rc
        
#define IDM_FILE_OPEN            40001
        

SHOWDIB5程序类似于SHOWDIB4,SHOWDIB4中不使用DIB中的颜色表,而使用适用于图像范围更大的调色板.为此,SHOWDIB5使用了由Windows支持的逻辑调色板,其句柄可以从CreateHalftonePalette函数获得.

中间色调色板并不比SHOWDIB4中的CreateAllPurposePalette函数所建立的调色板更复杂.的确,如果只是拿来自用,结果是相似的.然而,如果您呼叫下面两个函数:

SetStretchBltMode (hdc, HALFTONE) ;
        
                                  SetBrushOrgEx (hdc, x, y, NULL) ;
        

其中,x和y是DIB左上角的设备坐标,并且如果您用StretchDIBits而不是SetDIBitsToDevice来显示DIB,那么结果会让您吃惊:颜色色调要比不设定位图缩放模式来使用CreateAllPurposePalette或者CreateHalftonePalette更精确.Windows使用一种混色图案来处理中间色调色板上的颜色,以使其更接近8位显示卡上原始图像的颜色.与您所想象的一样,这样做的缺点是需要更多的处理时间.

索引调色板颜色

现在开始处理SetDIBitsToDevice、StretchDIBits、CreateDIBitmap、SetDIBits、GetDIBits和CreateDIBSection的fClrUse参数.通常,您将这个参数设定为DIB_RGB_COLORS(等于0).不过,您也能将它设定为DIB_PAL_COLORS.在这种情况下,假定BITMAPINFO结构中的颜色表不包括RGB颜色值,而是包括逻辑调色板中颜色项目的16位索引.逻辑调色板是作为第一个参数传递给函数的设备内容中目前选择的那个.实际上,在CreateDIBSection中,之所以需要指定一个非NULL的设备内容句柄作为第一个参数,只是因为使用了DIB_PAL_COLORS.

DIB_PAL_COLORS能为您做些什么呢?它可能提高一些性能.考虑一下在8位显示模式下呼叫SetDIBitsToDevice显示的8位DIB.Windows首先必须在DIB颜色表的所有颜色中搜索与设备可用颜色最接近的颜色.然后设定一个小表,以便将DIB像素值映像到设备像素.也就是说,最多需要搜索256次最接近的颜色.但是如果DIB颜色表中含有从设备内容中选择颜色的逻辑调色板项目索引,那么就可能跳过搜索.

除了使用调色板索引以外,程序16-16所示的SHOWDIB6程序与SHOWDIB3相似.

程序16-16 SHOWDIB6
        
SHOWDIB6.ASM
;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc
Include comdlg32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
IncludeLib comdlg32.lib

include macro.asm

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
IDM_FILE_OPEN equ 40001

.DATA
szAppName TCHAR "SHOWDIB6",0

.DATA?
hInstance HINSTANCE ?
pPackedDib DWORD ? ;指向 BITMAPINFO 结构体的指针
hPalette HPALETTE ?
cxClient DWORD ?
cyClient DWORD ?
ofn OPENFILENAME <?>
szFileName TCHAR MAX_PATH dup(?)
szTitleName TCHAR MAX_PATH dup(?)
szBuffer db 100 dup(?)

.CODE

; PackedDibLoad: Load DIB File as Packed-Dib Memory Block
PackedDibLoad proc szFN:PTSTR
LOCAL bmfh:BITMAPFILEHEADER
LOCAL pbmi:DWORD ;BITMAPINFO * pbmi ;
LOCAL bSuccess:BOOL
LOCAL dwPackedDibSize, dwBytesRead:DWORD
LOCAL hFile:HANDLE

; Open the file: read access, prohibit write access
invoke CreateFile,szFN, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL
mov hFile,eax
.if hFile == INVALID_HANDLE_VALUE
mov eax,NULL
ret
.endif

; Read in the BITMAPFILEHEADER
invoke ReadFile,hFile,addr bmfh, sizeof (BITMAPFILEHEADER),addr dwBytesRead, NULL
mov bSuccess,eax

mov eax,dwBytesRead
mov bx,bmfh.bfType
.if (bSuccess==0) || ( eax != sizeof (BITMAPFILEHEADER)) || ( ebx != "MB")
invoke CloseHandle,hFile
mov eax,NULL
ret
.endif


; Allocate memory for the packed DIB & read it in
mov eax,bmfh.bfSize
sub eax,sizeof (BITMAPFILEHEADER)
mov dwPackedDibSize,eax

invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,dwPackedDibSize
mov pbmi,eax

invoke ReadFile,hFile, pbmi, dwPackedDibSize,addr dwBytesRead, NULL
mov bSuccess,eax
invoke CloseHandle,hFile


mov eax,dwBytesRead
.if (bSuccess==0) || (eax != dwPackedDibSize)
invoke LocalFree,pbmi
mov eax,NULL
ret
.endif

mov eax,pbmi
ret
PackedDibLoad endp

; Functions to get information from packed DIB
PackedDibGetWidth proc pPackedDibPGW:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGW
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
;return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcWidth
mov bx,[esi+4]
xor eax,eax
mov ax,bx
ret
.else
;return pPackedDib->bmiHeader.biWidth
mov eax,[esi+4]
ret
.endif
PackedDibGetWidth endp

PackedDibGetHeight proc pPackedDibPGH:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGH
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
mov bx,[esi+6] ;return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcHeight
xor eax,eax
mov ax,bx
ret

ret
.else
;return abs (pPackedDib->bmiHeader.biHeight) ;
mov eax,[esi+8]
ret
.endif
PackedDibGetHeight endp

PackedDibGetBitCount proc pPackedDibPGB:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGB
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
mov bx,[esi+10] ; return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcBitCount
xor eax,eax
mov ax,bx
ret
.else
; return pPackedDib->bmiHeader.biBitCount
mov bx,[esi+14]
xor eax,eax
mov ax,bx
ret
.endif
PackedDibGetBitCount endp

PackedDibGetRowLength proc pPackedDibPGR:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGR
mov eax,[esi]
;return ((PackedDibGetWidth (pPackedDib)*PackedDibGetBitCount (pPackedDib) + 31) & ~31) >> 3
invoke PackedDibGetWidth,pPackedDibPGR
push eax
invoke PackedDibGetBitCount,pPackedDibPGR
mov ebx,eax
pop eax
add eax,ebx
add eax,31
mov ecx,31
neg ecx
and eax,ecx
shr eax,3
ret
PackedDibGetRowLength endp


PackedDibGetInfoHeaderSize proc pPackedDibPHS:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPHS
xor eax,eax
mov ax,[esi]
.if (ax == sizeof (BITMAPCOREHEADER))
;return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcSize
mov eax,[esi]
ret
.else
.if (ax == sizeof (BITMAPINFOHEADER))
;return pPackedDib->bmiHeader.biSize + (pPackedDib->bmiHeader.biCompression == BI_BITFIELDS ? 12 : 0) ;
mov eax,[esi+16]
.if eax== BI_BITFIELDS
mov ebx,12
.else
mov ebx,0
.endif
mov eax,[esi]
add eax,ebx
ret
.else
mov eax,[esi] ;return pPackedDib->bmiHeader.biSize ;
ret
.endif
.endif
PackedDibGetInfoHeaderSize endp

; PackedDibGetColorsUsed returns value in information header;
; could be 0 to indicate non-truncated color table!
PackedDibGetColorsUsed proc pPackedDibPGC:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGC
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
xor eax,eax
ret
.else ;return pPackedDib->bmiHeader.biClrUsed
mov eax,[esi+32]
ret
.endif
PackedDibGetColorsUsed endp

;PackedDibGetNumColors is actual number of entries in color table
PackedDibGetNumColors proc pPackedDibPGNC:DWORD ;指向 BITMAPINFO 结构体的指针
LOCAL iNumColors:DWORD
invoke PackedDibGetColorsUsed,pPackedDibPGNC
mov iNumColors,eax

invoke PackedDibGetBitCount,pPackedDibPGNC
.if (iNumColors == 0) && (eax < 16)
; iNumColors =1 << PackedDibGetBitCount (pPackedDib) ;
invoke PackedDibGetBitCount,pPackedDibPGNC
mov ecx,eax
mov eax,1
shl eax,cl
mov iNumColors,eax
.endif
mov eax,iNumColors
ret
PackedDibGetNumColors endp

PackedDibGetColorTableSize proc pPackedDibPGCTS:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGCTS
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
;return PackedDibGetNumColors (pPackedDib) * sizeof (RGBTRIPLE)
invoke PackedDibGetNumColors,pPackedDibPGCTS
mov ecx,sizeof (RGBTRIPLE)
mul ecx
ret
.else
;return PackedDibGetNumColors (pPackedDib) * sizeof (RGBQUAD)
invoke PackedDibGetNumColors,pPackedDibPGCTS
mov ecx,sizeof (RGBQUAD)
mul ecx
ret
.endif
PackedDibGetColorTableSize endp

PackedDibGetColorTablePtr proc pPackedDibPGCTP:DWORD ;指向 BITMAPINFO 结构体的指针
invoke PackedDibGetNumColors,pPackedDibPGCTP
.if ( eax == 0)
xor eax,eax
ret
.endif
;return (RGBQUAD *) (((BYTE *)pPackedDib) + PackedDibGetInfoHeaderSize (pPackedDib))
invoke PackedDibGetInfoHeaderSize,pPackedDibPGCTP
mov esi,pPackedDibPGCTP
add eax,esi
ret
PackedDibGetColorTablePtr endp

PackedDibGetColorTableEntry proc pPackedDibPDGT:DWORD,i:DWORD
invoke PackedDibGetNumColors,pPackedDibPDGT
.if eax==0
xor eax,eax
.endif


mov esi,pPackedDibPDGT
mov eax,[esi]
.if eax==sizeof (BITMAPCOREHEADER)
invoke PackedDibGetColorTablePtr,pPackedDibPDGT
mov ebx,i
shl ebx,2
add eax,ebx
ret
.else
invoke PackedDibGetColorTablePtr,pPackedDibPDGT
mov ebx,i
shl ebx,2
add eax,ebx

ret
.endif
PackedDibGetColorTableEntry endp

; PackedDibGetBitsPtr finally!
PackedDibGetBitsPtr proc pPackedDibPDPP:DWORD ;BITMAPINFO *
invoke PackedDibGetInfoHeaderSize,pPackedDibPDPP
push eax
invoke PackedDibGetColorTableSize,pPackedDibPDPP
pop ebx
add eax,ebx
add eax,pPackedDibPDPP
ret
PackedDibGetBitsPtr endp

; PackedDibGetBitsSize can be calculated from the height and row length
; if it's not explicitly in the biSizeImage field
PackedDibGetBitsSize proc pPackedDibPGBS:DWORD ;BITMAPINFO *
mov esi,pPackedDibPGBS
mov eax,[esi]

mov ebx,[esi+20]
.if (eax!=sizeof (BITMAPCOREHEADER)) && (ebx!=0)
mov eax,ebx
ret
.endif

invoke PackedDibGetHeight,pPackedDibPGBS
push eax
invoke PackedDibGetRowLength,pPackedDibPGBS
pop ecx
mul ecx
ret
PackedDibGetBitsSize endp

; PackedDibCreatePalette creates logical palette from PackedDib
PackedDibCreatePalette proc pPackedDibPDCP:DWORD ;BITMAPINFO *
LOCAL hPalettePDCP:HPALETTE
LOCAL i, iNumColors:DWORD
LOCAL plp:DWORD ;指向 LOGPALETTE 结构体的指针
LOCAL prgb:DWORD ;指向 RGBQUAD 结构体的指针
;RGBQUAD STRUCT
; rgbBlue BYTE ?
; rgbGreen BYTE ?
; rgbRed BYTE ?
; rgbReserved BYTE ?
;RGBQUAD ENDS

invoke PackedDibGetNumColors,pPackedDibPDCP
mov iNumColors,eax
.if eax==0
mov eax,NULL
ret
.endif

mov eax,sizeof (LOGPALETTE)
mov ecx,iNumColors
dec ecx
mul ecx
mov ecx,sizeof (PALETTEENTRY)
mul ecx
invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,eax
mov plp,eax ;plp = malloc (sizeof (LOGPALETTE) * (iNumColors - 1) * sizeof (PALETTEENTRY))
mov esi,plp
mov WORD ptr [esi],300h ;plp->palVersion = 0x0300
mov eax,iNumColors
mov WORD ptr [esi+2],ax ;plp->palNumEntries = iNumColors

add esi,4
mov i,0
LoopII:
push esi
invoke PackedDibGetColorTableEntry,pPackedDibPDCP,i
mov prgb,eax

mov edi,prgb ;其实一样的
mov al,[edi]
pop esi
mov [esi],al
mov al,[edi+1]
mov [esi+1],al
mov al,[edi+2]
mov [esi+2],al

mov BYTE ptr [esi+3],0
add esi,4
inc i

mov eax,i
.if eax<iNumColors
jmp LoopII
.endif

invoke CreatePalette,plp
mov hPalettePDCP,eax

invoke LocalFree,plp
mov eax,hPalettePDCP
ret
PackedDibCreatePalette endp


START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND

mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc

mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0

push hInst
pop wndclass.hInstance

invoke LoadIcon,NULL, IDI_APPLICATION
mov wndclass.hIcon,eax

invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax

invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX

lea eax,szAppName
mov wndclass.lpszMenuName,eax
mov wndclass.lpszClassName,eax

mov wndclass.hIconSm,0

invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif


invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
CTXT("Show DIB #6: Palette Indices"),
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax

invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd

StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:

mov eax,msg.wParam
ret
WinMain endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL hdc:HDC
LOCAL ps :PAINTSTRUCT
LOCAL i, iNumColors:DWORD
LOCAL pwIndex:DWORD ;指向WORD的指针

.if uMsg == WM_CREATE
mov eax,sizeof (OPENFILENAME)
mov ofn.lStructSize,eax
mov eax,hwnd
mov ofn.hwndOwner,eax
mov eax,NULL
mov ofn.hInstance,eax
mov ofn.lpstrFilter,CTEXT("Bitmap Files (*.BMP)\0*.bmp\0 All Files (*.*)\0*.*\0\0")
mov eax,NULL
mov ofn.lpstrCustomFilter,eax
xor eax,eax
mov ofn.nMaxCustFilter,eax
mov ofn.nFilterIndex,eax
lea eax,szFileName
mov ofn.lpstrFile,eax ; Set in Open and Close functions
mov eax,MAX_PATH
mov ofn.nMaxFile,eax
mov eax,NULL
mov ofn.lpstrFileTitle,eax ;Set in Open and Close functions
mov eax,MAX_PATH
mov ofn.nMaxFileTitle,eax
mov eax,NULL
mov ofn.lpstrInitialDir,eax
mov ofn.lpstrTitle,eax
mov eax,0
mov ofn.Flags,eax ; Set in Open and Close functions
mov ofn.nFileOffset,ax
mov ofn.nFileExtension,ax
mov ofn.lpstrDefExt,CTEXT ("bmp")
mov eax,0
mov ofn.lCustData,eax
mov eax,NULL
mov ofn.lpfnHook,eax
mov ofn.lpTemplateName,eax

xor eax,eax
ret
.elseif uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax

mov eax,lParam
shr eax,16
mov cyClient,eax

xor eax,eax
ret
.elseif uMsg == WM_COMMAND
mov eax,wParam
.if ax==IDM_FILE_OPEN
; Show the File Open dialog box
invoke GetOpenFileName,addr ofn
.if eax==0
xor eax,eax
ret
.endif
; If there's an existing packed DIB, free the memory
.if (pPackedDib!=0)
invoke LocalFree,pPackedDib
mov pPackedDib,NULL
.endif

; If there's an existing logical palette, delete it
.if (hPalette!=0)
invoke DeleteObject,hPalette
mov hPalette,NULL
.endif

; Load the packed DIB into memory
invoke LoadCursor,NULL, IDC_WAIT
invoke SetCursor,eax
invoke ShowCursor,TRUE
invoke PackedDibLoad,addr szFileName
mov pPackedDib,eax

invoke ShowCursor,FALSE
invoke LoadCursor,NULL, IDC_ARROW
invoke SetCursor,eax

.if pPackedDib!=0
; Create the palette from the DIB color table
invoke PackedDibCreatePalette,pPackedDib
mov hPalette,eax ;hPalette = PackedDibCreatePalette (pPackedDib)
.else
invoke MessageBox,hwnd, CTEXT ("Cannot load DIB file"),addr szAppName, 0
.endif
invoke InvalidateRect,hwnd, NULL, TRUE
xor eax,eax
ret
.endif

.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
.if hPalette!=0
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
.endif

.if pPackedDib!=0
invoke PackedDibGetWidth,pPackedDib
mov ebx,eax
push ebx
invoke PackedDibGetHeight,pPackedDib
mov ecx,eax
push ecx
invoke PackedDibGetBitsPtr,pPackedDib
pop ecx
pop ebx
invoke SetDIBitsToDevice,hdc, 0,0,
ebx,ecx,
0,0,0,ecx,
eax,
pPackedDib,
DIB_PAL_COLORS

.endif
invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_QUERYNEWPALETTE
.if hPalette==0
mov eax,FALSE
ret
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke InvalidateRect,hwnd, NULL, TRUE
invoke ReleaseDC,hwnd, hdc

mov eax,TRUE
ret
.elseif uMsg == WM_PALETTECHANGED
mov eax,wParam
.if (hPalette==0) || (eax == hwnd)
jmp WM_PALETTECHANGED_END
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke UpdateColors,hdc
invoke ReleaseDC,hwnd, hdc
.elseif uMsg == WM_DESTROY
.if pPackedDib!=0
invoke LocalFree,pPackedDib
.endif
.if hPalette!=0
invoke DeleteObject,hPalette
.endif
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif
WM_PALETTECHANGED_END:
invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START

SHOWDIB6.RC (摘录)
        
//Microsoft Developer Studio generated resource script.
        
#include "resource.h"
        
#include "afxres.h"
        
/////////////////////////////////////////////////////////////////////////////
        
// Menu
        
SHOWDIB6 MENU DISCARDABLE
        
BEGIN
        
   POPUP "&File"
        
   BEGIN
        
                          MENUITEM "&Open",                            IDM_FILE_OPEN
        
   END
        
END
        
RESOURCE.H (摘录)
        
// Microsoft Developer Studio generated include file.
        
// Used by ShowDib6.rc
        
//
        
#define IDM_FILE_OPEN           40001
        

SHOWDIB6将DIB加载到内存并由此建立了调色板以后,SHOWDIB6简单地用以0开始的WORD索引替换了DIB颜色表中的颜色.PackedDibGetNumColors函数将表示有多少种颜色,而PackedDibGetColorTablePtr函数传回指向DIB颜色表起始位置的指针.

注意,只有直接从DIB颜色表来建立调色板时,此技术才可行.如果使用通用调色板,则必须搜索最接近的颜色,以获得放入DIB的索引.

如果要使用调色板索引,那么请在将DIB储存到磁盘之前,确实替换掉DIB中的颜色表.另外,不要将包含调色板索引的DIB放入剪贴簿.实际上,在显示之前,将调色板索引放入DIB,然后将RGB颜色值放回,会更安全一些.

调色板和位图对象

程序16-17中的SHOWDIB7程序显示了如何使用与DIB相关联的调色板,这些DIB是使用CreateDIBitmap函数转换成GDI位图对象的.

程序16-17 SHOWDIB7
        
SHOWDIB7.ASM


;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc
Include comdlg32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
IncludeLib comdlg32.lib

include macro.asm

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
IDM_FILE_OPEN equ 40001

.DATA
szAppName TCHAR "SHOWDIB7",0

.DATA?
hBitmap HBITMAP ?
hPalette HPALETTE ?
cxClient DWORD ?
cyClient DWORD ?
ofn OPENFILENAME <?>
szFileName TCHAR MAX_PATH dup(?)
szTitleName TCHAR MAX_PATH dup(?)
hInstance HINSTANCE ?
szBuffer db 100 dup(?)

.CODE

; PackedDibLoad: Load DIB File as Packed-Dib Memory Block
PackedDibLoad proc szFN:PTSTR
LOCAL bmfh:BITMAPFILEHEADER
LOCAL pbmi:DWORD ;BITMAPINFO * pbmi ;
LOCAL bSuccess:BOOL
LOCAL dwPackedDibSize, dwBytesRead:DWORD
LOCAL hFile:HANDLE

; Open the file: read access, prohibit write access
invoke CreateFile,szFN, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL
mov hFile,eax
.if hFile == INVALID_HANDLE_VALUE
mov eax,NULL
ret
.endif

; Read in the BITMAPFILEHEADER
invoke ReadFile,hFile,addr bmfh, sizeof (BITMAPFILEHEADER),addr dwBytesRead, NULL
mov bSuccess,eax

mov eax,dwBytesRead
mov bx,bmfh.bfType
.if (bSuccess==0) || ( eax != sizeof (BITMAPFILEHEADER)) || ( ebx != "MB")
invoke CloseHandle,hFile
mov eax,NULL
ret
.endif


; Allocate memory for the packed DIB & read it in
mov eax,bmfh.bfSize
sub eax,sizeof (BITMAPFILEHEADER)
mov dwPackedDibSize,eax

invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,dwPackedDibSize
mov pbmi,eax

invoke ReadFile,hFile, pbmi, dwPackedDibSize,addr dwBytesRead, NULL
mov bSuccess,eax
invoke CloseHandle,hFile


mov eax,dwBytesRead
.if (bSuccess==0) || (eax != dwPackedDibSize)
invoke LocalFree,pbmi
mov eax,NULL
ret
.endif

mov eax,pbmi
ret
PackedDibLoad endp

; Functions to get information from packed DIB
PackedDibGetWidth proc pPackedDibPGW:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGW
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
;return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcWidth
mov bx,[esi+4]
xor eax,eax
mov ax,bx
ret
.else
;return pPackedDib->bmiHeader.biWidth
mov eax,[esi+4]
ret
.endif
PackedDibGetWidth endp

PackedDibGetHeight proc pPackedDibPGH:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGH
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
mov bx,[esi+6] ;return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcHeight
xor eax,eax
mov ax,bx
ret

ret
.else
;return abs (pPackedDib->bmiHeader.biHeight) ;
mov eax,[esi+8]
ret
.endif
PackedDibGetHeight endp

PackedDibGetBitCount proc pPackedDibPGB:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGB
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
mov bx,[esi+10] ; return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcBitCount
xor eax,eax
mov ax,bx
ret
.else
; return pPackedDib->bmiHeader.biBitCount
mov bx,[esi+14]
xor eax,eax
mov ax,bx
ret
.endif
PackedDibGetBitCount endp

PackedDibGetRowLength proc pPackedDibPGR:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGR
mov eax,[esi]
;return ((PackedDibGetWidth (pPackedDib)*PackedDibGetBitCount (pPackedDib) + 31) & ~31) >> 3
invoke PackedDibGetWidth,pPackedDibPGR
push eax
invoke PackedDibGetBitCount,pPackedDibPGR
mov ebx,eax
pop eax
add eax,ebx
add eax,31
mov ecx,31
neg ecx
and eax,ecx
shr eax,3
ret
PackedDibGetRowLength endp


PackedDibGetInfoHeaderSize proc pPackedDibPHS:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPHS
xor eax,eax
mov ax,[esi]
.if (ax == sizeof (BITMAPCOREHEADER))
;return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcSize
mov eax,[esi]
ret
.else
.if (ax == sizeof (BITMAPINFOHEADER))
;return pPackedDib->bmiHeader.biSize + (pPackedDib->bmiHeader.biCompression == BI_BITFIELDS ? 12 : 0) ;
mov eax,[esi+16]
.if eax== BI_BITFIELDS
mov ebx,12
.else
mov ebx,0
.endif
mov eax,[esi]
add eax,ebx
ret
.else
mov eax,[esi] ;return pPackedDib->bmiHeader.biSize ;
ret
.endif
.endif
PackedDibGetInfoHeaderSize endp

; PackedDibGetColorsUsed returns value in information header;
; could be 0 to indicate non-truncated color table!
PackedDibGetColorsUsed proc pPackedDibPGC:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGC
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
xor eax,eax
ret
.else ;return pPackedDib->bmiHeader.biClrUsed
mov eax,[esi+32]
ret
.endif
PackedDibGetColorsUsed endp

;PackedDibGetNumColors is actual number of entries in color table
PackedDibGetNumColors proc pPackedDibPGNC:DWORD ;指向 BITMAPINFO 结构体的指针
LOCAL iNumColors:DWORD
invoke PackedDibGetColorsUsed,pPackedDibPGNC
mov iNumColors,eax

invoke PackedDibGetBitCount,pPackedDibPGNC
.if (iNumColors == 0) && (eax < 16)
; iNumColors =1 << PackedDibGetBitCount (pPackedDib) ;
invoke PackedDibGetBitCount,pPackedDibPGNC
mov ecx,eax
mov eax,1
shl eax,cl
mov iNumColors,eax
.endif
mov eax,iNumColors
ret
PackedDibGetNumColors endp

PackedDibGetColorTableSize proc pPackedDibPGCTS:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGCTS
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
;return PackedDibGetNumColors (pPackedDib) * sizeof (RGBTRIPLE)
invoke PackedDibGetNumColors,pPackedDibPGCTS
mov ecx,sizeof (RGBTRIPLE)
mul ecx
ret
.else
;return PackedDibGetNumColors (pPackedDib) * sizeof (RGBQUAD)
invoke PackedDibGetNumColors,pPackedDibPGCTS
mov ecx,sizeof (RGBQUAD)
mul ecx
ret
.endif
PackedDibGetColorTableSize endp

PackedDibGetColorTablePtr proc pPackedDibPGCTP:DWORD ;指向 BITMAPINFO 结构体的指针
invoke PackedDibGetNumColors,pPackedDibPGCTP
.if ( eax == 0)
xor eax,eax
ret
.endif
;return (RGBQUAD *) (((BYTE *)pPackedDib) + PackedDibGetInfoHeaderSize (pPackedDib))
invoke PackedDibGetInfoHeaderSize,pPackedDibPGCTP
mov esi,pPackedDibPGCTP
add eax,esi
ret
PackedDibGetColorTablePtr endp

PackedDibGetColorTableEntry proc pPackedDibPDGT:DWORD,i:DWORD
invoke PackedDibGetNumColors,pPackedDibPDGT
.if eax==0
xor eax,eax
.endif


mov esi,pPackedDibPDGT
mov eax,[esi]
.if eax==sizeof (BITMAPCOREHEADER)
invoke PackedDibGetColorTablePtr,pPackedDibPDGT
add eax,i
ret
.else
invoke PackedDibGetColorTablePtr,pPackedDibPDGT
add eax,i

ret
.endif
PackedDibGetColorTableEntry endp

; PackedDibGetBitsPtr finally!
PackedDibGetBitsPtr proc pPackedDibPDPP:DWORD ;BITMAPINFO *
invoke PackedDibGetInfoHeaderSize,pPackedDibPDPP
push eax
invoke PackedDibGetColorTableSize,pPackedDibPDPP
pop ebx
add eax,ebx
add eax,pPackedDibPDPP
ret
PackedDibGetBitsPtr endp

; PackedDibGetBitsSize can be calculated from the height and row length
; if it's not explicitly in the biSizeImage field
PackedDibGetBitsSize proc pPackedDibPGBS:DWORD ;BITMAPINFO *
mov esi,pPackedDibPGBS
mov eax,[esi]

mov ebx,[esi+20]
.if (eax!=sizeof (BITMAPCOREHEADER)) && (ebx!=0)
mov eax,ebx
ret
.endif

invoke PackedDibGetHeight,pPackedDibPGBS
push eax
invoke PackedDibGetRowLength,pPackedDibPGBS
pop ecx
mul ecx
ret
PackedDibGetBitsSize endp

; PackedDibCreatePalette creates logical palette from PackedDib
PackedDibCreatePalette proc pPackedDibPDCP:DWORD ;BITMAPINFO *
LOCAL hPalettePDCP:HPALETTE
LOCAL i, iNumColors:DWORD
LOCAL plp:DWORD ;指向 LOGPALETTE 结构体的指针
LOCAL prgb:DWORD ;指向 RGBQUAD 结构体的指针
;RGBQUAD STRUCT
; rgbBlue BYTE ?
; rgbGreen BYTE ?
; rgbRed BYTE ?
; rgbReserved BYTE ?
;RGBQUAD ENDS

invoke PackedDibGetNumColors,pPackedDibPDCP
mov iNumColors,eax
.if eax==0
mov eax,NULL
ret
.endif

mov eax,sizeof (LOGPALETTE)
mov ecx,iNumColors
dec ecx
mul ecx
mov ecx,sizeof (PALETTEENTRY)
mul ecx
invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,eax
mov plp,eax ;plp = malloc (sizeof (LOGPALETTE) * (iNumColors - 1) * sizeof (PALETTEENTRY))
mov esi,plp
mov WORD ptr [esi],300h ;plp->palVersion = 0x0300
mov eax,iNumColors
mov WORD ptr [esi+2],ax ;plp->palNumEntries = iNumColors

add esi,4
mov i,0
LoopII:
push esi
invoke PackedDibGetColorTableEntry,pPackedDibPDCP,i
mov prgb,eax

mov edi,prgb ;其实一样的
mov al,[edi]
pop esi
mov [esi],al
mov al,[edi+1]
mov [esi+1],al
mov al,[edi+2]
mov [esi+2],al

mov BYTE ptr [esi+3],0
add esi,4
inc i

mov eax,i
.if eax<iNumColors
jmp LoopII
.endif

invoke CreatePalette,plp
mov hPalettePDCP,eax

invoke LocalFree,plp
mov eax,hPalettePDCP
ret
PackedDibCreatePalette endp


START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND

mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc

mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0

push hInst
pop wndclass.hInstance

invoke LoadIcon,NULL, IDI_APPLICATION
mov wndclass.hIcon,eax

invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax

invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX

lea eax,szAppName
mov wndclass.lpszMenuName,eax
mov wndclass.lpszClassName,eax

mov wndclass.hIconSm,0

invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif


invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
CTXT("Show DIB #7: Converted to DDB"),
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax

invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd

StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:

mov eax,msg.wParam
ret
WinMain endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL bitmap:BITMAP
LOCAL pPackedDib:DWORD ;指向 BITMAPINFO 结构体的指针
LOCAL hdc,hdcMem:HDC
LOCAL ps :PAINTSTRUCT


.if uMsg == WM_CREATE
mov eax,sizeof (OPENFILENAME)
mov ofn.lStructSize,eax
mov eax,hwnd
mov ofn.hwndOwner,eax
mov eax,NULL
mov ofn.hInstance,eax
mov ofn.lpstrFilter,CTEXT("Bitmap Files (*.BMP)\0*.bmp\0 All Files (*.*)\0*.*\0\0")
mov eax,NULL
mov ofn.lpstrCustomFilter,eax
xor eax,eax
mov ofn.nMaxCustFilter,eax
mov ofn.nFilterIndex,eax
lea eax,szFileName
mov ofn.lpstrFile,eax ; Set in Open and Close functions
mov eax,MAX_PATH
mov ofn.nMaxFile,eax
mov eax,NULL
mov ofn.lpstrFileTitle,eax ;Set in Open and Close functions
mov eax,MAX_PATH
mov ofn.nMaxFileTitle,eax
mov eax,NULL
mov ofn.lpstrInitialDir,eax
mov ofn.lpstrTitle,eax
mov eax,0
mov ofn.Flags,eax ; Set in Open and Close functions
mov ofn.nFileOffset,ax
mov ofn.nFileExtension,ax
mov ofn.lpstrDefExt,CTEXT ("bmp")
mov eax,0
mov ofn.lCustData,eax
mov eax,NULL
mov ofn.lpfnHook,eax
mov ofn.lpTemplateName,eax

xor eax,eax
ret
.elseif uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax

mov eax,lParam
shr eax,16
mov cyClient,eax

xor eax,eax
ret
.elseif uMsg == WM_COMMAND
mov eax,wParam
.if ax==IDM_FILE_OPEN
; Show the File Open dialog box
invoke GetOpenFileName,addr ofn
.if eax==0
xor eax,eax
ret
.endif
; If there's an existing packed DIB, free the memory
.if (hBitmap!=0)
invoke DeleteObject,hBitmap
mov hBitmap,NULL
.endif

; If there's an existing logical palette, delete it
.if (hPalette!=0)
invoke DeleteObject,hPalette
mov hPalette,NULL
.endif

; Load the packed DIB into memory
invoke LoadCursor,NULL, IDC_WAIT
invoke SetCursor,eax
invoke ShowCursor,TRUE
invoke PackedDibLoad,addr szFileName
mov pPackedDib,eax

invoke ShowCursor,FALSE
invoke LoadCursor,NULL, IDC_ARROW
invoke SetCursor,eax

.if pPackedDib!=0
; Create palette from the DIB and select it into DC
invoke PackedDibCreatePalette,pPackedDib
mov hPalette,eax ;hPalette = PackedDibCreatePalette (pPackedDib)

invoke GetDC,hwnd
mov hdc,eax

.if (hPalette!=0)
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
.endif

invoke PackedDibGetBitsPtr,pPackedDib
mov ebx,eax
; Create the DDB from the DIB
invoke CreateDIBitmap,hdc,pPackedDib,CBM_INIT,ebx,pPackedDib, DIB_RGB_COLORS
mov hBitmap,eax

invoke ReleaseDC,hwnd, hdc
; Free the packed-DIB memory
invoke LocalFree,pPackedDib
.else
invoke MessageBox,hwnd, CTEXT ("Cannot load DIB file"),addr szAppName, 0
.endif
invoke InvalidateRect,hwnd, NULL, TRUE
xor eax,eax
ret
.endif

.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
.if hPalette!=0
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
.endif

.if hBitmap!=0
invoke GetObject,hBitmap, sizeof (BITMAP),addr bitmap
invoke CreateCompatibleDC,hdc
mov hdcMem,eax

invoke SelectObject,hdcMem, hBitmap
invoke BitBlt,hdc,0,0,bitmap.bmWidth, bitmap.bmHeight,hdcMem,0, 0, SRCCOPY
invoke DeleteDC,hdcMem
.endif
invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_QUERYNEWPALETTE
.if hPalette==0
mov eax,FALSE
ret
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke InvalidateRect,hwnd, NULL, TRUE
invoke ReleaseDC,hwnd, hdc

mov eax,TRUE
ret
.elseif uMsg == WM_PALETTECHANGED
mov eax,wParam
.if (hPalette==0) || (eax == hwnd)
jmp WM_PALETTECHANGED_END
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke UpdateColors,hdc
invoke ReleaseDC,hwnd, hdc
.elseif uMsg == WM_DESTROY
.if hBitmap!=0
invoke DeleteObject,hBitmap
.endif
.if hPalette!=0
invoke DeleteObject,hBitmap
.endif
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif
WM_PALETTECHANGED_END:
invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START

 
SHOWDIB7.RC (摘录)
        
//Microsoft Developer Studio generated resource script.
        
#include "resource.h"
        
#include "afxres.h"
        
/////////////////////////////////////////////////////////////////////////////
        
// Menu
        
SHOWDIB7 MENU DISCARDABLE
        
BEGIN
        
  POPUP "&File"
        
   BEGIN
        
                          MENUITEM "&Open",                    IDM_FILE_OPEN
        
   END
        
END
        
RESOURCE.H (摘录)
        
// Microsoft Developer Studio generated include file.
        
// Used by ShowDib7.rc
        
#define IDM_FILE_OPEN      40001
        

与前面的程序一样,SHOWDIB7获得了一个指向packed DIB的指标,该DIB回应菜单的「File」、「Open」命令.程序从packed DIB建立了调色板,然后-还是在WM_COMMAND消息的处理过程中-获得了用于视讯显示的设备内容,并选进调色板,显现调色板.然后SHOWDIB7呼叫CreateDIBitmap以便从DIB建立DDB.如果调色板没有选进设备内容并显现,那么CreateDIBitmap建立的DDB将不使用逻辑调色板中的附加颜色.

呼叫CreateDIBitmap以后,该程序将释放packed DIB占用的内存空间.pPackedDib变量不是静态变量.相反的,SHOWDIB7按静态变量保留了位图句柄(hBitmap)和逻辑调色板句柄(hPalette).

在WM_PAINT消息处理期间,调色板再次选进设备内容并显现.GetObject函数可获得位图的宽度和高度.然后,程序通过建立兼容的内存设备内容在显示区域显示位图,选进位图,并执行BitBlt.显示DDB时所用的调色板,必须与从CreateDIBitmap呼叫建立时所用的一样.

如果将位图复制到剪贴簿,则最好使用packed DIB格式.然后Windows可以将位图对象提供给希望使用这些位图的程序.然而,如果需要将位图对象复制到剪贴簿,则首先要获得视讯设备内容并显现调色板.这允许Windows依据目前的系统调色板将DDB转换为DIB.

调色板和DIB区块

最后,程序16-18所示的SHOWDIB8说明了如何使用带有DIB区块的调色板.

程序16-18 SHOWDIB8
        
SHOWDIB8.ASM

;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc
Include comdlg32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
IncludeLib comdlg32.lib

include macro.asm

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
IDM_FILE_OPEN equ 40001

.DATA
szAppName TCHAR "SHOWDIB7",0

.DATA?
hBitmap HBITMAP ?
hPalette HPALETTE ?
cxClient DWORD ?
cyClient DWORD ?
ofn OPENFILENAME <?>
pBits DWORD ? ;PBYTE
szFileName TCHAR MAX_PATH dup(?)
szTitleName TCHAR MAX_PATH dup(?)
hInstance HINSTANCE ?
szBuffer db 100 dup(?)

.CODE

; PackedDibLoad: Load DIB File as Packed-Dib Memory Block
PackedDibLoad proc szFN:PTSTR
LOCAL bmfh:BITMAPFILEHEADER
LOCAL pbmi:DWORD ;BITMAPINFO * pbmi ;
LOCAL bSuccess:BOOL
LOCAL dwPackedDibSize, dwBytesRead:DWORD
LOCAL hFile:HANDLE

; Open the file: read access, prohibit write access
invoke CreateFile,szFN, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL
mov hFile,eax
.if hFile == INVALID_HANDLE_VALUE
mov eax,NULL
ret
.endif

; Read in the BITMAPFILEHEADER
invoke ReadFile,hFile,addr bmfh, sizeof (BITMAPFILEHEADER),addr dwBytesRead, NULL
mov bSuccess,eax

mov eax,dwBytesRead
mov bx,bmfh.bfType
.if (bSuccess==0) || ( eax != sizeof (BITMAPFILEHEADER)) || ( ebx != "MB")
invoke CloseHandle,hFile
mov eax,NULL
ret
.endif


; Allocate memory for the packed DIB & read it in
mov eax,bmfh.bfSize
sub eax,sizeof (BITMAPFILEHEADER)
mov dwPackedDibSize,eax

invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,dwPackedDibSize
mov pbmi,eax

invoke ReadFile,hFile, pbmi, dwPackedDibSize,addr dwBytesRead, NULL
mov bSuccess,eax
invoke CloseHandle,hFile


mov eax,dwBytesRead
.if (bSuccess==0) || (eax != dwPackedDibSize)
invoke LocalFree,pbmi
mov eax,NULL
ret
.endif

mov eax,pbmi
ret
PackedDibLoad endp

; Functions to get information from packed DIB
PackedDibGetWidth proc pPackedDibPGW:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGW
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
;return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcWidth
mov bx,[esi+4]
xor eax,eax
mov ax,bx
ret
.else
;return pPackedDib->bmiHeader.biWidth
mov eax,[esi+4]
ret
.endif
PackedDibGetWidth endp

PackedDibGetHeight proc pPackedDibPGH:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGH
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
mov bx,[esi+6] ;return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcHeight
xor eax,eax
mov ax,bx
ret

ret
.else
;return abs (pPackedDib->bmiHeader.biHeight) ;
mov eax,[esi+8]
ret
.endif
PackedDibGetHeight endp

PackedDibGetBitCount proc pPackedDibPGB:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGB
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
mov bx,[esi+10] ; return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcBitCount
xor eax,eax
mov ax,bx
ret
.else
; return pPackedDib->bmiHeader.biBitCount
mov bx,[esi+14]
xor eax,eax
mov ax,bx
ret
.endif
PackedDibGetBitCount endp

PackedDibGetRowLength proc pPackedDibPGR:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGR
mov eax,[esi]
;return ((PackedDibGetWidth (pPackedDib)*PackedDibGetBitCount (pPackedDib) + 31) & ~31) >> 3
invoke PackedDibGetWidth,pPackedDibPGR
push eax
invoke PackedDibGetBitCount,pPackedDibPGR
mov ebx,eax
pop eax
add eax,ebx
add eax,31
mov ecx,31
neg ecx
and eax,ecx
shr eax,3
ret
PackedDibGetRowLength endp


PackedDibGetInfoHeaderSize proc pPackedDibPHS:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPHS
xor eax,eax
mov ax,[esi]
.if (ax == sizeof (BITMAPCOREHEADER))
;return ((PBITMAPCOREINFO)pPackedDib)->bmciHeader.bcSize
mov eax,[esi]
ret
.else
.if (ax == sizeof (BITMAPINFOHEADER))
;return pPackedDib->bmiHeader.biSize + (pPackedDib->bmiHeader.biCompression == BI_BITFIELDS ? 12 : 0) ;
mov eax,[esi+16]
.if eax== BI_BITFIELDS
mov ebx,12
.else
mov ebx,0
.endif
mov eax,[esi]
add eax,ebx
ret
.else
mov eax,[esi] ;return pPackedDib->bmiHeader.biSize ;
ret
.endif
.endif
PackedDibGetInfoHeaderSize endp

; PackedDibGetColorsUsed returns value in information header;
; could be 0 to indicate non-truncated color table!
PackedDibGetColorsUsed proc pPackedDibPGC:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGC
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
xor eax,eax
ret
.else ;return pPackedDib->bmiHeader.biClrUsed
mov eax,[esi+32]
ret
.endif
PackedDibGetColorsUsed endp

;PackedDibGetNumColors is actual number of entries in color table
PackedDibGetNumColors proc pPackedDibPGNC:DWORD ;指向 BITMAPINFO 结构体的指针
LOCAL iNumColors:DWORD
invoke PackedDibGetColorsUsed,pPackedDibPGNC
mov iNumColors,eax

invoke PackedDibGetBitCount,pPackedDibPGNC
.if (iNumColors == 0) && (eax < 16)
; iNumColors =1 << PackedDibGetBitCount (pPackedDib) ;
invoke PackedDibGetBitCount,pPackedDibPGNC
mov ecx,eax
mov eax,1
shl eax,cl
mov iNumColors,eax
.endif
mov eax,iNumColors
ret
PackedDibGetNumColors endp

PackedDibGetColorTableSize proc pPackedDibPGCTS:DWORD ;指向 BITMAPINFO 结构体的指针
mov esi,pPackedDibPGCTS
mov eax,[esi]
.if (eax == sizeof (BITMAPCOREHEADER))
;return PackedDibGetNumColors (pPackedDib) * sizeof (RGBTRIPLE)
invoke PackedDibGetNumColors,pPackedDibPGCTS
mov ecx,sizeof (RGBTRIPLE)
mul ecx
ret
.else
;return PackedDibGetNumColors (pPackedDib) * sizeof (RGBQUAD)
invoke PackedDibGetNumColors,pPackedDibPGCTS
mov ecx,sizeof (RGBQUAD)
mul ecx
ret
.endif
PackedDibGetColorTableSize endp

PackedDibGetColorTablePtr proc pPackedDibPGCTP:DWORD ;指向 BITMAPINFO 结构体的指针
invoke PackedDibGetNumColors,pPackedDibPGCTP
.if ( eax == 0)
xor eax,eax
ret
.endif
;return (RGBQUAD *) (((BYTE *)pPackedDib) + PackedDibGetInfoHeaderSize (pPackedDib))
invoke PackedDibGetInfoHeaderSize,pPackedDibPGCTP
mov esi,pPackedDibPGCTP
add eax,esi
ret
PackedDibGetColorTablePtr endp

PackedDibGetColorTableEntry proc pPackedDibPDGT:DWORD,i:DWORD
invoke PackedDibGetNumColors,pPackedDibPDGT
.if eax==0
xor eax,eax
.endif


mov esi,pPackedDibPDGT
mov eax,[esi]
.if eax==sizeof (BITMAPCOREHEADER)
invoke PackedDibGetColorTablePtr,pPackedDibPDGT
add eax,i
ret
.else
invoke PackedDibGetColorTablePtr,pPackedDibPDGT
add eax,i

ret
.endif
PackedDibGetColorTableEntry endp

; PackedDibGetBitsPtr finally!
PackedDibGetBitsPtr proc pPackedDibPDPP:DWORD ;BITMAPINFO *
invoke PackedDibGetInfoHeaderSize,pPackedDibPDPP
push eax
invoke PackedDibGetColorTableSize,pPackedDibPDPP
pop ebx
add eax,ebx
add eax,pPackedDibPDPP
ret
PackedDibGetBitsPtr endp

; PackedDibGetBitsSize can be calculated from the height and row length
; if it's not explicitly in the biSizeImage field
PackedDibGetBitsSize proc pPackedDibPGBS:DWORD ;BITMAPINFO *
mov esi,pPackedDibPGBS
mov eax,[esi]

mov ebx,[esi+20]
.if (eax!=sizeof (BITMAPCOREHEADER)) && (ebx!=0)
mov eax,ebx
ret
.endif

invoke PackedDibGetHeight,pPackedDibPGBS
push eax
invoke PackedDibGetRowLength,pPackedDibPGBS
pop ecx
mul ecx
ret
PackedDibGetBitsSize endp

; PackedDibCreatePalette creates logical palette from PackedDib
PackedDibCreatePalette proc pPackedDibPDCP:DWORD ;BITMAPINFO *
LOCAL hPalettePDCP:HPALETTE
LOCAL i, iNumColors:DWORD
LOCAL plp:DWORD ;指向 LOGPALETTE 结构体的指针
LOCAL prgb:DWORD ;指向 RGBQUAD 结构体的指针
;RGBQUAD STRUCT
; rgbBlue BYTE ?
; rgbGreen BYTE ?
; rgbRed BYTE ?
; rgbReserved BYTE ?
;RGBQUAD ENDS

invoke PackedDibGetNumColors,pPackedDibPDCP
mov iNumColors,eax
.if eax==0
mov eax,NULL
ret
.endif

mov eax,sizeof (LOGPALETTE)
mov ecx,iNumColors
dec ecx
mul ecx
mov ecx,sizeof (PALETTEENTRY)
mul ecx
invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT,eax
mov plp,eax ;plp = malloc (sizeof (LOGPALETTE) * (iNumColors - 1) * sizeof (PALETTEENTRY))
mov esi,plp
mov WORD ptr [esi],300h ;plp->palVersion = 0x0300
mov eax,iNumColors
mov WORD ptr [esi+2],ax ;plp->palNumEntries = iNumColors

add esi,4
mov i,0
LoopII:
push esi
invoke PackedDibGetColorTableEntry,pPackedDibPDCP,i
mov prgb,eax

mov edi,prgb ;其实一样的
mov al,[edi]
pop esi
mov [esi],al
mov al,[edi+1]
mov [esi+1],al
mov al,[edi+2]
mov [esi+2],al

mov BYTE ptr [esi+3],0
add esi,4
inc i

mov eax,i
.if eax<iNumColors
jmp LoopII
.endif

invoke CreatePalette,plp
mov hPalettePDCP,eax

invoke LocalFree,plp
mov eax,hPalettePDCP
ret
PackedDibCreatePalette endp


START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND

mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc

mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0

push hInst
pop wndclass.hInstance

invoke LoadIcon,NULL, IDI_APPLICATION
mov wndclass.hIcon,eax

invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax

invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX

lea eax,szAppName
mov wndclass.lpszMenuName,eax
mov wndclass.lpszClassName,eax

mov wndclass.hIconSm,0

invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif


invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
CTXT("Show DIB #8: DIB Section"),
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax

invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd

StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:

mov eax,msg.wParam
ret
WinMain endp

CopyMemory proc Dest:DWORD, Source:DWORD, mlength:DWORD
cld ;Work upwards
mov esi, Source ;Source address
mov edi, Dest ;Destination address
mov ecx, mlength ;Get size in bytes
rep movsb ; repeat copy util all done
ret
CopyMemory endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL bitmap:BITMAP
LOCAL pPackedDib:DWORD ;指向 BITMAPINFO 结构体的指针
LOCAL hdc,hdcMem:HDC
LOCAL ps :PAINTSTRUCT


.if uMsg == WM_CREATE
mov eax,sizeof (OPENFILENAME)
mov ofn.lStructSize,eax
mov eax,hwnd
mov ofn.hwndOwner,eax
mov eax,NULL
mov ofn.hInstance,eax
mov ofn.lpstrFilter,CTEXT("Bitmap Files (*.BMP)\0*.bmp\0 All Files (*.*)\0*.*\0\0")
mov eax,NULL
mov ofn.lpstrCustomFilter,eax
xor eax,eax
mov ofn.nMaxCustFilter,eax
mov ofn.nFilterIndex,eax
lea eax,szFileName
mov ofn.lpstrFile,eax ; Set in Open and Close functions
mov eax,MAX_PATH
mov ofn.nMaxFile,eax
mov eax,NULL
mov ofn.lpstrFileTitle,eax ;Set in Open and Close functions
mov eax,MAX_PATH
mov ofn.nMaxFileTitle,eax
mov eax,NULL
mov ofn.lpstrInitialDir,eax
mov ofn.lpstrTitle,eax
mov eax,0
mov ofn.Flags,eax ; Set in Open and Close functions
mov ofn.nFileOffset,ax
mov ofn.nFileExtension,ax
mov ofn.lpstrDefExt,CTEXT ("bmp")
mov eax,0
mov ofn.lCustData,eax
mov eax,NULL
mov ofn.lpfnHook,eax
mov ofn.lpTemplateName,eax

xor eax,eax
ret
.elseif uMsg == WM_SIZE
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax

mov eax,lParam
shr eax,16
mov cyClient,eax

xor eax,eax
ret
.elseif uMsg == WM_COMMAND
mov eax,wParam
.if ax==IDM_FILE_OPEN
; Show the File Open dialog box
invoke GetOpenFileName,addr ofn
.if eax==0
xor eax,eax
ret
.endif
; If there's an existing packed DIB, free the memory
.if (hBitmap!=0)
invoke DeleteObject,hBitmap
mov hBitmap,NULL
.endif

; If there's an existing logical palette, delete it
.if (hPalette!=0)
invoke DeleteObject,hPalette
mov hPalette,NULL
.endif

; Load the packed DIB into memory
invoke LoadCursor,NULL, IDC_WAIT
invoke SetCursor,eax
invoke ShowCursor,TRUE
invoke PackedDibLoad,addr szFileName
mov pPackedDib,eax

invoke ShowCursor,FALSE
invoke LoadCursor,NULL, IDC_ARROW
invoke SetCursor,eax

.if pPackedDib!=0
; Create the DIB section from the DIB
invoke CreateDIBSection,NULL,pPackedDib,DIB_RGB_COLORS,addr pBits,NULL, 0
mov hBitmap,eax
; Copy the bits
invoke PackedDibGetBitsPtr,pPackedDib
push eax
invoke PackedDibGetBitsSize,pPackedDib
pop ebx
invoke CopyMemory,pBits,ebx,eax

; Create palette from the DIB
invoke PackedDibCreatePalette,pPackedDib
mov hPalette,eax

; Free the packed-DIB memory
invoke LocalFree,pPackedDib
.else
invoke MessageBox,hwnd, CTEXT ("Cannot load DIB file"),addr szAppName, 0
.endif
invoke InvalidateRect,hwnd, NULL, TRUE
xor eax,eax
ret
.endif

.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
.if hPalette!=0
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
.endif

.if hBitmap!=0
invoke GetObject,hBitmap, sizeof (BITMAP),addr bitmap
invoke CreateCompatibleDC,hdc
mov hdcMem,eax

invoke SelectObject,hdcMem, hBitmap
invoke BitBlt,hdc,0,0,bitmap.bmWidth, bitmap.bmHeight,hdcMem,0, 0, SRCCOPY
invoke DeleteDC,hdcMem
.endif
invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_QUERYNEWPALETTE
.if hPalette==0
mov eax,FALSE
ret
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke InvalidateRect,hwnd, NULL, TRUE
invoke ReleaseDC,hwnd, hdc

mov eax,TRUE
ret
.elseif uMsg == WM_PALETTECHANGED
mov eax,wParam
.if (hPalette==0) || (eax == hwnd)
jmp WM_PALETTECHANGED_END
.endif
invoke GetDC,hwnd
mov hdc,eax
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
invoke UpdateColors,hdc
invoke ReleaseDC,hwnd, hdc
.elseif uMsg == WM_DESTROY
.if hBitmap!=0
invoke DeleteObject,hBitmap
.endif
.if hPalette!=0
invoke DeleteObject,hBitmap
.endif
invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif
WM_PALETTECHANGED_END:
invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START

         
SHOWDIB8.RC (摘录)
        
//Microsoft Developer Studio generated resource script.
        
#include "resource.h"
        
#include "afxres.h"
        
/////////////////////////////////////////////////////////////////////////////
        
// Menu
        
SHOWDIB8 MENU DISCARDABLE
        
BEGIN
        
   POPUP "&File"
        
   BEGIN 
        
                          MENUITEM "&Open",                    IDM_FILE_OPEN
        
   END
        
END
        
RESOURCE.H (摘录)
        
// Microsoft Developer Studio generated include file.
        
// Used by ShowDib8.rc
        
#define IDM_FILE_OPEN             40001
        

在SHOWDIB7和SHOWDIB8中的WM_PAINT处理是一样的:两个程序都将位图句柄(hBitmap)和逻辑调色板句柄(hPalette)作为静态变量.调色板被选进设备内容并显现,位图的宽度和高度从GetObject函数获得,程序建立内存设备内容并选进位图,然后通过呼叫BitBlt将位图显示到显示区域.

两个程序之间最大的差别在于处理「File」、「Open」菜单命令的程序.在获得指向packed DIB的指标并建立了调色板以后,SHOWDIB7必须将调色板选进视讯设备内容,并在呼叫CreateDIBitmap之前显现.SHOWDIB8在获得packed DIB指标以后呼叫CreateDIBSection.不必将调色板选进设备内容,这是因为CreateDIBSection不将DIB转换成设备相关的格式.的确,CreateDIBSection的第一个参数(即设备内容句柄)的唯一用途在于您是否使用DIB_PAL_COLORS标志位.

呼叫CreateDIBSection以后,SHOWDIB8将像素位从packed DIB复制到从CreateDIBSection函数传回的内存位置,然后呼叫PackedDibCreatePalette.尽管此函数便于程序使用,但是SHOWDIB8将依据从GetDIBColorTable函数传回的信息建立调色板.

DIB 处理链接库

就是现在-经过我们长时间地学习GDI位图对象、设备无关位图、DIB区块和Windows调色板管理器之后-我们才做好了开发一套有助于处理位图的函数的准备.

前面的PACKEDIB文件展示了一种可能的方法:内存中的packed DIB只用指向它的指标表示.程序所的有关DIB的全部信息都可以从存取表头信息结构的函数获得.然而,实际上到「get pixel」和「set pixel」例程时,这种方法就会产生严重的执行问题.图像处理任务当然需要存取位图位,并且这些函数也应该尽可能地快.

可能的C++的解决方式中包括建立DIB类别,这时指向packed DIB的指标正好是一个成员变量.其它成员变量和成员函数有助于更快地执行获得和设定DIB中的像素的例程.不过,因为我在第一章已经指出,对于本书您只需要了解C,使用C++将是其它书的范围.

当然,用C++能做的事情用C也能做.一个好的例子就是许多Windows函数都使用句柄.除了将句柄当作数值以外,应用程序对它还了解什么呢?程序知道句柄引用特殊的函数对象,还知道函数用于处理现存的对象.显然,操作系统按某种方式用句柄来引用对象的内部信息.句柄可以与结构指针一样简单.

例如,假设有一个函数集,这些函数都使用一个称为HDIB的句柄.HDIB是什么呢?它可能在某个表头文件中定义如下:

typedef void * HDIB ;
        

此定义用「不关您的事」回答了「HDIB是什么」这个问题.

然而,实际上HDIB可能是结构指针,该结构不仅包括指向packed DIB的指针,还包括其它信息:

typedef struct
        
{
        
           BITMAPINFO    *      pPackedDib ;
        
           int                                  cx, cy, cBitsPerPixel, cBytesPerRow ;
        
          BYTE                          * pBits ;
        
{
        
DIBSTRUCTURE, * PDIBSTRUCTURE ;
        

此结构的其它五个字段包括从packed DIB中引出的信息.当然,结构中这些值允许更快速地存取它们.不同的DIB链接库函数都可以处理这个结构,而不是pPackedDib指标.可以按下面的方法来执行DibGetPixelPointer函数:

BYTE * DibGetPixelPointer (HDIB hdib, int x, int y)
        
{
        
          PDIBSTRUCTURE pdib = hdib ;
        
           return pdib->pBits + y * pdib->cBytesPerRow +
        
                                         x * pdib->cBitsPerPixel / 8 ;
        
}
        

当然,这种方法可能要比PACKEDIB.C中执行「get pixel」例程快.

由于这种方法非常合理,所以我决定放弃packed DIB,并改用处理DIB区块的DIB链接库.这实际上使我们对packed DIB的处理有更大的弹性(也就是说,能够在设备无关的方式下操纵DIB像素位),而且在Windows NT下执行时将更有效.

DIBSTRUCT结构

DIBHELP.C文件-如此命名是因为对处理DIB提供帮助-有上千行,并在几个小部分中显示.但是首先让我们看一下DIBHELP函数所处理的结构,该结构在DIBHELP.C中定义如下:

typedef struct
        
{
        
           PBYTE         * ppRow ;       // array of row pointers
        
           int           iSignature ;  // = "Dib "
        
           HBITMAP      hBitmap ;     // handle returned from CreateDIBSection
        
           BYTE          *      pBits ;     // pointer to bitmap bits
        
           DIBSECTION    ds ;          // DIBSECTION structure
        
           int           iRShift[3] ;  // right-shift values for color masks
        
           int          iLShift[3] ;  // left-shift values for color masks
        
}
        
DIBSTRUCT, * PDIBSTRUCT ;
        

现在跳过第一个字段.它之所以为第一个字段是因为它使某些宏更易于使用-在讨论完其它字段以后再来理解第一个字段就更容易了.

在DIBHELP.C中,当DIB建立的函数首先设定了此结构时,第二个字段就设定为文字字符串「Dib」的二进制值.通过一些DIBHELP函数,第二个字段将用于结构有效指针的一个标记.

第三个字段,即hBitmap,是从CreateDIBSection函数传回的位图句柄.您将想起该句柄可有多种使用方式,它与我们在前面遇到的GDI位图对象的句柄用法一样.不过,从CreateDIBSection传回的句柄将涉及按设备无关格式储存的位图,该位图格式一直储存到通过呼叫BitBlt和StretchBlt来将位图画到输出设备.

DIBSTRUCT的第四个字段是指向位图位的指针.此值也可由CreateDIBSection函数设定.您将想起,操作系统将控制这个内存块,但应用程序有存取它的权限.在删除位图句柄时,内存块将自动释放.

DIBSTRUCT的第五个字段是DIBSECTION结构.如果您有从CreateDIBSection传回的位图句柄,那么您可以将句柄传递给GetObject函数以获得有关DIBSECTION结构中的位图信息:

GetObject (hBitmap, sizeof (DIBSECTION), &ds) ;
        

作为提示,DIBSECTION结构在WINGDI.H中定义如下:

typedef struct tagDIBSECTION {
        
   BITMAP                               dsBm ;
        
  BITMAPINFOHEADER      dsBmih ;
        
  DWORD                 dsBitfields[3] ;    // Color masks
        
   HANDLE          dshSection ;
        
   DWORD           dsOffset ;
        
}
        
DIBSECTION, * PDIBSECTION ;
        

第一个字段是BITMAP结构,它与CreateBitmapIndirect一起建立位图对象,与GetObject一起传回关于DDB的信息.第二个字段是BITMAPINFOHEADER结构.不管位图信息结构是否传递给CreateDIBSection函数,DIBSECTION结构总有BITMAPINFOHEADER结构而不是其它结构,例如BITMAPCOREHEADER结构.这意味着在存取此结构时,DIBHELP.C中的许多函数都不必检查与OS/2兼容的DIB.

对于16位和32位的DIB,如果BITMAPINFOHEADER结构的biCompression字段是BI_BITFIELDS,那么在信息表头结构后面通常有三个屏蔽值.这些屏蔽值决定如何将16位和32位像素值转换成RGB颜色.屏蔽储存在DIBSECTION结构的第三个字段中.

DIBSECTION结构的最后两个字段指的是DIB区块,此区块由文件映射建立.DIBHELP不使用CreateDIBSection的这个特性,因此可以忽略这些字段.

DIBSTRUCT的最后两个字段储存左右移位值,这些值用于处理16位和32位DIB的颜色屏蔽.

让我们再回来看一下DIBSTRUCT的第一个字段.正如我们所看到的一样,在开始建立DIB时,此字段设定为指向一个指针数组的指针,该数组中的每个指针都指向DIB中的一行像素.这些指标允许以更快的方式来获得DIB像素位,同时也被定义,以便顶行可以首先引用DIB像素位.此数组的最后一个元素-引用DIB图像的最底行-通常等于DIBSTRUCT的pBits字段.

信息函数

DIBHELP.C以定义DIBSTRUCT结构开始,然后提供一个函数集,此函数集允许应用程序获得有关DIB区块的信息.程序16-19显示了DIBHELP.C的第一部分.

程序16-19 DIBHELP.C文件的第一部分
        
DIBHELP.C (第一部分)
        
/*--------------------------------------------------------------------------
        
  DIBHELP.C -- DIB Section Helper Routines
        
                                                         (c) Charles Petzold, 1998
        
----------------------------------------------------------------------------*/
        
#include <windows.h>
        
#include "dibhelp.h"
        

#define HDIB_SIGNATURE (* (int *) "Dib ")
        
typedef struct
        
{
        
           PBYTE                         *      ppRow ;    // must be first field for macros!
        
           int                           iSignature ;
        
           HBITMAP                       hBitmap ;
        
           BYTE                          *      pBits ;
        
           DIBSECTION            ds ;
        
           int                                 iRShift[3] ;
        
           int                                  iLShift[3] ;
        
}
        
DIBSTRUCT, * PDIBSTRUCT ;
        

/*----------------------------------------------------------------------------
        
    DibIsValid:  Returns TRUE if hdib points to a valid DIBSTRUCT
        
-----------------------------------------------------------------------------*/
        

BOOL DibIsValid (HDIB hdib)
        
{
        
          PDIBSTRUCT pdib = hdib ;
        
           if (pdib == NULL)
        
                          return FALSE ;
        
           if (IsBadReadPtr (pdib, sizeof (DIBSTRUCT)))
        
                          return FALSE ;
        
           if (pdib->iSignature != HDIB_SIGNATURE)
        
                          return FALSE ;
        
           return TRUE ;
        
}
        

/*----------------------------------------------------------------------------
        
  DibBitmapHandle:      Returns the handle to the DIB section bitmap object
        
-----------------------------------------------------------------------------*/
        

HBITMAP DibBitmapHandle (HDIB hdib)
        
{
        
           if (!DibIsValid (hdib))
        
                          return NULL ;
        
           return ((PDIBSTRUCT) hdib)->hBitmap ;
        
}
        

/*---------------------------------------------------------------------------
        
  DibWidth:  Returns the bitmap pixel width
        
-----------------------------------------------------------------------------*/
        

int DibWidth (HDIB hdib)
        
{
        
           if (!DibIsValid (hdib))
        
                          return 0 ;
        
           return ((PDIBSTRUCT) hdib)->ds.dsBm.bmWidth ;
        
}
        

/*---------------------------------------------------------------------------
        
  DibHeight:  Returns the bitmap pixel height
        
----------------------------------------------------------------------------*/
        

int DibHeight (HDIB hdib)
        
{
        
   if (!DibIsValid (hdib))
        
                         return 0 ;
        
           return ((PDIBSTRUCT) hdib)->ds.dsBm.bmHeight ;
        
}
        

/*---------------------------------------------------------------------------
        
  DibBitCount:    Returns the number of bits per pixel
        
----------------------------------------------------------------------------*/
        

int DibBitCount (HDIB hdib)
        
{
        
           if (!DibIsValid (hdib))
        
                          return 0 ;
        
   
        
           return ((PDIBSTRUCT) hdib)->ds.dsBm.bmBitsPixel ;
        
}
        

/*----------------------------------------------------------------------------
        
  DibRowLength:  Returns the number of bytes per row of pixels
        
-----------------------------------------------------------------------------*/
        

int DibRowLength (HDIB hdib)
        
{
        
  if (!DibIsValid (hdib))
        
                          return 0 ;
        
           return 4 * ((DibWidth (hdib) * DibBitCount (hdib) + 31) / 32) ;
        
}
        

/*---------------------------------------------------------------------------
        
  DibNumColors:  Returns the number of colors in the color table
        
----------------------------------------------------------------------------*/
        

int DibNumColors (HDIB hdib)
        
{
        
           PDIBSTRUCT pdib = hdib ;
        

           if (!DibIsValid (hdib))
        
                          return 0 ;
        

           if (pdib->ds.dsBmih.biClrUsed != 0)
        
    {
        
                          return pdib->ds.dsBmih.biClrUsed ;
        
    }
        
           else if (DibBitCount (hdib) <= 8)
        
    {
        
                  return 1 << DibBitCount (hdib) ;
        
    }
        
           return 0 ;
        
}
        
/*---------------------------------------------------------------------------
        
  DibMask:  Returns one of the color masks
        
---------------------------------------------------------------------------*/
        

DWORD       DibMask (HDIB hdib, int i)
        
{
        
           PDIBSTRUCT pdib = hdib ;
        

           if (!DibIsValid (hdib) || i < 0 || i > 2)
        
                          return 0 ;
        
   
        
           return pdib->ds.dsBitfields[i] ;
        
}
        

/*----------------------------------------------------------------------------
        
  DibRShift:  Returns one of the right-shift values
        
-----------------------------------------------------------------------------*/
        

int DibRShift (HDIB hdib, int i)
        
{  
        
           PDIBSTRUCT pdib = hdib ;
        

           if (!DibIsValid (hdib) || i < 0 || i > 2)
        
                          return 0 ;
        
   
        
           return pdib->iRShift[i] ;
        
}
        

/*----------------------------------------------------------------------------
        
  DibLShift:  Returns one of the left-shift values
        
----------------------------------------------------------------------------*/
        

int DibLShift (HDIB hdib, int i)
        
{
        
           PDIBSTRUCT pdib = hdib ;
        

           if (!DibIsValid (hdib) || i < 0 || i > 2)
        
                          return 0 ;
        
   
        
           return pdib->iLShift[i] ;
        
}
        

/*---------------------------------------------------------------------------
        
  DibCompression:  Returns the value of the biCompression field
        
----------------------------------------------------------------------------*/
        
int DibCompression (HDIB hdib)
        
{
        
           if (!DibIsValid (hdib))
        
                          return 0 ;
        

           return ((PDIBSTRUCT) hdib)->ds.dsBmih.biCompression ;
        
}
        

/*---------------------------------------------------------------------------
        
  DibIsAddressable:  Returns TRUE if the DIB is not compressed
        
----------------------------------------------------------------------------*/
        

BOOL        DibIsAddressable (HDIB hdib)
        
{
        
           int iCompression ;
        

           if (!DibIsValid (hdib))
        
                          return FALSE ;
        

           iCompression = DibCompression (hdib) ;
        

           if (   iCompression == BI_RGB || iCompression == BI_BITFIELDS)
        
                          return TRUE ;
        

           return FALSE ;
        
}
        

/*---------------------------------------------------------------------------
        
  These functions return the sizes of various components of the DIB section
        
           AS THEY WOULD APPEAR in a packed DIB. These functions aid in converting
        
    the DIB section to a packed DIB and in saving DIB files.
        
-----------------------------------------------------------------------------*/
        

DWORD DibInfoHeaderSize (HDIB hdib)
        
{
        
           if (!DibIsValid (hdib))
        
                          return 0 ;
        
           return ((PDIBSTRUCT) hdib)->ds.dsBmih.biSize ;
        
}
        

DWORD       DibMaskSize (HDIB hdib)
        
{
        
         PDIBSTRUCT pdib = hdib ;
        
           if (!DibIsValid (hdib))
        
                          return 0 ;
        

    if (pdib->ds.dsBmih.biCompression == BI_BITFIELDS)
        
                          return 3 * sizeof (DWORD) ;
        

           return 0 ;
        
}
        

DWORD DibColorSize (HDIB hdib)
        
{
        
    return DibNumColors (hdib) * sizeof (RGBQUAD) ;
        
}
        

DWORD DibInfoSize (HDIB hdib)
        
{
        
           return DibInfoHeaderSize(hdib) + DibMaskSize(hdib) + DibColorSize(hdib) ;
        
}
        

DWORD DibBitsSize (HDIB hdib)
        
{
        
           PDIBSTRUCT pdib = hdib ;
        

           if (!DibIsValid (hdib))
        
                          return 0 ;
        

           if (pdib->ds.dsBmih.biSizeImage != 0)
        
    {
        
                          return pdib->ds.dsBmih.biSizeImage ;
        
    }
        
           return DibHeight (hdib) * DibRowLength (hdib) ;
        
}
        

DWORD DibTotalSize (HDIB hdib)
        
{
        
           return DibInfoSize (hdib) + DibBitsSize (hdib) ;
        
}
        

/*---------------------------------------------------------------------------
        
  These functions return pointers to the various components of the DIB
        
           section.
        
-----------------------------------------------------------------------------*/
        
BITMAPINFOHEADER * DibInfoHeaderPtr (HDIB hdib)
        
{
        
           if (!DibIsValid (hdib))
        
                          return NULL ;
        
           return & (((PDIBSTRUCT) hdib)->ds.dsBmih) ;
        
}
        

DWORD * DibMaskPtr (HDIB hdib)
        
{
        
           PDIBSTRUCT pdib = hdib ;
        
           if (!DibIsValid (hdib))
        
                        return 0 ;
        
           return pdib->ds.dsBitfields ;
        
}
        

void * DibBitsPtr (HDIB hdib)
        
{
        
           if (!DibIsValid (hdib))
        
                          return NULL ;
        
           return ((PDIBSTRUCT) hdib)->pBits ;
        
}
        

/*---------------------------------------------------------------------------
        
  DibSetColor:  Obtains entry from the DIB color table
        
-----------------------------------------------------------------------------*/
        

BOOL DibGetColor (HDIB hdib, int index, RGBQUAD * prgb)
        
{
        
           PDIBSTRUCT pdib = hdib ;
        
           HDC                                 hdcMem ;
        
           int                                  iReturn ;
        

           if (!DibIsValid (hdib))
        
                        return 0 ;
        
           hdcMem = CreateCompatibleDC (NULL) ;
        
           SelectObject (hdcMem, pdib->hBitmap) ;
        
           iReturn = GetDIBColorTable (hdcMem, index, 1, prgb) ;
        
           DeleteDC (hdcMem) ;
        
           return iReturn ? TRUE : FALSE ;
        
}
        

/*----------------------------------------------------------------------------
        
  DibGetColor:  Sets an entry in the DIB color table
        
----------------------------------------------------------------------------*/
        

BOOL DibSetColor (HDIB hdib, int index, RGBQUAD * prgb)
        
{
        
           PDIBSTRUCT    pdib = hdib ;
        
           HDC                           hdcMem ;
        
           int                           iReturn ;
        

           if (!DibIsValid (hdib))
        
                          return 0 ;
        
           hdcMem = CreateCompatibleDC (NULL) ;
        
           SelectObject (hdcMem, pdib->hBitmap) ;
        
           iReturn = SetDIBColorTable (hdcMem, index, 1, prgb) ;
        
           DeleteDC (hdcMem) ;
        

           return iReturn ? TRUE : FALSE ;
        
}
        

DIBHELP.C中的大部分函数是不用解释的.DibIsValid函数能有助于保护整个系统.在试图引用DIBSTRUCT中的信息之前,其它函数都呼叫DibIsValid.所有这些函数都有(而且通常是只有)HDIB型态的第一个参数,( 我们将立即看到)该参数在DIBHELP.H中定义为空指标.这些函数可以将此参数储存到PDIBSTRUCT,然后再存取结构中的字段.

注意传回BOOL值的DibIsAddressable函数.DibIsNotCompressed函数也可以呼叫此函数.传回值表示独立的DIB像素能否寻址.

以DibInfoHeaderSize开始的函数集将取得DIB区块中不同组件出现在packed DIB中的大小.与我们所看到的一样,这些函数有助于将DIB区块转换成packed DIB,并储存DIB文件.这些函数的后面是获得指向不同DIB组件的指针的函数集.

尽管DIBHELP.C包括名称为DibInfoHeaderPtr的函数,而且该函数将获得指向BITMAPINFOHEADER结构的指针,但还是没有函数可以获得BITMAPINFO结构指针-即接在DIB颜色表后面的信息结构.这是因为在处理DIB区块时,应用程序并不直接存取这种型态的结构.BITMAPINFOHEADER结构和颜色屏蔽都在DIBSECTION结构中有效,而且从CreateDIBSection函数传回指向像素位的指针,这时通过呼叫GetDIBColorTable和SetDIBColorTable,就只能间接存取DIB颜色表.这些功能都封装到DIBHELP的DibGetColor和DibSetColor函数里头了.

在DIBHELP.C的后面,文件DibCopyToInfo配置一个指向BITMAPINFO结构的指针,并填充信息,但是那与获得指向内存中现存结构的指针不完全相同.

读、写像素

应用程序维护packed DIB或DIB区块的一个引人注目的优点是能够直接操作DIB像素位.程序16-20所示的DIBHELP.C第二部分列出了提供此功能的函数.

程序16-20 DIBHELP.C文件的第二部分
        
DIBHELP.C (第二部分)
        
/*----------------------------------------------------------------------------
        
  DibPixelPtr:  Returns a pointer to the pixel at position (x, y)
        
-----------------------------------------------------------------------------*/
        
BYTE * DibPixelPtr (HDIB hdib, int x, int y)
        
{
        
           if (!DibIsAddressable (hdib))
        
                          return NULL ;
        
           if (x < 0 || x >= DibWidth (hdib) || y < 0 || y >= DibHeight (hdib))
        
                          return NULL ;
        
           return (((PDIBSTRUCT) hdib)->ppRow)[y] + (x * DibBitCount (hdib) >> 3) ;
        
}
        

/*---------------------------------------------------------------------------
        
  DibGetPixel:  Obtains a pixel value at (x, y)
        
-----------------------------------------------------------------------------*/
        

DWORD DibGetPixel (HDIB hdib, int x, int y)
        
{
        
           PBYTE pPixel ;
        
           if (!(pPixel = DibPixelPtr (hdib, x, y)))
        
                          return 0 ;
        
           switch (DibBitCount (hdib))
        
           {
        
           case          1:  return 0x01 & (* pPixel >> (7 - (x & 7))) ;
        
           case          4:  return 0x0F & (* pPixel >> (x & 1 ? 0 : 4)) ;
        
           case          8:  return * pPixel ;
        
           case          16: return * (WORD *) pPixel ;
        
           case          24: return 0x00FFFFFF & * (DWORD *) pPixel ;
        
           case          32: return * (DWORD *) pPixel ;
        
           }
        
  return 0 ;
        
}
        

/*-------------------------------------------------------------------------
        
  DibSetPixel:  Sets a pixel value at (x, y)
        
---------------------------------------------------------------------------*/
        

BOOL DibSetPixel (HDIB hdib, int x, int y, DWORD dwPixel)
        
{
        
           PBYTE pPixel ;
        
           if (!(pPixel = DibPixelPtr (hdib, x, y)))
        
                         return FALSE ;
        
           switch (DibBitCount (hdib))
        
    {
        
           case  1:      *      pPixel &= ~(1     << (7 - (x & 7))) ;
        
                          *      pPixel |= dwPixel     << (7 - (x & 7)) ;
        
                                                 break ;
        
           case  4:  * pPixel &= 0x0F    << (x & 1 ? 4 : 0) ;
        
                         *      pPixel |= dwPixel     << (x & 1 ? 0 : 4) ;
        
                                                 break ;
        
           case 8:       *      pPixel = (BYTE) dwPixel ;
        
                                                 break ;
        

           case 16:      *      (WORD *) pPixel = (WORD) dwPixel ;
        
                                                 break ;
        

           case 24:      *      (RGBTRIPLE *) pPixel = * (RGBTRIPLE *) &dwPixel ;
        
                                                 break ;
        

           case 32:      *      (DWORD *) pPixel = dwPixel ;
        
                                                 break ;
        
           default:
        
                          return FALSE ;
        
           }
        
           return TRUE ;
        
}
        

/*---------------------------------------------------------------------------
        
  DibGetPixelColor:  Obtains the pixel color at (x, y)
        
----------------------------------------------------------------------------*/
        

BOOL DibGetPixelColor (HDIB hdib, int x, int y, RGBQUAD * prgb)
        
{
        
          DWORD                        dwPixel ;
        
           int                           iBitCount ;
        
           PDIBSTRUCT    pdib = hdib ;
        

                          // Get bit count; also use this as a validity check
        

           if (0 == (iBitCount = DibBitCount (hdib)))
        
                          return FALSE ;
        
                          // Get the pixel value
        
         dwPixel = DibGetPixel (hdib, x, y) ;
        
                          // If the bit-count is 8 or less, index the color table
        
           if (iBitCount <= 8)
        
                          return DibGetColor (hdib, (int) dwPixel, prgb) ;
        
                          // If the bit-count is 24, just use the pixel
        
          else   if (iBitCount == 24)
        
    {
        
                  *      (RGBTRIPLE *) prgb = * (RGBTRIPLE *) & dwPixel ;
        
                          prgb->rgbReserved = 0 ;
        
   }
        

                  // If the bit-count is 32 and the biCompression field is BI_RGB,
        
                  //  just use the pixel
        

           else   if (iBitCount == 32 &&
        
                                                 pdib->ds.dsBmih.biCompression == BI_RGB)
        
           {
        
                  *      prgb = * (RGBQUAD *) & dwPixel ;
        
           }
        

                  // Otherwise, use the mask and shift values
        
                  //   (for best performance, don't use DibMask and DibShift functions)
        
           else
        
           {
        
           prgb->rgbRed = (BYTE)(((pdib->ds.dsBitfields[0] & dwPixel)
        
                    >> pdib->iRShift[0]) << pdib->iLShift[0]) ;
        

           prgb->rgbGreen=(BYTE((pdib->ds.dsBitfields[1] & dwPixel)
        
                   >> pdib->iRShift[1]) << pdib->iLShift[1]) ;
        

           prgb->rgbBlue=(BYTE)(((pdib->ds.dsBitfields[2] & dwPixel)
        
                    >> pdib->iRShift[2]) << pdib->iLShift[2]) ;
        
           }
        
   return TRUE ;
        
}
        

/*-----------------------------------------------------------------------------
        
  DibSetPixelColor:  Sets the pixel color at (x, y)
        
-----------------------------------------------------------------------------*/
        

BOOL DibSetPixelColor (HDIB hdib, int x, int y, RGBQUAD * prgb)
        
{
        
           DWORD                         dwPixel ;
        
           int                           iBitCount ;
        
           PDIBSTRUCT    pdib = hdib ;
        

                  // Don't do this function for DIBs with color tables
        

           iBitCount = DibBitCount (hdib) ;
        
           if (iBitCount <= 8)
        
                          return FALSE ;
        
                  // The rest is just the opposite of DibGetPixelColor
        
           else if (iBitCount == 24)
        
           {
        
                          * (RGBTRIPLE *) & dwPixel = * (RGBTRIPLE *) prgb ;
        
                          dwPixel &= 0x00FFFFFF ;
        
           }
        
           else if (iBitCount == 32 &&
        
                                                 pdib->ds.dsBmih.biCompression == BI_RGB)
        
    {
        
                          * (RGBQUAD *) & dwPixel = * prgb ;
        
    }
        

           else
        
    {
        
       dwPixel  = (((DWORD)     prgb->rgbRed >> pdib->iLShift[0])
        
       << pdib->iRShift[0]) ;
        

       dwPixel |= (((DWORD)     prgb->rgbGreen >> pdib->iLShift[1])
        
       << pdib->iRShift[1]) ;
        

       dwPixel |= (((DWORD)     prgb->rgbBlue >> pdib->iLShift[2])
        
       << pdib->iRShift[2]) ;
        
           }
        

           DibSetPixel (hdib, x, y, dwPixel) ;
        
          return TRUE ;
        
}
        

这部分DIBHELP.C从DibPixelPtr函数开始,该函数获得指向储存(或部分储存)有特殊像素的字节的指针.回想一下DIBSTRUCT结构的ppRow字段,那是个指向DIB中由顶行开始排列的像素行地址的指针.这样,

((PDIBSTRUCT) hdib)->pprow)[0]
        

就是指向DIB顶行最左端像素的指标,而

(((PDIBSTRUCT) hdib)->ppRow)[y] + (x * DibBitCount (hdib) >> 3)
        

是指向位于(x,y)的像素的指标.注意,如果DIB中的像素不可被寻址(即如果已压缩),或者如果函数的x和y参数是负数或相对位于DIB外面的区域,则函数将传回NULL.此检查降低了函数(和所有依赖于DibPixelPtr的函数)的执行速度,下面我将讲述一些更快的例程.

文件后面的DibGetPixel和DibSetPixel函数利用了DibPixelPtr.对于8位、16位和32位DIB,这些函数只记录指向合适数据尺寸的指针,并存取像素值.对于1位和4位的DIB,则需要屏蔽和移位角度.

DibGetColor函数按RGBQUAD结构获得像素颜色.对于1位、4位和8位DIB,这包括使用像素值来从DIB颜色表获得颜色.对于16位、24位和32位DIB,通常必须将像素值屏蔽和移位以得到RGB颜色.DibSetPixel函数则相反,它允许从RGBQUAD结构设定像素值.该函数只为16位、24位和32位DIB定义.

建立和转换

程序16-21所示的DIBHELP第三部分和最后部分展示了如何建立DIB区块,以及如何将DIB区块与packed DIB相互转换.

程序16-21 DIBHELP.C文件的第三部分和最后部分
        
DIBHELP.C (第三部分)
        
/*--------------------------------------------------------------------------
        
    Calculating shift values from color masks is required by the
        
    DibCreateFromInfo function.
        
----------------------------------------------------------------------------*/
        
static int MaskToRShift (DWORD dwMask)
        
{
        
           int iShift ;
        
           if (dwMask == 0)
        
                          return 0 ;
        
           for (iShift = 0 ; !(dwMask & 1) ; iShift++)
        
                          dwMask >>= 1 ;
        
           return iShift ;
        
}
        

static int MaskToLShift (DWORD dwMask)
        
{
        
           int iShift ;
        
           if (dwMask == 0)
        
                          return 0 ;
        
           while (!(dwMask & 1))
        
                          dwMask >>= 1 ;
        
           for (iShift = 0 ; dwMask & 1 ; iShift++)
        
                          dwMask >>= 1 ;
        
           return 8 - iShift ;
        
}
        
/*----------------------------------------------------------------------------
        
    DibCreateFromInfo: All DIB creation functions ultimately call this one.
        
    This function is responsible for calling CreateDIBSection, allocating
        
    memory for DIBSTRUCT, and setting up the row pointer.
        
-----------------------------------------------------------------------------*/
        

HDIB DibCreateFromInfo (BITMAPINFO * pbmi)
        
{
        
           BYTE                          *      pBits ;
        
           DIBSTRUCT     *      pdib ;
        
           HBITMAP                       hBitmap ;
        
           int                                 i, iRowLength, cy, y ;
        
   
        
           hBitmap = CreateDIBSection (NULL, pbmi, DIB_RGB_COLORS, &pBits, NULL, 0) ;
        
           if (hBitmap == NULL)
        
                          return NULL ;
        
           if (NULL == (pdib = malloc (sizeof (DIBSTRUCT))))
        
           {
        
                          DeleteObject (hBitmap) ;
        
                          return NULL ;
        
   }
        

           pdib->iSignature      = HDIB_SIGNATURE ;
        
           pdib->hBitmap         = hBitmap ;
        
           pdib->pBits           = pBits ;
        

           GetObject (hBitmap, sizeof (DIBSECTION), &pdib->ds) ;
        
                  // Notice that we can now use the DIB information functions
        
                  //   defined above.
        

                  // If the compression is BI_BITFIELDS, calculate shifts from masks
        

           if (DibCompression (pdib) == BI_BITFIELDS)
        
           {
        
                  for (i = 0 ; i < 3 ; i++)
        
                 {
        
                                  pdib->iLShift[i] = MaskToLShift (pdib->ds.dsBitfields[i]) ;
        
                                  pdib->iRShift[i] = MaskToRShift (pdib->ds.dsBitfields[i]) ;
        
                  }
        
           }
        

                  // If the compression is BI_RGB, but bit-count is 16 or 32,
        
                  //   set the bitfields and the masks
        
           else if (DibCompression (pdib) == BI_RGB)
        
           {
        
                  if (DibBitCount (pdib) == 16)
        
                  {
        
                                  pdib->ds.dsBitfields[0] = 0x00007C00 ;
        
                                 pdib->ds.dsBitfields[1] = 0x000003E0 ;
        
                                  pdib->ds.dsBitfields[2] = 0x0000001F ;
        

                                  pdib->iRShift [0]    =             10     ;
        
                                  pdib->iRShift [1]    =             5      ;
        
                                  pdib->iRShift [2]    =             0      ;
        

                                  pdib->iLShift [0]    =             3      ;
        
                                  pdib->iLShift [1]    =             3      ;
        
                                  pdib->iLShift [2]    =             3      ;
        
                  }
        
                  else if (DibBitCount (pdib) == 24 || DibBitCount (pdib) == 32)
        
                 {
        
                                  pdib->ds.dsBitfields[0] = 0x00FF0000 ;
        
                                  pdib->ds.dsBitfields[1] = 0x0000FF00 ;
        
                                  pdib->ds.dsBitfields[2] = 0x000000FF ;
        

                                  pdib->iRShift [0]    =             16     ;
        
                                  pdib->iRShift [1]    =             8      ;
        
                                  pdib->iRShift [2]    =             0      ;
        

                                  pdib->iLShift [0]    =             0      ;
        
                                  pdib->iLShift [1]    =             0      ;
        
                                  pdib->iLShift [2]    =             0      ;
        
                  }
        
    }
        
                  // Allocate an array of pointers to each row in the DIB
        
           cy = DibHeight (pdib) ;
        
           if (NULL == (pdib->ppRow = malloc (cy * sizeof (BYTE *))))
        
           {
        
                          free (pdib) ;
        
                          DeleteObject (hBitmap) ;
        
                          return NULL ;
        
           }
        

                  // Initialize them.
        
           iRowLength = DibRowLength (pdib) ;
        
           if (pbmi->bmiHeader.biHeight > 0)                           // ie, bottom up
        
           {
        
                          for (y = 0 ; y < cy ; y++)
        
                                                 pdib->ppRow[y] = pBits + (cy - y - 1) * iRowLength ;
        
           }
        
           else                                                                                           
        
// top down
        
    {
        
                          for (y = 0 ; y < cy ; y++)
        
                                               pdib->ppRow[y] = pBits + y * iRowLength ;
        
           }
        
           return pdib ;
        
}
        

/*--------------------------------------------------------------------------
        
  DibDelete:  Frees all memory for the DIB section
        
----------------------------------------------------------------------------*/
        

BOOL DibDelete (HDIB hdib)
        
{
        
    DIBSTRUCT * pdib = hdib ;
        
           if (!DibIsValid (hdib))
        
                          return FALSE ;
        
           free (pdib->ppRow) ;
        
           DeleteObject (pdib->hBitmap) ;
        
           free (pdib) ;
        
           return TRUE ;
        
}
        

/*----------------------------------------------------------------------------
        
  DibCreate: Creates an HDIB from explicit arguments
        
-----------------------------------------------------------------------------*/
        

HDIB DibCreate (int cx, int cy, int cBits, int cColors)
        
{
        
         BITMAPINFO    *      pbmi ;
        
           DWORD                                dwInfoSize ;
        
           HDIB                                 hDib ;
        
           int                                  cEntries ;
        

           if (cx <= 0 || cy <= 0 ||
        
                          ((cBits !=  1) && (cBits !=  4) && (cBits != 8) &&
        
                                  (cBits != 16) && (cBits != 24) && (cBits != 32)))
        
   {
        
                  return NULL ;
        
  }
        

   if (   cColors != 0)
        
                          cEntries = cColors ;
        
  else   if (cBits <= 8)
        
                          cEntries = 1 << cBits ;
        

           dwInfoSize = sizeof (BITMAPINFOHEADER) + (cEntries - 1) * sizeof (RGBQUAD);
        

           if (NULL == (pbmi = malloc (dwInfoSize)))
        
           {
        
                  return NULL ;
        
           }
        

           ZeroMemory (pbmi, dwInfoSize) ;
        

           pbmi->bmiHeader.biSize                       = sizeof (BITMAPINFOHEADER) ;
        
           pbmi->bmiHeader.biWidth                      = cx ;
        
           pbmi->bmiHeader.biHeight                     = cy ;
        
           pbmi->bmiHeader.biPlanes                     = 1 ;
        
           pbmi->bmiHeader.biBitCount                   = cBits ;
        
           pbmi->bmiHeader.biCompression                = BI_RGB ;
        
           pbmi->bmiHeader.biSizeImage                  = 0 ;
        
           pbmi->bmiHeader.biXPelsPerMeter              = 0 ;
        
           pbmi->bmiHeader.biYPelsPerMeter              = 0 ;
        
           pbmi->bmiHeader.biClrUsed                    = cColors ;
        
           pbmi->bmiHeader.biClrImportant               = 0 ;
        

           hDib = DibCreateFromInfo (pbmi) ;
        
           free (pbmi) ;
        

           return hDib ;
        
}
        

/*----------------------------------------------------------------------------
        
    DibCopyToInfo:                Builds BITMAPINFO structure.
        
                                                               Used by DibCopy and DibCopyToDdb
        
-----------------------------------------------------------------------------*/
        

static BITMAPINFO * DibCopyToInfo (HDIB hdib)
        
{
        
           BITMAPINFO            *      pbmi ;
        
           int                                          i, iNumColors ;
        
          RGBQUAD                       *      prgb ;
        
           if (!DibIsValid (hdib))
        
                          return NULL ;
        
                          // Allocate the memory
        
           if (NULL == (pbmi = malloc (DibInfoSize (hdib))))
        
                         return NULL ;
        
                          // Copy the information header
        
           CopyMemory (pbmi, DibInfoHeaderPtr   (hdib), sizeof (BITMAPINFOHEADER));
        
        
        
                          // Copy the possible color masks
        

           prgb = (RGBQUAD *) ((BYTE *) pbmi + sizeof (BITMAPINFOHEADER)) ;
        
           if (DibMaskSize (hdib))
        
           {
        
                          CopyMemory (prgb, DibMaskPtr (hdib), 3 * sizeof (DWORD)) ;
        
                          prgb = (RGBQUAD *) ((BYTE *) prgb + 3 * sizeof (DWORD)) ;
        
   }
        
                          // Copy the color table
        
           iNumColors = DibNumColors (hdib) ;
        
           for (i = 0 ; i < iNumColors ; i++)
        
                         DibGetColor (hdib, i, prgb + i) ;
        
           return pbmi ;
        
}
        

/*--------------------------------------------------------------------------
        
  DibCopy:  Creates a new DIB section from an existing DIB section,
        
                  possibly swapping the DIB width and height.
        
---------------------------------------------------------------------------*/
        

HDIB DibCopy (HDIB hdibSrc, BOOL fRotate)
        
{
        
           BITMAPINFO            *      pbmi ;
        
           BYTE                  *      pBitsSrc, * pBitsDst ;
        
           HDIB        hdibDst ;
        

           if (!DibIsValid (hdibSrc))
        
                          return NULL ;
        
           if (NULL == (pbmi = DibCopyToInfo (hdibSrc)))
        
                          return NULL ;
        
           if (fRotate)
        
           {
        
                          pbmi->bmiHeader.biWidth = DibHeight (hdibSrc) ;
        
                          pbmi->bmiHeader.biHeight = DibWidth (hdibSrc) ;
        
           }
        

           hdibDst = DibCreateFromInfo (pbmi) ;
        
           free (pbmi) ;
        

           if (   hdibDst == NULL)
        
                          return NULL ;
        

                                  // Copy the bits
        

           if (!fRotate)
        
           {
        
                                  pBitsSrc = DibBitsPtr (hdibSrc) ;
        
                                  pBitsDst = DibBitsPtr (hdibDst) ;
        

                                 CopyMemory (pBitsDst, pBitsSrc, DibBitsSize (hdibSrc)) ;
        
           }
        
    return hdibDst ;
        
}
        

/*----------------------------------------------------------------------------
        
  DibCopyToPackedDib is generally used for saving DIBs and for
        
   transferring DIBs to the clipboard. In the second case, the second
        
  argument should be set to TRUE so that the memory is allocated
        
  with the GMEM_SHARE flag.
        
-----------------------------------------------------------------------------*/
        

BITMAPINFO * DibCopyToPackedDib (HDIB hdib, BOOL fUseGlobal)
        
{
        
           BITMAPINFO            *      pPackedDib ;
        
           BYTE                  *      pBits ;
        
           DWORD                 dwDibSize ;
        
           HDC                   hdcMem ;
        
           HGLOBAL               hGlobal ;
        
           int                   iNumColors ;
        
           PDIBSTRUCT            pdib = hdib ;
        
           RGBQUAD               *      prgb ;
        

           if (!DibIsValid (hdib))
        
                          return NULL ;
        
                                  // Allocate memory for packed DIB
        
           dwDibSize = DibTotalSize (hdib) ;
        
           if (fUseGlobal)
        
           {
        
                          hGlobal = GlobalAlloc (GHND | GMEM_SHARE, dwDibSize) ;
        
                          pPackedDib = GlobalLock (hGlobal) ;
        
           }
        
           else
        
  {
        
                          pPackedDib = malloc (dwDibSize) ;
        
           }
        

           if (pPackedDib == NULL)
        
                          return NULL ;
        
                          // Copy the information header
        
           CopyMemory (pPackedDib, &pdib->ds.dsBmih, sizeof (BITMAPINFOHEADER)) ;
        
           prgb = (RGBQUAD *) ((BYTE *) pPackedDib + sizeof (BITMAPINFOHEADER)) ;
        
                         // Copy the possible color masks
        
           if (pdib->ds.dsBmih.biCompression == BI_BITFIELDS)
        
           {
        
                  CopyMemory (prgb, pdib->ds.dsBitfields, 3 * sizeof (DWORD)) ;
        
                  prgb = (RGBQUAD *) ((BYTE *) prgb + 3 * sizeof (DWORD)) ;
        
           }
        
                          // Copy the color table
        
           if (iNumColors = DibNumColors (hdib))
        
           {
        
                        hdcMem = CreateCompatibleDC (NULL) ;
        
                          SelectObject (hdcMem, pdib->hBitmap) ;
        
                          GetDIBColorTable (hdcMem, 0, iNumColors, prgb) ;
        
                        DeleteDC (hdcMem) ;
        
           }
        

           pBits = (BYTE *) (prgb + iNumColors) ;
        
                          // Copy the bits
        
           CopyMemory (pBits, pdib->pBits, DibBitsSize (pdib)) ;
        
                          // If last argument is TRUE, unlock global memory block and
        
                         //   cast it to pointer in preparation for return
        

           if (fUseGlobal)
        
           {
        
                          GlobalUnlock (hGlobal) ;
        
                          pPackedDib = (BITMAPINFO *) hGlobal ;   
        
           }
        
    return pPackedDib ;
        
}
        

/*--------------------------------------------------------------------------
        
  DibCopyFromPackedDib is generally used for pasting DIBs from the
        
    clipboard.
        
------------------------------------------------------------------------*/
        

HDIB DibCopyFromPackedDib (BITMAPINFO * pPackedDib)
        
{
        
           BYTE                          *      pBits ;   
        
           DWORD                                dwInfoSize, dwMaskSize, dwColorSize ;
        
           int                                  iBitCount ;
        
           PDIBSTRUCT            pdib ;
        

                          // Get the size of the information header and do validity check
        
   
        
           dwInfoSize = pPackedDib->bmiHeader.biSize ;
        
           if (   dwInfoSize != sizeof (BITMAPCOREHEADER) &&
        
                          dwInfoSize != sizeof (BITMAPINFOHEADER) &&
        
                          dwInfoSize != sizeof (BITMAPV4HEADER) &&
        
                          dwInfoSize != sizeof (BITMAPV5HEADER))
        
           {
        
                          return NULL ;
        
           }
        
                          // Get the possible size of the color masks
        

           if (dwInfoSize == sizeof (BITMAPINFOHEADER) &&
        
                          pPackedDib->bmiHeader.biCompression == BI_BITFIELDS)
        
           {
        
                          dwMaskSize = 3 * sizeof (DWORD) ;
        
           }
        
           else
        
           {
        
                          dwMaskSize = 0 ;
        
           }
        
                          // Get the size of the color table
        
           if (dwInfoSize == sizeof (BITMAPCOREHEADER))
        
           {
        
                          iBitCount = ((BITMAPCOREHEADER *) pPackedDib)->bcBitCount ;
        

                         if (iBitCount <= 8)
        
                          {
        
                          dwColorSize = (1 << iBitCount) * sizeof (RGBTRIPLE) ;
        
                          }
        
                          else
        
                                                 dwColorSize = 0 ;
        
  }
        
           else                                 // all non-OS/2 compatible DIBs
        
           {
        
                         if (pPackedDib->bmiHeader.biClrUsed > 0)
        
                          {
        
             dwColorSize = pPackedDib->bmiHeader.biClrUsed * sizeof (RGBQUAD);
        
                          }
        
                          else if (pPackedDib->bmiHeader.biBitCount <= 8)
        
                          {
        
                                                 dwColorSize = (1 << pPackedDib->bmiHeader.biBitCount) * sizeof (RGBQUAD) ;
        
                          }
        
                          else
        
                          {
        
                                                 dwColorSize = 0 ;
        
                          }
        
           }
        
                         // Finally, get the pointer to the bits in the packed DIB
        
          pBits = (BYTE *) pPackedDib + dwInfoSize + dwMaskSize + dwColorSize ;
        
                          // Create the HDIB from the packed-DIB pointer
        
           pdib = DibCreateFromInfo (pPackedDib) ;
        
                         // Copy the pixel bits
        
           CopyMemory (pdib->pBits, pBits, DibBitsSize (pdib)) ;
        
           return pdib ;
        
}
        

/*----------------------------------------------------------------------------
        
  DibFileLoad:  Creates a DIB section from a DIB file
        
-----------------------------------------------------------------------------*/
        
HDIB DibFileLoad (const TCHAR * szFileName)
        
{
        
           BITMAPFILEHEADER              bmfh ;
        
           BITMAPINFO                    *      pbmi ;
        
           BOOL                                                        bSuccess ;
        
           DWORD                                                       dwInfoSize, dwBitsSize, dwBytesRead ;
        
           HANDLE                                               hFile ;
        
           HDIB                                                        hDib ;
        

            // 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 information structure & read it in
        
           dwInfoSize = bmfh.bfOffBits - sizeof (BITMAPFILEHEADER) ;
        
           if (NULL == (pbmi = malloc (dwInfoSize)))
        
           {
        
                          CloseHandle (hFile) ;
        
                          return NULL ;
        
           }
        

           bSuccess = ReadFile (hFile, pbmi, dwInfoSize, &dwBytesRead, NULL) ;
        
           if (!bSuccess || (dwBytesRead != dwInfoSize))
        
    {
        
                  CloseHandle (hFile) ;
        
                  free (pbmi) ;
        
                  return NULL ;
        
           }
        
                  // Create the DIB
        
           hDib = DibCreateFromInfo (pbmi) ;
        
           free (pbmi) ;
        

           if (hDib == NULL)
        
           {
        
                  CloseHandle (hFile) ;
        
                  return NULL ;
        
           }
        
                  // Read in the bits
        
           dwBitsSize = bmfh.bfSize - bmfh.bfOffBits ;
        
           bSuccess = ReadFile ( hFile, ((PDIBSTRUCT) hDib)->pBits,
        
                                dwBitsSize, &dwBytesRead, NULL) ;
        
           CloseHandle (hFile) ;
        
           if (!bSuccess || (dwBytesRead != dwBitsSize))
        
           {
        
                  DibDelete (hDib) ;
        
                  return NULL ;
        
           }
        
           return hDib ;
        
}
        

/*--------------------------------------------------------------------------
        
  DibFileSave:  Saves a DIB section to a file
        
----------------------------------------------------------------------------*/
        

BOOL DibFileSave (HDIB hdib, const TCHAR * szFileName)
        
{
        
           BITMAPFILEHEADER              bmfh ;
        
           BITMAPINFO                    *      pbmi ;
        
           BOOL                          bSuccess ;
        
           DWORD                         dwTotalSize, dwBytesWritten ;
        
           HANDLE                        hFile ;
        

           hFile = CreateFile (szFileName, GENERIC_WRITE, 0, NULL,
        
                                  CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL) ;
        
           if (hFile == INVALID_HANDLE_VALUE)
        
                          return FALSE ;
        
           dwTotalSize                          = DibTotalSize (hdib) ;
        
           bmfh.bfType                          = * (WORD *) "BM" ;
        
           bmfh.bfSize                         = sizeof (BITMAPFILEHEADER) + dwTotalSize ;
        
           bmfh.bfReserved1              = 0 ;
        
           bmfh.bfReserved2              = 0 ;
        
           bmfh.bfOffBits                = bmfh.bfSize - DibBitsSize (hdib) ;
        

                          // Write the BITMAPFILEHEADER
        

           bSuccess = WriteFile (hFile, &bmfh, sizeof (BITMAPFILEHEADER),
        
                                  &dwBytesWritten, NULL) ;
        

           if (!bSuccess || (dwBytesWritten != sizeof (BITMAPFILEHEADER)))
        
           {
        
                          CloseHandle (hFile) ;
        
                          DeleteFile (szFileName) ;
        
                          return FALSE ;
        
           }
        
                          // Get entire DIB in packed-DIB format
        
           if (NULL == (pbmi = DibCopyToPackedDib (hdib, FALSE)))
        
    {
        
                          CloseHandle (hFile) ;
        
                          DeleteFile (szFileName) ;
        
                          return FALSE ;
        
           }
        
                         // Write out the packed DIB
        
           bSuccess = WriteFile (hFile, pbmi, dwTotalSize, &dwBytesWritten, NULL) ;
        
           CloseHandle (hFile) ;
        
           free (pbmi) ;
        

           if (!bSuccess || (dwBytesWritten != dwTotalSize))
        
           {
        
                          DeleteFile (szFileName) ;
        
                          return FALSE ;
        
           }
        
           return TRUE ;
        
}
        

/*---------------------------------------------------------------------------
        
  DibCopyToDdb:  For more efficient screen displays
        
---------------------------------------------------------------------------*/
        
HBITMAP DibCopyToDdb (HDIB hdib, HWND hwnd, HPALETTE hPalette)
        
{
        
           BITMAPINFO            *      pbmi ;
        
           HBITMAP                              hBitmap ;
        
           HDC                                          hdc ;
        

           if (!DibIsValid (hdib))
        
                          return NULL ;
        
           if (NULL == (pbmi = DibCopyToInfo (hdib)))
        
                          return NULL ;
        
           hdc = GetDC (hwnd) ;
        
           if (hPalette)
        
           {
        
                          SelectPalette (hdc, hPalette, FALSE) ;
        
                          RealizePalette (hdc) ;
        
           }
        
   
        
           hBitmap = CreateDIBitmap (hdc, DibInfoHeaderPtr (hdib), CBM_INIT,
        
                                                                   DibBitsPtr (hdib), pbmi, DIB_RGB_COLORS) ;
        

           ReleaseDC (hwnd, hdc) ;
        
           free (pbmi) ;
        

           return hBitmap ;
        
}
        

这部分的DIBHELP.C文件从两个小函数开始,这两个函数根据16位和32位DIB的颜色屏蔽得到左、右移位值.这些函数在 第十五章「颜色屏蔽」一节说明.

DibCreateFromInfo函数是DIBHELP中唯一呼叫CreateDIBSection并为DIBSTRUCT结构配置内存的函数.其它所有建立和复制函数都重复此函数.DibCreateFromInfo唯一的参数是指向BITMAPINFO结构的指针.此结构的颜色表必须存在,但是它不必用有效的值填充.呼叫CreateDIBSection之后,该函数将初始化DIBSTRUCT结构的所有字段.注意,在设定DIBSTRUCT结构的ppRow字段的值时(指向DIB行地址的指针),DIB有由下而上和由上而下的不同储存方式.ppRow开头的元素就是DIB的顶行.

DibDelete删除DibCreateFromInfo中建立的位图,同时释放在该函数中配置的内存.

DibCreate可能比DibCreateFromInfo更像一个从应用程序呼叫的函数.前三个参数提供像素的宽度、高度和每像素的位数.最后一个参数可以设定为0(用于颜色表的内定尺寸),或者设定为非0(表示比每像素位数所需要的颜色表更小的颜色表).

DibCopy函数根据现存的DIB区块建立新的DIB区块,并用DibCreateInfo函数为BITMAPINFO结构配置了内存,还填了所有的数据.DibCopy函数的一个BOOL参数指出是否在建立新的DIB时交换了DIB的宽度和高度.我们将在后面看到此函数的用法.

DibCopyToPackedDib和DibCopyFromPackedDib函数的使用通常与透过剪贴簿传递DIB相关.DibFileLoad函数从DIB文件建立DIB区块;DibFileSave函数将数据储存到DIB文件.

最后,DibCopyToDdb函数根据DIB建立GDI位图对象.注意,该函数需要目前调色板的句柄和程序窗口的句柄.程序窗口句柄用于获得选进并显现调色板的设备内容.只有这样,函数才可以呼叫CreateDIBitmap.这曾在本章前面的SHOWDIB7中展示.

DIBHELP表头文件和宏

DIBHELP.H表头文件如程序16-22所示.

程序16-22 DIBHELP.H文件
        
DIBHELP.H
        
/*--------------------------------------------------------------------------
        
  DIBHELP.H header file for DIBHELP.C
        
----------------------------------------------------------------------------*/
        
typedef void * HDIB ;
        
           // Functions in DIBHELP.C
        
BOOL DibIsValid (HDIB hdib) ;     
        
HBITMAP DibBitmapHandle (HDIB hdib) ;
        
int DibWidth (HDIB hdib) ;
        
int DibHeight (HDIB hdib) ;
        
int DibBitCount (HDIB hdib) ;
        
int DibRowLength (HDIB hdib) ;
        
int DibNumColors (HDIB hdib) ;
        
DWORD DibMask (HDIB hdib, int i) ;
        
int DibRShift (HDIB hdib, int i) ;
        
int DibLShift (HDIB hdib, int i) ;
        
int DibCompression (HDIB hdib) ;
        
BOOL DibIsAddressable (HDIB hdib) ;
        
DWORD DibInfoHeaderSize (HDIB hdib) ;
        
DWORD DibMaskSize (HDIB hdib) ;
        
DWORD DibColorSize (HDIB hdib) ;
        
DWORD DibInfoSize (HDIB hdib) ;
        
DWORD DibBitsSize (HDIB hdib) ;
        
DWORD DibTotalSize (HDIB hdib) ;
        
BITMAPINFOHEADER * DibInfoHeaderPtr (HDIB hdib) ;
        
DWORD * DibMaskPtr (HDIB hdib) ;
        
void * DibBitsPtr (HDIB hdib) ;
        
BOOL DibGetColor (HDIB hdib, int index, RGBQUAD * prgb) ;
        
BOOL DibSetColor (HDIB hdib, int index, RGBQUAD * prgb) ;
        
BYTE * DibPixelPtr (HDIB hdib, int x, int y) ;
        
DWORD DibGetPixel (HDIB hdib, int x, int y) ;
        
BOOL DibSetPixel (HDIB hdib, int x, int y, DWORD dwPixel) ;
        
BOOL DibGetPixelColor (HDIB hdib, int x, int y, RGBQUAD * prgb) ;
        
BOOL DibSetPixelColor (HDIB hdib, int x, int y, RGBQUAD * prgb) ;
        
HDIB DibCreateFromInfo (BITMAPINFO * pbmi) ;
        
BOOL DibDelete (HDIB hdib) ;
        
HDIB DibCreate (int cx, int cy, int cBits, int cColors) ;
        
HDIB DibCopy (HDIB hdibSrc, BOOL fRotate) ;
        
BITMAPINFO * DibCopyToPackedDib (HDIB hdib, BOOL fUseGlobal) ;
        
HDIB DibCopyFromPackedDib (BITMAPINFO * pPackedDib) ;
        
HDIB DibFileLoad (const TCHAR * szFileName) ;
        
BOOL DibFileSave (HDIB hdib, const TCHAR * szFileName) ;
        
HBITMAP DibCopyToDdb (HDIB hdib, HWND hwnd, HPALETTE hPalette) ;
        
HDIB DibCreateFromDdb (HBITMAP hBitmap) ;
        

/*---------------------------------------------------------------------------
        
  Quickie no-bounds-checked pixel gets and sets
        
-----------------------------------------------------------------------------*/
        

#define     DibPixelPtr1(hdib, x, y)      (((* (PBYTE **) hdib) [y]) + ((x) >> 3))
        
#define     DibPixelPtr4(hdib, x, y)      (((* (PBYTE **) hdib) [y]) + ((x) >> 1))
        
#define     DibPixelPtr8(hdib, x, y)      (((* (PBYTE **) hdib) [y]) +  (x)            )
        
#define     DibPixelPtr16(hdib, x, y)  \
        
                                                 ((WORD *) (((* (PBYTE **) hdib) [y]) + (x) *  2))
        

#define DibPixelPtr24(hdib, x, y)  \
        
                                                         ((RGBTRIPLE *) (((* (PBYTE **) hdib) [y]) +  (x) *  3))
        
#define DibPixelPtr32(hdib, x, y)  \
        
                                                                ((DWORD *) (((* (PBYTE **) hdib) [y]) +  (x) *  4))
        

#define DibGetPixel1(hdib, x, y)   \
        
                                         (0x01 & (* DibPixelPtr1 (hdib, x, y) >> (7 - ((x) & 7))))
        

#define DibGetPixel4(hdib, x, y)   \
        
                                         (0x0F & (* DibPixelPtr4 (hdib, x, y) >> ((x) & 1 ? 0 : 4)))
        

#define     DibGetPixel8(hdib, x, y)                     (* DibPixelPtr8                   (hdib, x, y))
        
#define     DibGetPixel16(hdib, x, y)     (* DibPixelPtr16      (hdib, x, y))
        
#define     DibGetPixel24(hdib, x, y)    (* DibPixelPtr24      (hdib, x, y))
        
#define     DibGetPixel32(hdib, x, y)    (* DibPixelPtr32      (hdib, x, y))
        

#define DibSetPixel1(hdib, x, y, p)                                                                                          \
        
                  ((* DibPixelPtr1 (hdib, x, y) &= ~( 1<< (7 - ((x) & 7)))),             \
        
                  (* DibPixelPtr1 (hdib, x, y) |=      ((p) << (7 - ((x) & 7)))))
        

#define DibSetPixel4(hdib, x, y, p)                                                                                          \
        
                  ((* DibPixelPtr4 (hdib, x, y) &= (0x0F <<    ((x) & 1 ? 4 : 0))),  \
        
                  (* DibPixelPtr4 (hdib, x, y) |= ((p) <<     ((x) & 1 ? 0 : 4))))
        

#define DibSetPixel8(hdib, x, y, p)  (* DibPixelPtr8 (hdib, x, y) = p)
        
#define DibSetPixel16(hdib, x, y, p) (* DibPixelPtr16 (hdib, x, y) = p)
        
#define DibSetPixel24(hdib, x, y, p) (* DibPixelPtr24 (hdib, x, y) = p)
        
#define DibSetPixel32(hdib, x, y, p) (* DibPixelPtr32 (hdib, x, y) = p)
        

这个表头文件将HDIB定义为空指标(void* ).应用程序的确不需要了解HDIB所指结构的内部结构.此表头文件还包括DIBHELP.C中所有函数的说明,还有一些宏-非常特殊的宏.

如果再看一看DIBHELP.C中的DibPixelPtr、DibGetPixel和DibSetPixel函数,并试图提高它们的执行速度表现,那么您将看到两种可能的解决方法.第一种,可以删除所有的检查保护,并相信应用程序不会使用无效参数呼叫函数.还可以删除一些函数呼叫,例如DibBitCount,并使用指向DIBSTRUCT结构内部的指针来直接获得信息.

提高执行速度表现另一项较不明显的方法是删除所有对每像素位数的处理方式,同时分离出处理不同DIB函数-例如DibGetPixel1、DibGetPixel4、DibGetPixel8等等.下一个最佳化步骤是删除整个函数呼叫,将其处理动作透过inline function或宏中进行合并.

DIBHELP.H采用宏的方法.它依据DibPixelPtr、DibGetPixel和DibSetPixel函数提出了三套宏.这些宏都明确对应于特殊的像素位数.

DIBBLE程序

DIBBLE程序,如程序16-23所示,使用DIBHELP函数和宏工作.尽管DIBBLE是本书中最长的程序,它确实只是一些作业的粗略范例,这些作业可以在简单的数字影像处理程序中找到.对DIBBLE的明显改进是转换成了多重文件接口(MDI:multiple document interface),我们将在第十九章学习有关多重文件接口的知识.

程序16-23 DIBBLE
        
DIBBLE.C
        
/*---------------------------------------------------------------------------
        
  DIBBLE.C --   Bitmap and Palette Program
        
                                                        (c) Charles Petzold, 1998
        
-----------------------------------------------------------------------------*/
        
#include <windows.h>
        
#include "dibhelp.h"
        
#include "dibpal.h"
        
#include "dibconv.h"
        
#include "resource.h"
        

#define WM_USER_SETSCROLLS                (WM_USER + 1)
        
#define WM_USER_DELETEDIB                 (WM_USER + 2)
        
#define WM_USER_DELETEPAL                 (WM_USER + 3)
        
#define WM_USER_CREATEPAL                 (WM_USER + 4)
        

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
        
TCHAR szAppName[] = TEXT ("Dibble") ;
        
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
        
                                                                PSTR szCmdLine, int iCmdShow)
        
{
        
         HACCEL                hAccel ;
        
           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, szAppName,
        
               WS_OVERLAPPEDWINDOW | WM_VSCROLL | WM_HSCROLL,
        
              CW_USEDEFAULT, CW_USEDEFAULT,
        
              CW_USEDEFAULT, CW_USEDEFAULT,
        
               NULL, NULL, hInstance, NULL) ;
        

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

           hAccel = LoadAccelerators (hInstance, szAppName) ;
        

           while (GetMessage (&msg, NULL, 0, 0))
        
          {
        
                  if (!TranslateAccelerator (hwnd, hAccel, &msg))
        
                  {
        
                                         TranslateMessage (&msg) ;
        
                                         DispatchMessage (&msg) ;
        
                  }
        
           }
        
           return msg.wParam ;
        
}
        

/*----------------------------------------------------------------------------
        
  DisplayDib:           Displays or prints DIB actual size or stretched
        
                                                         depending on menu selection
        
-----------------------------------------------------------------------------*/
        

int DisplayDib (   HDC hdc, HBITMAP hBitmap, int x, int y,
        
                                                         int cxClient, int cyClient,
        
                                                         WORD wShow, BOOL fHalftonePalette)
        
{
        
           BITMAP        bitmap ;
        
           HDC                   hdcMem ;
        
           int                  cxBitmap, cyBitmap, iReturn ;
        
           GetObject     (hBitmap, sizeof (BITMAP), &bitmap) ;
        
           cxBitmap              =      bitmap.bmWidth ;
        
           cyBitmap              =      bitmap.bmHeight ;
        

           SaveDC (hdc) ;
        
           if (fHalftonePalette)
        
                          SetStretchBltMode (hdc, HALFTONE) ;
        
         else
        
                          SetStretchBltMode (hdc, COLORONCOLOR) ;
        
           hdcMem = CreateCompatibleDC (hdc) ;
        
           SelectObject (hdcMem, hBitmap) ;
        

           switch (wShow)
        
           {
        
           case   IDM_SHOW_NORMAL:
        
                          if (fHalftonePalette)
        
                                  iReturn = StretchBlt (hdc,    0, 0, min (cxClient, cxBitmap - x),
        
                                   min (cyClient, cyBitmap - y),
        
                   hdcMem, x, y, min (cxClient, cxBitmap - x),
        
                                 min (cyClient, cyBitmap - y),
        
                   SRCCOPY);
        
                          else
        
                                  iReturn = BitBlt (hdc,0, 0, min (cxClient, cxBitmap - x),
        
                       min (cyClient, cyBitmap - y),
        
hdcMem, x, y, SRCCOPY) ;
        
                          break ;
        
             
        
           case   IDM_SHOW_CENTER:
        
                         if (fHalftonePalette)
        
                          iReturn = StretchBlt (hdc, (cxClient - cxBitmap) / 2,
        
                                        (cyClient - cyBitmap) / 2,
        
cxBitmap, cyBitmap,
        
                             hdcMem, 0, 0, cxBitmap, cyBitmap, SRCCOPY);
        
                          else
        
                                         iReturn = BitBlt (hdc,        (cxClient - cxBitmap) / 2,
        
                          cyClient - cyBitmap) / 2,
        
                           cxBitmap, cyBitmap,
        
                           hdcMem, 0, 0, SRCCOPY) ;
        
                          break ;
        
           case   IDM_SHOW_STRETCH:
        
                          iReturn = StretchBlt (hdc,    0, 0, cxClient, cyClient,
        
                   hdcMem,        0, 0, cxBitmap, cyBitmap, SRCCOPY) ;
        
                          break ;
        

           case   IDM_SHOW_ISOSTRETCH:
        
                        SetMapMode (hdc, MM_ISOTROPIC) ;
        
                          SetWindowExtEx (hdc, cxBitmap, cyBitmap, NULL) ;
        
                          SetViewportExtEx (hdc, cxClient, cyClient, NULL) ;
        
                          SetWindowOrgEx (hdc, cxBitmap / 2, cyBitmap / 2, NULL) ;
        
                          SetViewportOrgEx (hdc, cxClient / 2, cyClient / 2, NULL) ;
        

                          iReturn = StretchBlt (hdc,    0, 0, cxBitmap, cyBitmap,
        
                  hdcMem,0, 0, cxBitmap, cyBitmap, SRCCOPY) ;
        
                          break ;
        
           }
        
    DeleteDC (hdcMem) ;
        
    RestoreDC (hdc, -1) ;
        
    return iReturn ;
        
}
        

/*---------------------------------------------------------------------------
        
  DibFlipHorizontal: Calls non-optimized DibSetPixel and DibGetPixel
        
----------------------------------------------------------------------------*/
        

HDIB DibFlipHorizontal (HDIB hdibSrc)
        
{
        
           HDIB hdibDst ;
        
           int  cx, cy, x, y ;
        

           if (!DibIsAddressable (hdibSrc))
        
                          return NULL ;
        
          if (NULL == (hdibDst = DibCopy (hdibSrc, FALSE)))
        
                          return NULL ;
        
           cx = DibWidth  (hdibSrc) ;
        
           cy = DibHeight (hdibSrc) ;
        
   
        
    for (x = 0 ; x < cx ; x++)
        
           for (y = 0 ; y < cy ; y++)
        
           {
        
                  DibSetPixel (hdibDst, x, cy - 1 - y, DibGetPixel (hdibSrc, x, y)) ;
        
           }
        

           return hdibDst ;
        
}
        

/*---------------------------------------------------------------------------
        
  DibRotateRight: Calls optimized DibSetPixelx and DibGetPixelx
        
-----------------------------------------------------------------------------*/
        

HDIB DibRotateRight (HDIB hdibSrc)
        
{
        
           HDIB   hdibDst ;
        
           int    cx, cy, x, y ;
        

           if (!DibIsAddressable (hdibSrc))
        
                          return NULL ;
        

           if (NULL == (hdibDst = DibCopy (hdibSrc, TRUE)))
        
                          return NULL ;
        

          cx = DibWidth (hdibSrc) ;
        
           cy = DibHeight (hdibSrc) ;
        

           switch (DibBitCount (hdibSrc))
        
    {
        
           case          1:
        
                                  for (  x = 0 ; x < cx ; x++)
        
                                  for (  y = 0 ; y < cy ; y++)
        
                                                                DibSetPixel1 (hdibDst, cy - y - 1, x,
        
                                                               DibGetPixel1 (hdibSrc, x, y)) ;
        
                                  break ;
        

           case          4:
        
                                  for (  x = 0 ; x < cx ; x++)
        
                                  for (  y = 0 ; y < cy ; y++)
        
                                                                DibSetPixel4 (hdibDst, cy - y - 1, x,
        
                                                              DibGetPixel4 (hdibSrc, x, y)) ;
        
                                 break ;
        

           case          8:
        
                                  for (  x = 0 ; x < cx ; x++)
        
                                  for (  y = 0 ; y < cy ; y++)
        
                                                                DibSetPixel8 (hdibDst, cy - y - 1, x,
        
                                                               DibGetPixel8 (hdibSrc, x, y)) ;
        
                                  break ;
        
           case          16:
        
                                  for (x = 0 ; x < cx ; x++)
        
                                  for (y = 0 ; y < cy ; y++)
        
                                                         DibSetPixel16 (hdibDst, cy - y - 1, x,
        
                                                                DibGetPixel16 (hdibSrc, x, y)) ;
        
                                  break ;
        

          case          24:
        
                                  for (x = 0 ; x < cx ; x++)
        
                                  for (y = 0 ; y < cy ; y++)
        
                                                         DibSetPixel24 (hdibDst, cy - y - 1, x,
        
                                                                DibGetPixel24 (hdibSrc, x, y)) ;
        
                                  break ;
        

           case          32:
        
                                  for (x = 0 ; x < cx ; x++)
        
                                  for (y = 0 ; y < cy ; y++)
        
                                                         DibSetPixel32 (hdibDst, cy - y - 1, x,
        
                                                                DibGetPixel32 (hdibSrc, x, y)) ;
        
                                  break ;
        
           }
        
           return hdibDst ;
        
}
        

/*------------------------------------------------------------------------
        
  PaletteMenu: Uncheck and check menu item on palette menu
        
--------------------------------------------------------------------------*/
        

void PaletteMenu (HMENU hMenu, WORD wItemNew)
        
{
        
           static WORD wItem = IDM_PAL_NONE ;
        
           CheckMenuItem (hMenu, wItem, MF_UNCHECKED) ;
        
           wItem = wItemNew ;
        
           CheckMenuItem (hMenu, wItem, MF_CHECKED) ;
        
}
        

LRESULT CALLBACK WndProc ( HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam)
        
{
        
          static BOOL                                 fHalftonePalette ;
        
           static        DOCINFO                       di = {sizeof(DOCINFO),TEXT("Dibble:Printing")} ;
        
           static HBITMAP               hBitmap ;
        
           static HDIB                                 hdib ;
        
           static HMENU                                hMenu ;
        
           static HPALETTE                      hPalette ;
        
           static int                                  cxClient, cyClient, iVscroll, iHscroll ;
        
           static OPENFILENAME ofn ;
        
           static PRINTDLG                      printdlg = { sizeof (PRINTDLG) } ;
        
           static TCHAR                              szFileName [MAX_PATH], szTitleName [MAX_PATH] ;
        
          static TCHAR                                szFilter[]=   TEXT ("Bitmap Files (*.BMP)\0*.bmp\0")
        
              TEXT ("All Files (*.*)\0*.*\0\0") ;
        
           static TCHAR                  *             szCompression[] = {
        
                        TEXT("BI_RGB"),TEXT("BI_RLE8"),TEXT("BI_RLE4"),
        
      TEXT("BI_BITFIELDS"),TEXT("Unknown")} ;
        
           static WORD           wShow = IDM_SHOW_NORMAL ;
        
           BOOL                  fSuccess ;
        
           BYTE                          *             pGlobal ;
        
           HDC                  hdc, hdcPrn ;
        
           HGLOBAL               hGlobal ;
        
           HDIB                 hdibNew ;
        
           int                   iEnable, cxPage, cyPage, iConvert ;
        
           PAINTSTRUCT           ps ;
        
           SCROLLINFO            si ;
        
           TCHAR                 szBuffer [256] ;
        

           switch (message)
        
           {
        
           case   WM_CREATE:
        
       
        
                                         // Save the menu handle in a static variable
        

                          hMenu = GetMenu (hwnd) ;
        

                          // Initialize the OPENFILENAME structure for the File Open
        
                         //   and File Save dialog boxes.
        

                          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             = OFN_OVERWRITEPROMPT ;
        
                          ofn.nFileOffset       = 0 ;
        
                          ofn.nFileExtension    = 0 ;
        
                          ofn.lpstrDefExt       = TEXT ("bmp") ;
        
                          ofn.lCustData         = 0 ;
        
                          ofn.lpfnHook          = NULL ;
        
                          ofn.lpTemplateName    = NULL ;
        
                          return 0 ;
        

          case   WM_DISPLAYCHANGE:
        
                          SendMessage (hwnd, WM_USER_DELETEPAL, 0, 0) ;
        
                          SendMessage (hwnd, WM_USER_CREATEPAL, TRUE, 0) ;
        
                          return 0 ;
        

           case   WM_SIZE:
        
                                  // Save the client area width and height in static variables.
        

                          cxClient = LOWORD (lParam) ;
        
                          cyClient = HIWORD (lParam) ;
        

                          wParam = FALSE ;
        
                       // fall through
        
                                  // WM_USER_SETSCROLLS:  Programmer-defined Message!
        
                                 // Set the scroll bars. If the display mode is not normal,
        
                                  //   make them invisible. If wParam is TRUE, reset the
        
                                  //   scroll bar position.
        

           case   WM_USER_SETSCROLLS:
        
                          if (hdib == NULL || wShow != IDM_SHOW_NORMAL)
        
                          {
        
                                                 si.cbSize             = sizeof (SCROLLINFO) ;
        
                                                 si.fMask              = SIF_RANGE ;
        
                                                si.nMin               = 0 ;
        
                                                 si.nMax               = 0 ;
        
                                                 SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;
        
                                                 SetScrollInfo (hwnd, SB_HORZ, &si, TRUE) ;
        
                          }
        
                          else
        
                          {
        
                                                // First the vertical scroll
        

                                                 si.cbSize             = sizeof (SCROLLINFO) ;
        
                                                 si.fMask                      = SIF_ALL ;
        

                                         GetScrollInfo (hwnd, SB_VERT, &si) ;
        
                                                 si.nMin                       = 0 ;
        
                                                 si.nMax                      = DibHeight (hdib) ;
        
                                                 si.nPage                      = cyClient ;
        
                                                 if ((BOOL) wParam)
        
                                                                si.nPos = 0 ;
        

                                                 SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;
        
                                                 GetScrollInfo (hwnd, SB_VERT, &si) ;
        

                                                 iVscroll = si.nPos ;
        

                                                         // Then the horizontal scroll
        

                                                 GetScrollInfo (hwnd, SB_HORZ, &si) ;
        
                                                 si.nMin               = 0 ;
        
                                                 si.nMax               = DibWidth (hdib) ;
        
                                                si.nPage              = cxClient ;
        
        
        
                                                 if ((BOOL) wParam)
        
                                                                si.nPos = 0 ;
        

                                                 SetScrollInfo (hwnd, SB_HORZ, &si, TRUE) ;
        
                                                 GetScrollInfo (hwnd, SB_HORZ, &si) ;
        

                                                 iHscroll = si.nPos ;
        
                          }     
        
                          return 0 ;
        

                          // WM_VSCROLL: Vertically scroll the DIB
        

           case   WM_VSCROLL:
        
                          si.cbSize = sizeof (SCROLLINFO) ;
        
                          si.fMask  = SIF_ALL ;
        
                          GetScrollInfo (hwnd, SB_VERT, &si) ;
        
        
        
                          iVscroll = si.nPos ;
        

                          switch (LOWORD (wParam))
        
                          {
        
                          case SB_LINEUP:    si.nPos -  = 1 ;         break ;
        
                          case SB_LINEDOWN:  si.nPos +  = 1 ;         break ;
        
                          case SB_PAGEUP:    si.nPos -  = si.nPage ;break ;
        
                         case SB_PAGEDOWN:  si.nPos +  = si.nPage ;break ;
        
                          case SB_THUMBTRACK:si.nPos           = si.nTrackPos ;break ;
        
                          default:                                                                  break ;
        
                          }
        
                          si.fMask = SIF_POS ;
        
                          SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;
        
                          GetScrollInfo (hwnd, SB_VERT, &si) ;
        
                          if (si.nPos != iVscroll)
        
                          {
        
                                        ScrollWindow (hwnd, 0, iVscroll - si.nPos, NULL, NULL) ;
        
                                         iVscroll = si.nPos ;
        
                                        UpdateWindow (hwnd) ;
        
                          }
        
                          return 0 ;
        

                          // WM_HSCROLL: Horizontally scroll the DIB
        

           case   WM_HSCROLL:
        
                          si.cbSize = sizeof (SCROLLINFO) ;
        
                          si.fMask  = SIF_ALL ;
        
                          GetScrollInfo (hwnd, SB_HORZ, &si) ;
        
        
        
                         iHscroll = si.nPos ;
        

                          switch (LOWORD (wParam))
        
                          {
        
                          case SB_LINELEFT:  si.nPos -=1 ;     break ;
        
                          case SB_LINERIGHT: si.nPos +=1 ;     break ;
        
                         case SB_PAGELEFT:  si.nPos -=si.nPage ;break ;
        
                         case SB_PAGERIGHT: si.nPos +=si.nPage ;break ;
        
                          case SB_THUMBTRACK:si.nPos  =si.nTrackPos ;break ;
        
                          default:                                                        break ;
        
                          }
        

                          si.fMask = SIF_POS ;
        
                          SetScrollInfo (hwnd, SB_HORZ, &si, TRUE) ;
        
                          GetScrollInfo (hwnd, SB_HORZ, &si) ;
        

                          if (si.nPos != iHscroll)
        
                          {
        
                                        ScrollWindow (hwnd, iHscroll - si.nPos, 0, NULL, NULL) ;
        
                                        iHscroll = si.nPos ;
        
                                         UpdateWindow (hwnd) ;
        
                         }
        
                          return 0 ;
        

                          // WM_INITMENUPOPUP:  Enable or Gray menu items
        

    case   WM_INITMENUPOPUP:
        
                          if (hdib)
        
                                                 iEnable = MF_ENABLED ;
        
                          else
        
                                                 iEnable = MF_GRAYED ;
        
      EnableMenuItem (hMenu, IDM_FILE_SAVE,                     iEnable) ;
        
      EnableMenuItem (hMenu, IDM_FILE_PRINT,                    iEnable) ;
        
      EnableMenuItem (hMenu, IDM_FILE_PROPERTIES,               iEnable) ;
        
      EnableMenuItem (hMenu, IDM_EDIT_CUT,                      iEnable) ;
        
      EnableMenuItem (hMenu, IDM_EDIT_COPY,                     iEnable) ;
        
      EnableMenuItem (hMenu, IDM_EDIT_DELETE,                   iEnable) ;
        

                          if (DibIsAddressable (hdib))
        
                                               iEnable = MF_ENABLED ;
        
                          else
        
                                                 iEnable = MF_GRAYED ;
        

        EnableMenuItem (hMenu, IDM_EDIT_ROTATE,         iEnable) ;
        
         EnableMenuItem (hMenu, IDM_EDIT_FLIP,           iEnable) ;
        
         EnableMenuItem (hMenu, IDM_CONVERT_01,          iEnable) ;
        
         EnableMenuItem (hMenu, IDM_CONVERT_04,          iEnable) ;
        
        EnableMenuItem (hMenu, IDM_CONVERT_08,          iEnable) ;
        
         EnableMenuItem (hMenu, IDM_CONVERT_16,          iEnable) ;
        
         EnableMenuItem (hMenu, IDM_CONVERT_24,          iEnable) ;
        
         EnableMenuItem (hMenu, IDM_CONVERT_32,          iEnable) ;
        

                         switch (DibBitCount (hdib))
        
                          {
        
                          case  1:     EnableMenuItem (hMenu, IDM_CONVERT_01, MF_GRAYED) ;       break ;
        
                         case  4:     EnableMenuItem (hMenu, IDM_CONVERT_04, MF_GRAYED) ;       break ;
        
                          case  8:     EnableMenuItem (hMenu, IDM_CONVERT_08, MF_GRAYED) ;       break ;
        
                          case   16: EnableMenuItem (hMenu, IDM_CONVERT_16, MF_GRAYED) ;     break ;
        
                         case   24: EnableMenuItem (hMenu, IDM_CONVERT_24, MF_GRAYED) ;     break ;
        
                          case   32: EnableMenuItem (hMenu, IDM_CONVERT_32, MF_GRAYED) ;     break ;
        
                          }
        

                          if (hdib && DibColorSize (hdib) > 0)
        
                                                 iEnable = MF_ENABLED ;
        
                          else
        
                                                 iEnable = MF_GRAYED ;
        

                          EnableMenuItem (hMenu, IDM_PAL_DIBTABLE,    iEnable) ;
        
                  if (DibIsAddressable (hdib) && DibBitCount (hdib) > 8)
        
                                                 iEnable = MF_ENABLED ;
        
                        else
        
                                                 iEnable = MF_GRAYED ;
        

           EnableMenuItem        (hMenu, IDM_PAL_OPT_POP4,            iEnable) ;
        
           EnableMenuItem        (hMenu, IDM_PAL_OPT_POP5,            iEnable) ;
        
           EnableMenuItem        (hMenu, IDM_PAL_OPT_POP6,            iEnable) ;
        
           EnableMenuItem        (hMenu, IDM_PAL_OPT_MEDCUT,          iEnable) ;
        
           EnableMenuItem        (hMenu, IDM_EDIT_PASTE,
        
                 IsClipboardFormatAvailable (CF_DIB) ? MF_ENABLED : MF_GRAYED) ;
        

                          return 0 ;
        

                          // WM_COMMAND:  Process all menu commands.
        

           case   WM_COMMAND:
        
                          iConvert = 0 ;
        

                          switch (LOWORD (wParam))
        
                          {
        
                         case   IDM_FILE_OPEN:
        

                  // Show the File Open dialog box
        

                                                if (!GetOpenFileName (&ofn))
        
                                                                return 0 ;
        
             
        
                  // If there's an existing DIB and palette, delete them
        

                                                 SendMessage (hwnd, WM_USER_DELETEDIB, 0, 0) ;
        
             
        
                 // Load the DIB into memory
        

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

                                                 hdib = DibFileLoad (szFileName) ;
        

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

                                                                // Reset the scroll bars
        

                                                 SendMessage (hwnd, WM_USER_SETSCROLLS, TRUE, 0) ;
        

                                                                // Create the palette and DDB
        

                                                 SendMessage (hwnd, WM_USER_CREATEPAL, TRUE, 0) ;
        

                                                 if (!hdib)
        
                                                 {
        
                 MessageBox (hwnd, TEXT ("Cannot load DIB file!"),
        
                             szAppName, MB_OK | MB_ICONEXCLAMATION) ;
        
                                                 }
        
                                                InvalidateRect (hwnd, NULL, TRUE) ;
        
                                                 return 0 ;
        

           case   IDM_FILE_SAVE:
        

                                  // Show the File Save dialog box
        

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

                                  // Save the DIB to memory
        

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

                                                 fSuccess = DibFileSave (hdib, szFileName) ;
        

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

                                                 if (!fSuccess)
        
                  MessageBox (    hwnd, TEXT ("Cannot save DIB file!"),
        
                               szAppName, MB_OK | MB_ICONEXCLAMATION) ;
        
                                                return 0 ;
        

                          case   IDM_FILE_PRINT:
        
                                                 if (!hdib)
        
                                                                return 0 ;
        

                                                                // Get printer DC
        

                          printdlg.Flags = PD_RETURNDC | PD_NOPAGENUMS | PD_NOSELECTION ;
        

                                                if (!PrintDlg (&printdlg))
        
                                                                return 0 ;
        

                                                 if (NULL == (hdcPrn = printdlg.hDC))
        
                                                 {
        
                   MessageBox(    hwnd, TEXT ("Cannot obtain Printer DC"),
        
                            szAppName, MB_ICONEXCLAMATION | MB_OK) ;
        
                                                                return 0 ;
        
                                                 }
        
                          // Check if the printer can print bitmaps
        

         if (!(RC_BITBLT & GetDeviceCaps (hdcPrn, RASTERCAPS)))
        
                                                 {
        
                                                        DeleteDC (hdcPrn) ;
        
         MessageBox (     hwnd, TEXT ("Printer cannot print bitmaps"),
        
                       szAppName, MB_ICONEXCLAMATION | MB_OK) ;
        
                                                        return 0 ;
        
                                                 }
        
                                                         // Get size of printable area of page
        

                                                 cxPage = GetDeviceCaps (hdcPrn, HORZRES) ;
        
                                                 cyPage = GetDeviceCaps (hdcPrn, VERTRES) ;
        

                                                 fSuccess = FALSE ;
        

                                                         // Send the DIB to the printer
        

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

                                         if ((StartDoc (hdcPrn, &di) > 0) && (StartPage (hdcPrn) > 0))
        
                                  {
        
                          DisplayDib (hdcPrn, DibBitmapHandle (hdib), 0, 0,
        
                                       cxPage, cyPage, wShow, FALSE) ;
        
                  
        
                                                         if (EndPage (hdcPrn) > 0)
        
                                                        {
        
                                                                                fSuccess = TRUE ;
        
                                                                                EndDoc (hdcPrn) ;
        
                                                        }
        
                                         }
        
                                         ShowCursor (FALSE) ;
        
                                         SetCursor (LoadCursor (NULL, IDC_ARROW)) ;
        

                                         DeleteDC (hdcPrn) ;
        

                                         if (   !fSuccess)
        
                  MessageBox (    hwnd, TEXT ("Could not print bitmap"),
        
                               szAppName, MB_ICONEXCLAMATION | MB_OK) ;
        
                                         return 0 ;
        

                  case   IDM_FILE_PROPERTIES:
        
                                         if (!hdib)
        
                                                         return 0 ;
        

                           wsprintf (szBuffer, TEXT ("Pixel width:\t%i\n")
        
                                   TEXT ("Pixel height:\t%i\n")
        
                                 TEXT ("Bits per pixel:\t%i\n")
        
                                  TEXT ("Number of colors:\t%i\n")
        
                                 TEXT ("Compression:\t%s\n"),
        
                       DibWidth (hdib), DibHeight (hdib),
        
                       DibBitCount (hdib), DibNumColors (hdib),
        
                       szCompression [min (3, DibCompression (hdib))]) ;
        

                                  MessageBox (  hwnd, szBuffer, szAppName,
        
                       MB_ICONEXCLAMATION | MB_OK) ;
        
                                         return 0 ;
        

                  case   IDM_APP_EXIT:
        
                                         SendMessage (hwnd, WM_CLOSE, 0, 0) ;
        
                                         return 0 ;
        

                  case   IDM_EDIT_COPY:
        
                  case   IDM_EDIT_CUT:
        
                                         if (!(hGlobal = DibCopyToPackedDib (hdib, TRUE)))
        
                                                        return 0 ;
        

                                         OpenClipboard (hwnd) ;
        
                                         EmptyClipboard () ;
        
                                         SetClipboardData (CF_DIB, hGlobal) ;
        
                                         CloseClipboard () ;
        

                                         if (LOWORD (wParam) == IDM_EDIT_COPY)
        
                          return 0 ;
        
                           // fall through for IDM_EDIT_CUT
        
                  case   IDM_EDIT_DELETE:
        
                                         SendMessage (hwnd, WM_USER_DELETEDIB, 0, 0) ;
        
                                         InvalidateRect (hwnd, NULL, TRUE) ;
        
                                         return 0 ;
        

                  case   IDM_EDIT_PASTE:
        
                                         OpenClipboard (hwnd) ;
        

                                         hGlobal = GetClipboardData (CF_DIB) ;
        
                                         pGlobal = GlobalLock (hGlobal) ;
        

                          // If there's an existing DIB and palette,delete them.
        
                          // Then convert the packed DIB to an HDIB.
        

                                         if (pGlobal)
        
                                         {
        
                         SendMessage (hwnd, WM_USER_DELETEDIB, 0, 0) ;
        
                         hdib = DibCopyFromPackedDib ((BITMAPINFO *) pGlobal) ;
        
                          SendMessage (hwnd, WM_USER_CREATEPAL, TRUE, 0) ;
        
                                         }
        

                                         GlobalUnlock (hGlobal) ;
        
                                         CloseClipboard () ;
        
                                                // Reset the scroll bars
        

                                         SendMessage (hwnd, WM_USER_SETSCROLLS, TRUE, 0) ;
        
                                         InvalidateRect (hwnd, NULL, TRUE) ;
        
                                         return 0 ;
        

                 case   IDM_EDIT_ROTATE:
        
                                         if (hdibNew = DibRotateRight (hdib))
        
                                         {
        
                                  DibDelete (hdib) ;
        
                                  DeleteObject (hBitmap) ;
        
                                  hdib = hdibNew ;
        
                                  hBitmap = DibCopyToDdb (hdib, hwnd, hPalette) ;
        
                                 SendMessage (hwnd, WM_USER_SETSCROLLS, TRUE, 0) ;
        
                                  InvalidateRect (hwnd, NULL, TRUE) ;
        
                                         }
        
                                         else
        
                                         {
        
                   MessageBox (   hwnd, TEXT ("Not enough memory"),
        
                                 szAppName, MB_OK | MB_ICONEXCLAMATION) ;
        
                                         }
        
                                         return 0 ;
        

                  case   IDM_EDIT_FLIP:
        
                                         if (hdibNew = DibFlipHorizontal (hdib))
        
                                         {
        
                                 DibDelete (hdib) ;
        
                                  DeleteObject (hBitmap) ;
        
                                  hdib = hdibNew ;
        
                                  hBitmap = DibCopyToDdb (hdib, hwnd, hPalette) ;
        
                                  InvalidateRect (hwnd, NULL, TRUE) ;
        
                                        }
        
                                         else
        
                                         {
        
                   MessageBox (   hwnd, TEXT ("Not enough memory"),
        
                                  szAppName, MB_OK | MB_ICONEXCLAMATION) ;
        
                                         }
        
                                         return 0 ;
        

                  case   IDM_SHOW_NORMAL:
        
                  case   IDM_SHOW_CENTER:
        
                  case   IDM_SHOW_STRETCH:
        
                  case   IDM_SHOW_ISOSTRETCH:
        
                                         CheckMenuItem (hMenu, wShow, MF_UNCHECKED) ;
        
                                         wShow = LOWORD (wParam) ;
        
                                        CheckMenuItem (hMenu, wShow, MF_CHECKED) ;
        
                                         SendMessage (hwnd, WM_USER_SETSCROLLS, FALSE, 0) ;
        

                                         InvalidateRect (hwnd, NULL, TRUE) ;
        
                                         return 0 ;
        

                  case   IDM_CONVERT_32:  iConvert += 8 ;
        
                  case   IDM_CONVERT_24:  iConvert += 8 ; 
        
                  case   IDM_CONVERT_16:  iConvert += 8 ;
        
                  case   IDM_CONVERT_08:  iConvert += 4 ;
        
                  case   IDM_CONVERT_04:  iConvert += 3 ;
        
                  case   IDM_CONVERT_01:  iConvert += 1 ;
        
                                         SetCursor (LoadCursor (NULL, IDC_WAIT)) ;
        
                                         ShowCursor (TRUE) ;
        

                                         hdibNew = DibConvert (hdib, iConvert) ;
        

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

                                         if (hdibNew)
        
                                         {
        
                   SendMessage (hwnd, WM_USER_DELETEDIB, 0, 0) ;
        
                                         hdib = hdibNew ;
        
                   SendMessage (hwnd, WM_USER_CREATEPAL, TRUE, 0) ;
        
                  InvalidateRect (hwnd, NULL, TRUE) ;
        
                                         }
        
                                         else
        
                                         {
        
                    MessageBox (  hwnd, TEXT ("Not enough memory"),
        
                                  szAppName, MB_OK | MB_ICONEXCLAMATION) ;
        
                                                }
        
                                                 return 0 ;
        

                          case   IDM_APP_ABOUT:
        
          MessageBox (    hwnd, TEXT ("Dibble (c) Charles Petzold, 1998"),
        
                       szAppName, MB_OK | MB_ICONEXCLAMATION) ;
        
                                  return 0 ;
        
                         }
        
   
        
                                  // All the other WM_COMMAND messages are from the palette
        
                                  //     items. Any existing palette is deleted, and the cursor
        
                                  //     is set to the hourglass.
        

                          SendMessage (hwnd, WM_USER_DELETEPAL, 0, 0) ;
        
                          SetCursor (LoadCursor (NULL, IDC_WAIT)) ;
        
                          ShowCursor (TRUE) ;
        
                                  // Notice that all messages for palette items are ended
        
                                  //     with break rather than return. This is to allow
        
                                 //     additional processing later on.
        

                          switch (LOWORD (wParam))
        
                          {
        
                          case   IDM_PAL_DIBTABLE:
        
                                                 hPalette = DibPalDibTable (hdib) ;
        
                                                 break ;
        

                          case   IDM_PAL_HALFTONE:
        
                                                 hdc = GetDC (hwnd) ;
        

                                                if (hPalette = CreateHalftonePalette (hdc))
        
                                                               fHalftonePalette = TRUE ;
        

                                                 ReleaseDC (hwnd, hdc) ;
        
                                                 break ;
        

                         case   IDM_PAL_ALLPURPOSE:
        
                                                 hPalette = DibPalAllPurpose () ;
        
                                                 break ;
        

      case IDM_PAL_GRAY2:hPalette    = DibPalUniformGrays   (2);      break;
        
      case IDM_PAL_GRAY3:hPalette    = DibPalUniformGrays  (3);      break;
        
      case IDM_PAL_GRAY4:hPalette    = DibPalUniformGrays  (4);      break;
        
      case IDM_PAL_GRAY8:hPalette    = DibPalUniformGrays   (8);      break;
        
      case IDM_PAL_GRAY16:hPalette       = DibPalUniformGrays  (16)    ;      break;
        
      case IDM_PAL_GRAY32:hPalette       = DibPalUniformGrays  (32)    ;      break;
        
      case IDM_PAL_GRAY64:hPalette       = DibPalUniformGrays  (64)    ;      break;
        
      case IDM_PAL_GRAY128:hPalette      = DibPalUniformGrays (128)    ;      break;
        
      case IDM_PAL_GRAY256:hPalette      = DibPalUniformGrays (256)    ;      break;
        
      case IDM_PAL_RGB222:hPalette       = DibPalUniformColors (2,2,2);       break;
        
      case IDM_PAL_RGB333:hPalette       = DibPalUniformColors (3,3,3);       break;
        
      case IDM_PAL_RGB444:hPalette       = DibPalUniformColors (4,4,4);       break;
        
      case IDM_PAL_RGB555:hPalette       = DibPalUniformColors (5,5,5);       break;
        
      case IDM_PAL_RGB666:hPalette       = DibPalUniformColors (6,6,6);       break;
        
      case IDM_PAL_RGB775:hPalette       = DibPalUniformColors (7,7,5);       break;
        
      case IDM_PAL_RGB757:hPalette       = DibPalUniformColors (7,5,7);       break;
        
      case IDM_PAL_RGB577:hPalette       = DibPalUniformColors (5,7,7);       break;
        
      case IDM_PAL_RGB884:hPalette       = DibPalUniformColors (8,8,4);       break;
        
      case IDM_PAL_RGB848:hPalette       = DibPalUniformColors (8,4,8);       break;
        
      case IDM_PAL_RGB488:hPalette       = DibPalUniformColors (4,8,8);       break;
        
      case IDM_PAL_OPT_POP4:hPalette   = DibPalPopularity (hdib, 4) ;  break ;
        
      case IDM_PAL_OPT_POP5:hPalette   = DibPalPopularity (hdib, 5) ;  break ;
        
      case IDM_PAL_OPT_POP6:hPalette   = DibPalPopularity (hdib, 6) ;  break ;                 
        
      case IDM_PAL_OPT_MEDCUT:hPalette = DibPalMedianCut  (hdib, 6) ;  break ;
        
                          }
        

                          // After processing Palette items from the menu, the cursor
        
                          //     is restored to an arrow, the menu item is checked, and
        
                          //     the window is invalidated.
        

                          hBitmap = DibCopyToDdb (hdib, hwnd, hPalette) ;
        

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

                          if (hPalette)
        
                                                 PaletteMenu (hMenu, (LOWORD (wParam))) ;
        

                         InvalidateRect (hwnd, NULL, TRUE) ;
        
                          return 0 ;
        

                          // This programmer-defined message deletes an existing DIB
        
                          //   in preparation for getting a new one.  Invoked during
        
                          //   File Open command, Edit Paste command, and others.
        

   case   WM_USER_DELETEDIB:
        
                          if (hdib)
        
                         {
        
                                                 DibDelete (hdib) ;
        
                                                 hdib = NULL ;
        
                          }
        
                          SendMessage (hwnd, WM_USER_DELETEPAL, 0, 0) ;
        
                          return 0 ;
        

                         // This programmer-defined message deletes an existing palette
        
                          //            in preparation for defining a new one.
        

           case   WM_USER_DELETEPAL:
        
                          if (hPalette)
        
                          {
        
                                                 DeleteObject (hPalette) ;
        
                                                 hPalette = NULL ;
        
                                                 fHalftonePalette = FALSE ;
        
                                                 PaletteMenu (hMenu, IDM_PAL_NONE) ;
        
                          }
        
                          if (hBitmap)
        
                                                 DeleteObject (hBitmap) ;
        

                                  return 0 ;
        

                          // Programmer-defined message to create a new palette based on
        
                        //            a new DIB.  If wParam == TRUE, create a DDB as well.
        

           case   WM_USER_CREATEPAL:
        
                          if (hdib)
        
                          {
        
                                                 hdc = GetDC (hwnd) ;
        

                          if (!(RC_PALETTE & GetDeviceCaps (hdc, RASTERCAPS)))
        
                                                {
        
                   PaletteMenu (hMenu, IDM_PAL_NONE) ;
        
                                                 }
        
                                                 else if (hPalette = DibPalDibTable (hdib))
        
                                         {
        
                   PaletteMenu (hMenu, IDM_PAL_DIBTABLE) ;
        
                                                 }
        
                                               else if (hPalette = CreateHalftonePalette (hdc))
        
                                                 {
        
                                                               fHalftonePalette = TRUE ;
        
                   PaletteMenu (hMenu, IDM_PAL_HALFTONE) ;
        
                                                 }
        
                                                 ReleaseDC (hwnd, hdc) ;
        

                                                 if ((BOOL) wParam)
        
                   hBitmap = DibCopyToDdb (hdib, hwnd, hPalette) ;
        
                          }
        
                          return 0 ;
        

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

                          if (hPalette)
        
                        {
        
                                                 SelectPalette (hdc, hPalette, FALSE) ;
        
                                                 RealizePalette (hdc) ;
        
                          }
        
                          if (hBitmap)
        
                 {
        
                                         DisplayDib (  hdc,
        
                          fHalftonePalette ? DibBitmapHandle (hdib) : hBitmap,
        
                          iHscroll, iVscroll,
        
                          cxClient, cyClient,
        
                          wShow, fHalftonePalette) ;
        
                          }
        
                          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 (hdib)
        
                                                 DibDelete (hdib) ;
        

                          if (hBitmap)
        
                                                 DeleteObject (hBitmap) ;
        

                          if (hPalette)
        
                                                 DeleteObject (hPalette) ;
        

                          PostQuitMessage (0) ;
        
                          return 0 ;
        
           }
        
           return DefWindowProc (hwnd, message, wParam, lParam) ;
        
}
        
DIBBLE.RC (摘录)
        
//Microsoft Developer Studio generated resource script.
        
#include "resource.h"
        
#include "afxres.h"
        
/////////////////////////////////////////////////////////////////////////////
        
// Menu
        
DIBBLE MENU DISCARDABLE BEGIN POPUP "&File"
        
   BEGIN
        
            MENUITEM "&Open...\tCtrl+O",        IDM_FILE_OPEN
        
            MENUITEM "&Save...\tCtrl+S",        IDM_FILE_SAVE
        
           MENUITEM SEPARATOR
        
            MENUITEM "&Print...\tCtrl+P",       IDM_FILE_PRINT
        
            MENUITEM SEPARATOR
        
            MENUITEM "Propert&ies...",          IDM_FILE_PROPERTIES
        
            MENUITEM SEPARATOR
        
            MENUITEM "E&xit",                            IDM_APP_EXIT
        
    END
        
   POPUP "&Edit"
        
   BEGIN 
        
            MENUITEM "Cu&t\tCtrl+X",      IDM_EDIT_CUT
        
            MENUITEM "&Copy\tCtrl+C",       IDM_EDIT_COPY
        
   MENUITEM "&Paste\tCtrl+V",    IDM_EDIT_PASTE
        
           MENUITEM "&Delete\tDelete",   IDM_EDIT_DELETE
        
           MENUITEM SEPARATOR           
        
       MENUITEM "&Flip",                         IDM_EDIT_FLIP
        
           MENUITEM "&Rotate",           IDM_EDIT_ROTATE
        
   END
        
   POPUP "&Show"
        
   BEGIN
        
       MENUITEM "&Actual Size",   IDM_SHOW_NORMAL, CHECKED
        
       MENUITEM "&Center",        IDM_SHOW_CENTER
        
       MENUITEM "&Stretch to Window",            IDM_SHOW_STRETCH
        
       MENUITEM "Stretch &Isotropically",IDM_SHOW_ISOSTRETCH
        
   END
        
   POPUP "&Palette"
        
   BEGIN
        
       MENUITEM "&None",                         IDM_PAL_NONE, CHECKED
        
       MENUITEM "&Dib ColorTable",       IDM_PAL_DIBTABLE
        
       MENUITEM "&Halftone",     IDM_PAL_HALFTONE
        
       MENUITEM "&All-Purpose",          IDM_PAL_ALLPURPOSE
        
           POPUP "&Gray Shades"
        
           BEGIN
        
       MENUITEM "&1. 2 Grays",           IDM_PAL_GRAY2
        
       MENUITEM "&2. 3 Grays",       IDM_PAL_GRAY3
        
       MENUITEM "&3. 4 Grays",       IDM_PAL_GRAY4
        
       MENUITEM "&4. 8 Grays",       IDM_PAL_GRAY8
        
       MENUITEM "&5. 16 Grays",      IDM_PAL_GRAY16
        
       MENUITEM "&6. 32 Grays",      IDM_PAL_GRAY32
        
       MENUITEM "&7. 64 Grays",      IDM_PAL_GRAY64
        
       MENUITEM "&8. 128 Grays",     IDM_PAL_GRAY128
        
       MENUITEM "&9. 256 Grays",     IDM_PAL_GRAY256
        
           END
        
           POPUP "&Uniform Colors"
        
           BEGIN
        
       MENUITEM "&1. 2R x 2G x 2B (8)",                  IDM_PAL_RGB222
        
       MENUITEM "&2. 3R x 3G x 3B (27)",         IDM_PAL_RGB333
        
       MENUITEM "&3. 4R x 4G x 4B (64)",            IDM_PAL_RGB444
        
       MENUITEM "&4. 5R x 5G x 5B (125)",        IDM_PAL_RGB555
        
       MENUITEM "&5. 6R x 6G x 6B (216)",        IDM_PAL_RGB666
        
       MENUITEM "&6. 7R x 7G x 5B (245)",       IDM_PAL_RGB775
        
       MENUITEM "&7. 7R x 5B x 7B (245)",      IDM_PAL_RGB757
        
       MENUITEM "&8. 5R x 7G x 7B (245)",       IDM_PAL_RGB577
        
       MENUITEM "&9. 8R x 8G x 4B (256)",        IDM_PAL_RGB884
        
       MENUITEM "&A. 8R x 4G x 8B (256)",        IDM_PAL_RGB848
        
           MENUITEM "&B. 4R x 8G x 8B (256)",        IDM_PAL_RGB488
        
           END
        
           POPUP "&Optimized"
        
         BEGIN
        
       MENUITEM "&1. Popularity Algorithm (4 bits)"IDM_PAL_OPT_POP4
        
       MENUITEM "&2. Popularity Algorithm (5 bits)"IDM_PAL_OPT_POP5
        
       MENUITEM "&3. Popularity Algorithm (6 bits)"IDM_PAL_OPT_POP6
        
       MENUITEM "&4. Median Cut Algorithm ", IDM_PAL_OPT_MEDCUT
        
           END
        
   END
        
   POPUP "Con&vert"
        
   BEGIN
        
       MENUITEM "&1. to 1 bit per pixel",        IDM_CONVERT_01
        
      MENUITEM "&2. to 4 bits per pixel",       IDM_CONVERT_04
        
       MENUITEM "&3. to 8 bits per pixel",      IDM_CONVERT_08
        
       MENUITEM "&4. to 16 bits per pixel",      IDM_CONVERT_16
        
       MENUITEM "&5. to 24 bits per pixel",      IDM_CONVERT_24
        
      MENUITEM "&6. to 32 bits per pixel",      IDM_CONVERT_32
        
   END
        
   POPUP "&Help"
        
   BEGIN
        
                  MENUITEM "&About",
        
                   IDM_APP_ABOUT
        
   END
        
END
        
/////////////////////////////////////////////////////////////////////////////
        
// Accelerator
        
DIBBLE ACCELERATORS DISCARDABLE
        
BEGIN
        
   "C",          IDM_EDIT_COPY,          VIRTKEY, CONTROL, NOINVERT
        
   "O",      IDM_FILE_OPEN,          VIRTKEY, CONTROL, NOINVERT
        
   "P",      IDM_FILE_PRINT,         VIRTKEY, CONTROL, NOINVERT
        
   "S",      IDM_FILE_SAVE,          VIRTKEY, CONTROL, NOINVERT
        
   "V",      IDM_EDIT_PASTE,         VIRTKEY, CONTROL, NOINVERT
        
   VK_DELETE,            IDM_EDIT_DELETE,        VIRTKEY, NOINVERT
        
   "X",      IDM_EDIT_CUT,           VIRTKEY, CONTROL, NOINVERT
        
END
        
RESOURCE.H (摘录)
        
// Microsoft Developer Studio generated include file.
        
// Used by Dibble.rc
        
#define IDM_FILE_OPEN               40001
        
#define IDM_FILE_SAVE                40002
        
#define IDM_FILE_PRINT               40003
        
#define IDM_FILE_PROPERTIES          40004
        
#define IDM_APP_EXIT                 40005
        
#define IDM_EDIT_CUT                 40006
        
#define IDM_EDIT_COPY               40007
        
#define IDM_EDIT_PASTE               40008
        
#define IDM_EDIT_DELETE              40009
        
#define IDM_EDIT_FLIP                40010
        
#define IDM_EDIT_ROTATE              40011
        
#define IDM_SHOW_NORMAL              40012
        
#define IDM_SHOW_CENTER              40013
        
#define IDM_SHOW_STRETCH             40014
        
#define IDM_SHOW_ISOSTRETCH          40015
        
#define IDM_PAL_NONE                 40016
        
#define IDM_PAL_DIBTABLE             40017
        
#define IDM_PAL_HALFTONE             40018
        
#define IDM_PAL_ALLPURPOSE           40019
        
#define IDM_PAL_GRAY2                40020
        
#define IDM_PAL_GRAY3                40021
        
#define IDM_PAL_GRAY4                40022
        
#define IDM_PAL_GRAY8                40023
        
#define IDM_PAL_GRAY16               40024
        
#define IDM_PAL_GRAY32               40025
        
#define IDM_PAL_GRAY64               40026
        
#define IDM_PAL_GRAY128              40027
        
#define IDM_PAL_GRAY256              40028
        
#define IDM_PAL_RGB222               40029
        
#define IDM_PAL_RGB333               40030
        
#define IDM_PAL_RGB444               40031
        
#define IDM_PAL_RGB555               40032
        
#define IDM_PAL_RGB666               40033
        
#define IDM_PAL_RGB775               40034
        
#define IDM_PAL_RGB757               40035
        
#define IDM_PAL_RGB577               40036
        
#define IDM_PAL_RGB884               40037
        
#define IDM_PAL_RGB848               40038
        
#define IDM_PAL_RGB488               40039
        
#define IDM_PAL_OPT_POP4             40040
        
#define IDM_PAL_OPT_POP5             40041
        
#define IDM_PAL_OPT_POP6             40042
        
#define IDM_PAL_OPT_MEDCUT           40043
        
#define IDM_CONVERT_01               40044
        
#define IDM_CONVERT_04               40045
        
#define IDM_CONVERT_08               40046
        
#define IDM_CONVERT_16               40047
        
#define IDM_CONVERT_24               40048
        
#define IDM_CONVERT_32               40049
        
#define IDM_APP_ABOUT                40050
        

DIBBLE使用了两个其它文件,我将简要地说明它们.DIBCONV文件(DIBCONV.C和DIBCONV.H)在两种不同格式之间转换-例如,从每像素24位转换成每像素8位.DIBPAL文件(DIBPAL.C和DIBPAL.H)建立调色板.

DIBBLE维护WndProc中的三个重要的静态变量.这些是呼叫hdib的HDIB句柄、呼叫hPalette的HPALETTE句柄和呼叫hBitmap的HBITMAP句柄.HDIB来自DIBHELP中的不同函数;HPALETTE来自DIBPAL中的不同函数或CreateHalftonePalette函数;而HBITMAP句柄来自DIBHELP.C中的DibCopyToDdb函数并帮助加速屏幕显示,特别是在256色显示模式下.不过,无论在程序建立新的「DIB Section」(显而易见地)或在程序建立不同的调色板(不很明显)时,这个句柄都必须重新建立.

让我们从功能上而非循序渐进地来介绍一下DIBBLE.

文件载入和储存

DIBBLE可以在响应IDM_FILE_LOAD和IDM_FILE_SAVE的WM_COMMAND消息处理过程中加载DIB文件并储存这些文件.在处理这些消息处理期间,DIBBLE通过分别呼叫GetOpenFileName和GetSaveFileName来启动公用文件对话框.

对于「File」、「Save」菜单命令,DIBBLE只需要呼叫DibFileSave.对于「File」、「Open」菜单命令,DIBBLE必须首先删除前面的HDIB、调色板和位图对象.它透过发送一个WM_USER_DELETEDIB消息来完成这件事,此消息通过呼叫DibDelete和DeleteObject来处理.然后DIBBLE呼叫DIBHELP中的DibFileLoad函数,发送WM_USER_SETSCROLLS和WM_USER_CREATEPAL消息来重新设定滚动条并建立调色板.WM_USER_CREATEPAL消息也位于程序从DIB区块建立的新的DDB位置.

显示、卷动和打印

DIBBLE的菜单允许它以实际尺寸在显示区域左上角显示DIB,或在显示区域中间显示DIB,或伸展到填充显示区域,或者在保持纵横比的情况下尽量填充显示区域.您可以在DIBBLE的「Show」菜单上来选择需要的选项.注意,这些与上一章的SHOWDIB2程序中四个选项相同.

在WM_PAINT消息处理期间-也是处理「File」、「Print」命令的过程中-DIBBLE呼叫DisplayDib函数.注意,DisplayDib使用BitBlt和StretchBlt,而不是使用SetDIBitsToDevice和StretchDIBits.在WM_PAINT消息处理期间,传递给函数的位图句柄由DibCopyToDdb函数建立,并在WM_USER_CREATEPAL消息处理期间呼叫.其中DDB与视讯设备内容兼容.当处理「File」、「Print」命令时,DIBBLE呼叫DisplayDib,其中可用的DIB区块句柄来自DIBHELP.C中的DibBitmapHandle函数.

另外要注意,DIBBLE保留一个称作fHalftonePalette的静态BOOL变量,如果从CreateHalftonePalette函数中获得hPalette,则此变量设定为TRUE.这将迫使DisplayDib函数呼叫StretchBlt而不是呼叫BitBlt,即使DIB被指定按实际尺寸显示.fHalftonePalette变量也导致WM_PAINT处理程序将DIB区块句柄传递给DisplayDib函数,而不是由DibCopyToDdb函数建立的位图句柄.本章前面讨论过中间色调色板的使用,并在SHOWDIB5程序中进行了展示.

第一次使用范例程序时,DIBBLE允许在显示区域中卷动DIB.只有按实际尺寸显示DIB时,才显示滚动条.在处理WM_PAINT时,WndProc简单地将滚动条的目前位置传递给DisplayDib函数.

剪贴簿

对于「Cut」和「Copy」菜单项,DIBBLE呼叫DIBHELP中的DibCopyToPackedDib函数.该函数将获得所有的DIB组件并将它们放入大的内存块中.

对于第一次使用本书中的某些范例程序来说,DIBBLE从剪贴簿中粘贴DIB.这包括呼叫DibCopyFromPackedDib函数,并替换窗口消息处理程序前面储存的HDIB、调色板和位图.

翻转和旋转

DIBBLE中的「Edit」菜单中除了常见的「Cut」、「Copy」、「Paste」和「Delete」选项之外,还包括两个附加项-「Flip」和「Rotate」.「Flip」选项使位图绕水平轴翻转-即上下颠倒翻转.「Rotate」选项使位图顺时针旋转90度.这两个函数都需要透过将它们从一个DIB复制到另一个来存取所有的DIB像素(因为这两个函数不需要建立新的调色板,所以不删除和重新建立调色板).

「Flip」菜单选项使用DibFlipHorizontal函数,此函数也位于DIBBLE.C文件.此函数呼叫DibCopy来获得DIB精确的副本.然后,进入将原DIB中的像素复制到新DIB的循环,但是复制这些像素是为了上下翻转图像.注意,此函数呼叫DibGetPixel和DibSetPixel.这些是DIBHELP.C中的通用(但不像我们所希望的那么快)函数.

为了说明DibGetPixel和DibSetPixel函数与DIBHELP.H中执行更快的DibGetPixel和DibSetPixel宏之间的区别,DibRotateRight函数使用了宏.然而,首先要注意的是,该函数呼叫DibCopy时,第二个参数设定为TRUE.这导致DibCopy翻转原DIB的宽度和高度来建立新的DIB.另外,像素位不能由DibCopy函数复制.但是,DibRotateRight函数有六个不同的循环将像素位从原DIB复制到新的DIB-每一个都对应不同的DIB像素宽度(1位、4位、8位、16位、24位和32位).虽然包括了更多的程序代码,但是函数更快了.

尽管可以使用「Flip Horizontal」和「Rotate Right」选项来产生「Flip Vertical」、「Rotate Left」和「Rotate 180°」功能,但通常程序将直接执行所有选项.毕竟,DIBBLE只是个展示程序而已.

简单调色板;最佳化调色板

在DIBBLE中,您可以在256色视讯显示器上选择不同的调色板来显示DIB.这些都在DIBBLE的「Palette」菜单中列出.除了中间色调色板以外,其余的都直接由Windows函数呼叫建立,建立不同调色板的所有函数都由程序16-24所示的DIBPAL文件提供.

程序16-24 DIBPAL文件
        
DIBPAL.H
        
/*--------------------------------------------------------------------------
        
  DIBPAL.H header file for DIBPAL.C
        
----------------------------------------------------------------------------*/
        
HPALETTE DibPalDibTable (HDIB hdib) ;
        
HPALETTE DibPalAllPurpose (void) ;
        
HPALETTE DibPalUniformGrays (int iNum) ;
        
HPALETTE DibPalUniformColors (int iNumR, int iNumG, int iNumB) ;
        
HPALETTE DibPalVga (void) ;
        
HPALETTE DibPalPopularity (HDIB hdib, int iRes) ;
        
HPALETTE DibPalMedianCut (HDIB hdib, int iRes) ;
        
DIBPAL.C
        
/*----------------------------------------------------------------------------
        
  DIBPAL.C --   Palette-Creation Functions
        
                                                       (c) Charles Petzold, 1998
        
-------------------------------------------------------------------------*/
        
#include <windows.h>
        
#include "dibhelp.h"
        
#include "dibpal.h"
        

/*---------------------------------------------------------------------------
        
  DibPalDibTable: Creates a palette from the DIB color table
        
-----------------------------------------------------------------------------*/
        

HPALETTE DibPalDibTable (HDIB hdib)
        
{
        
   HPALETTE                      hPalette ;
        
           int                                  i, iNum ;
        
           LOGPALETTE    *      plp ;
        
           RGBQUAD                       rgb ;
        

           if (0 == (iNum = DibNumColors (hdib)))
        
                          return NULL ;
        
           plp = malloc (sizeof (LOGPALETTE) + (iNum - 1) * sizeof (PALETTEENTRY)) ;
        
           plp->palVersion               = 0x0300 ;
        
           plp->palNumEntries    = iNum ;
        

           for (i = 0 ; i < iNum ; i++)
        
           {
        
                          DibGetColor (hdib, i, &rgb) ;
        
                          plp->palPalEntry[i].peRed   = rgb.rgbRed ;
        
                          plp->palPalEntry[i].peGreen = rgb.rgbGreen ;
        
                          plp->palPalEntry[i].peBlue  = rgb.rgbBlue ;
        
                          plp->palPalEntry[i].peFlags = 0 ;
        
           }
        
           hPalette = CreatePalette (plp) ;
        
           free (plp) ;
        
           return hPalette ;
        
}
        
/*---------------------------------------------------------------------------
        
  DibPalAllPurpose: 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 DibPalAllPurpose (void)
        
{
        
           HPALETTE                     hPalette ;
        
           int                                  i, incr, R, G, B ;
        
           LOGPALETTE * plp ;
        

           plp = 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 ;
        
}
        

/*---------------------------------------------------------------------------
        
    DibPalUniformGrays:  Creates a palette of iNum grays, uniformly spaced
        
----------------------------------------------------------------------------*/
        

HPALETTE DibPalUniformGrays (int iNum)
        
{
        
           HPALETTE                      hPalette ;
        
           int                                  i ;
        
           LOGPALETTE    *      plp ;
        

           plp = malloc (sizeof (LOGPALETTE) + (iNum - 1) * sizeof (PALETTEENTRY)) ;
        
           plp->palVersion                      = 0x0300 ;
        
           plp->palNumEntries            = iNum ;
        

         for (i = 0 ; i < iNum ; i++)
        
           {
        
                          plp->palPalEntry[i].peRed            =
        
                          plp->palPalEntry[i].peGreen   =
        
                          plp->palPalEntry[i].peBlue  = (BYTE) (i * 255 / (iNum - 1)) ;
        
                          plp->palPalEntry[i].peFlags = 0 ;
        
           }
        
         hPalette = CreatePalette (plp) ;
        
           free (plp) ;
        
           return hPalette ;
        
}
        

/*--------------------------------------------------------------------------
        
    DibPalUniformColors: Creates a palette of iNumR x iNumG x iNumB colors
        
----------------------------------------------------------------------------*/
        

HPALETTE DibPalUniformColors (int iNumR, int iNumG, int iNumB)
        
{
        
           HPALETTE                      hPalette ;
        
           int                                  i, iNum, R, G, B ;
        
           LOGPALETTE * plp ;
        

           iNum = iNumR * iNumG * iNumB ;
        
           plp = malloc (sizeof (LOGPALETTE) + (iNum - 1) * sizeof (PALETTEENTRY)) ;
        
           plp->palVersion    = 0x0300 ;
        
           plp->palNumEntries = iNumR * iNumG * iNumB ;
        

           i = 0 ;
        
           for (R = 0 ; R < iNumR ; R++)
        
           for (G = 0 ; G < iNumG ; G++)
        
           for (B = 0 ; B < iNumB ; B++)
        
           {
        
                  plp->palPalEntry[i].peRed     = (BYTE) (R * 255 / (iNumR - 1)) ;
        
                  plp->palPalEntry[i].peGreen   = (BYTE) (G * 255 / (iNumG - 1)) ;
        
                  plp->palPalEntry[i].peBlue    = (BYTE) (B * 255 / (iNumB - 1)) ;
        
                  plp->palPalEntry[i].peFlags   = 0 ;
        

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

/*---------------------------------------------------------------------------
        
    DibPalVga:  Creates a palette based on standard 16 VGA colors
        
----------------------------------------------------------------------------*/
        

HPALETTE DibPalVga (void)
        
{
        
    static RGBQUAD rgb [16] = { 0x00, 0x00, 0x00,0x00,
        
                               0x00, 0x00, 0x80, 0x00,
        
                              0x00, 0x80, 0x00, 0x00,
        
                               0x00, 0x80, 0x80, 0x00,
        
                               0x80, 0x00, 0x00, 0x00,
        
                               0x80, 0x00, 0x80, 0x00,
        
                               0x80, 0x80, 0x00, 0x00,
        
                              0x80, 0x80, 0x80, 0x00,
        
                               0xC0, 0xC0, 0xC0, 0x00,
        
                               0x00, 0x00, 0xFF, 0x00,
        
                               0x00, 0xFF, 0x00, 0x00,
        
                               0x00, 0xFF, 0xFF, 0x00,
        
                              0xFF, 0x00, 0x00, 0x00,
        
                               0xFF, 0x00, 0xFF, 0x00,
        
                               0xFF, 0xFF, 0x00, 0x00,
        
                               0xFF, 0xFF, 0xFF, 0x00 } ;
        
           HPALETTE                     hPalette ;
        
          int                           i ;
        
           LOGPALETTE               * plp ;
        

           plp = malloc (sizeof (LOGPALETTE) + 15 * sizeof (PALETTEENTRY)) ;
        
           plp->palVersion               = 0x0300 ;
        
           plp->palNumEntries    = 16 ;
        

           for (i = 0 ; i < 16 ; i++)
        
           {
        
                         plp->palPalEntry[i].peRed     = rgb[i].rgbRed ;
        
                          plp->palPalEntry[i].peGreen   = rgb[i].rgbGreen ;
        
                          plp->palPalEntry[i].peBlue    = rgb[i].rgbBlue ;
        
                          plp->palPalEntry[i].peFlags  = 0 ;
        
           }
        
           hPalette = CreatePalette (plp) ;
        
           free (plp) ;
        
           return hPalette ;
        
}
        

/*---------------------------------------------------------------------------
        
  Macro used in palette optimization routines
        
-------------------------------------------------------------------------*/
        

#define PACK_RGB(R,G,B,iRes) ((int) (R) | ((int) (G) <<  (iRes)) |      \
        
                            ((int) (B) << ((iRes) + (iRes))))
        

/*----------------------------------------------------------------------------
        
  AccumColorCounts: Fills up piCount (indexed by a packed RGB color)
        
   with counts of pixels of that color.
        
-----------------------------------------------------------------------------*/
        

static void AccumColorCounts (HDIB hdib, int * piCount, int iRes)
        
{
        
           int           x, y, cx, cy ;
        
           RGBQUADrgb ;
        

           cx = DibWidth (hdib) ;
        
           cy = DibHeight (hdib) ;
        

           for (y = 0 ; y < cy ; y++)
        
           for (x = 0 ; x < cx ; x++)
        
           {
        
                          DibGetPixelColor (hdib, x, y, &rgb) ;
        

                          rgb.rgbRed   >>= (8 - iRes) ;
        
                          rgb.rgbGreen >>= (8 - iRes) ;
        
                          rgb.rgbBlue  >>= (8 - iRes) ;
        

                          ++piCount [PACK_RGB (rgb.rgbRed, rgb.rgbGreen, rgb.rgbBlue, iRes)] ;
        
           }
        
}
        

/*---------------------------------------------------------------------------
        
  DibPalPopularity:  Popularity algorithm for optimized colors
        
-----------------------------------------------------------------------------*/
        

HPALETTE DibPalPopularity (HDIB hdib, int iRes)
        
{
        
           HPALETTE                      hPalette ;
        
           int                                  i, iArraySize, iEntry, iCount, iIndex, iMask, R, G, B ;
        
           int                           *      piCount ;
        
           LOGPALETTE    *      plp ;
        

                          // Validity checks
        
  
        
           if (DibBitCount (hdib) < 16)
        
                          return NULL ;
        
           if (iRes < 3 || iRes > 8)
        
                 return NULL ;
        
                   // Allocate array for counting pixel colors
        
           iArraySize = 1 << (3 * iRes) ;
        
           iMask = (1 << iRes) - 1 ;
        

           if (NULL == (piCount = calloc (iArraySize, sizeof (int))))
        
                          return NULL ;
        
                         // Get the color counts
        
           AccumColorCounts (hdib, piCount, iRes) ;
        
                          // Set up a palette
        
           plp = malloc (sizeof (LOGPALETTE) + 235 * sizeof (PALETTEENTRY)) ;
        
           plp->palVersion = 0x0300 ;
        
           for (iEntry = 0 ; iEntry < 236 ; iEntry++)
        
           {
        
                          for (i = 0, iCount = 0 ; i < iArraySize ; i++)
        
                                                 if (piCount[i] > iCount)
        

                                                 {
        
                                                                iCount = piCount[i] ;
        
                                                                iIndex = i ;
        
                                                }
        
                          if (iCount == 0)
        
                                                 break ;
        
                          R = (iMask &  iIndex) << (8 - iRes) ;
        
          G = (iMask & (iIndex >> iRes )) << (8 - iRes) ;
        
           B = (iMask & (iIndex >> (iRes + iRes)))<< (8 - iRes) ;
        

                          plp->palPalEntry[iEntry].peRed       = (BYTE) R ;
        
                          plp->palPalEntry[iEntry].peGreen     = (BYTE) G ;
        
                          plp->palPalEntry[iEntry].peBlue      = (BYTE) B ;
        
                          plp->palPalEntry[iEntry].peFlags     = 0 ;
        

                          piCount [iIndex] = 0 ;
        
    }
        
                  // On exit from the loop iEntry will be the number of stored entries
        
           plp->palNumEntries = iEntry ;
        
                 // Create the palette, clean up, and return the palette handle
        
           hPalette = CreatePalette (plp) ;
        
           free (piCount) ;
        
           free (plp) ;
        

           return hPalette ;
        
}
        

/*--------------------------------------------------------------------------
        
  Structures used for implementing median cut algorithm
        
----------------------------------------------------------------------------*/
        

typedef struct                                    // defines dimension of a box
        
{
        
           int Rmin, Rmax, Gmin, Gmax, Bmin, Bmax ;
        
}
        
MINMAX ;
        
typedef struct                                    // for Compare routine for qsort
        
{
        
           int                   iBoxCount ;
        
           RGBQUAD       rgbBoxAv ;
        
}
        

BOXES ;
        
/*----------------------------------------------------------------------------
        
  FindAverageColor: In a box
        
-----------------------------------------------------------------------------*/
        

static int FindAverageColor (      int * piCount, MINMAX mm,
        
                                  int iRes, RGBQUAD * prgb)
        
{
        
           int R, G, B, iR, iG, iB, iTotal, iCount ;
        
                          // Initialize some variables
        
           iTotal = iR = iG = iB = 0 ;
        
                          // Loop through all colors in the box
        
           for (R = mm.Rmin ; R <= mm.Rmax ; R++)
        
           for (G = mm.Gmin ; G <= mm.Gmax ; G++)
        
           for (B = mm.Bmin ; B <= mm.Bmax ; B++)
        
           {
        
                                         // Get the number of pixels of that color
        
                         iCount = piCount [PACK_RGB (R, G, B, iRes)] ;
        
                                         // Weight the pixel count by the color value
        
                          iR += iCount * R ;
        
                          iG += iCount * G ;
        
                          iB += iCount * B ;
        

                          iTotal += iCount ;
        
           }
        
                         // Find the average color
        
           prgb->rgbRed          = (BYTE) ((iR / iTotal) << (8 - iRes)) ;
        
           prgb->rgbGreen        = (BYTE) ((iG / iTotal) << (8 - iRes)) ;
        
           prgb->rgbBlue         = (BYTE) ((iB / iTotal) << (8 - iRes)) ;
        

                          // Return the total number of pixels in the box
        
   
        
           return iTotal ;
        
}
        

/*---------------------------------------------------------------------------
        
  CutBox:  Divide a box in two
        
----------------------------------------------------------------------------*/
        
static void CutBox (int * piCount, int iBoxCount, MINMAX mm,
        
                   int iRes, int iLevel, BOXES * pboxes, int * piEntry)
        
{
        
           int           iCount, R, G, B ;
        
           MINMAX mmNew ;
        
   
        
                          // If the box is empty, return
        

           if (iBoxCount == 0)
        
                          return ;
        

                          // If the nesting level is 8, or the box is one pixel, we're ready
        
                          // to find the average color in the box and save it along with
        
                          // the number of pixels of that color
        

           if (iLevel == 8 || (  mm.Rmin == mm.Rmax &&
        
                                  mm.Gmin == mm.Gmax &&
        
                                  mm.Bmin == mm.Bmax))
        
    {
        
                  pboxes[*piEntry].iBoxCount =
        
                          FindAverageColor (piCount, mm, iRes, &pboxes[*piEntry].rgbBoxAv) ;
        
                          (*piEntry) ++ ;
        
           }
        
                         // Otherwise, if blue is the largest side, split it
        
           else if ((mm.Bmax - mm.Bmin > mm.Rmax - mm.Rmin) &&
        
                                                 (mm.Bmax - mm.Bmin > mm.Gmax - mm.Gmin))
        
           {
        
                                         // Initialize a counter and loop through the blue side
        
                  iCount = 0 ;
        
                  for (B = mm.Bmin ; B < mm.Bmax ; B++)
        
                  {
        
                                                 // Accumulate all the pixels for each successive blue value
        
                                  for (  R = mm.Rmin ; R <= mm.Rmax ; R++)
        
                                  for (  G = mm.Gmin ; G <= mm.Gmax ; G++)
        
                                  iCount += piCount [PACK_RGB (R, G, B, iRes)] ;
        

                                  // If it's more than half the box count, we're there
        

                                if (i  Count >= iBoxCount / 2)
        
                                                 break ;
        

                         //     If the next blue value will be the max, we're there
        
                                                                if (   B == mm.Bmax - 1)
        
                                                                        break ;
        
                  }
        
                                         // Cut the two split boxes.
        
                                         //   The second argument to CutBox is the new box count.
        
                                         //   The third argument is the new min and max values.
        

                  mmNew = mm ;
        
                  mmNew.Bmin = mm.Bmin ;
        
                  mmNew.Bmax = B ;
        

                  CutBox (      piCount, iCount, mmNew, iRes, iLevel + 1,
        
                                                        pboxes, piEntry) ;
        

                  mmNew.Bmin = B + 1 ;
        
                  mmNew.Bmax = mm.Bmax ;
        

                  CutBox (      piCount, iBoxCount - iCount, mmNew, iRes, iLevel + 1,
        
                                                         pboxes, piEntry) ;
        
    }
        
                  // Otherwise, if red is the largest side, split it (just like blue)
        
           else if (mm.Rmax - mm.Rmin > mm.Gmax - mm.Gmin)
        
    {
        
                  iCount = 0 ;
        
                  for (R = mm.Rmin ; R < mm.Rmax ; R++)
        
                  {
        
                                                 for (B = mm.Bmin ; B <= mm.Bmax ; B++)
        
                                                for (G = mm.Gmin ; G <= mm.Gmax ; G++)
        
                                         iCount += piCount [PACK_RGB (R, G, B, iRes)] ;
        
                                                 if (iCount >= iBoxCount / 2)
        
                                                                break ;
        
                                                 if (R == mm.Rmax - 1)
        
                                                               break ;
        
                  }
        
                  mmNew = mm ;
        
                  mmNew.Rmin = mm.Rmin ;
        
                  mmNew.Rmax = R ;
        

                  CutBox (      piCount, iCount, mmNew, iRes, iLevel + 1,
        
                                                         pboxes, piEntry) ;
        

                  mmNew.Rmin    = R + 1 ;
        
                 mmNew.Rmax    = mm.Rmax ;
        
                  CutBox (      piCount, iBoxCount - iCount, mmNew, iRes, iLevel + 1,
        
                                                         pboxes, piEntry) ;
        
    }
        
                                  // Otherwise, split along the green size
        
           else
        
           {
        
                  iCount = 0 ;
        
                  for (G = mm.Gmin ; G < mm.Gmax ; G++)
        
                {
        
                                  for (  B = mm.Bmin ; B <= mm.Bmax ; B++)
        
                                  for (  R = mm.Rmin ; R <= mm.Rmax ; R++)
        
                                  iCount += piCount [PACK_RGB (R, G, B, iRes)] ;
        

                                  if (   iCount >= iBoxCount / 2)
        
                                                break ;
        

                                  if (   G == mm.Gmax - 1)
        
                                                 break ;
        
                  }
        
                  mmNew = mm ;
        
                  mmNew.Gmin = mm.Gmin ;
        
                  mmNew.Gmax = G ;
        

                  CutBox (      piCount, iCount, mmNew, iRes, iLevel + 1,
        
                                                         pboxes, piEntry) ;
        

                  mmNew.Gmin    = G + 1 ;
        
                  mmNew.Gmax    = mm.Gmax ;
        

                  CutBox (      piCount, iBoxCount - iCount, mmNew, iRes, iLevel + 1,
        
                                                         pboxes, piEntry) ;
        
           }
        
}
        

/*---------------------------------------------------------------------------
        
  Compare routine for qsort
        
-----------------------------------------------------------------------------*/
        

static int Compare (const BOXES * pbox1, const BOXES * pbox2)
        
{
        
          return pbox1->iBoxCount - pbox2->iBoxCount ;
        
}
        

/*---------------------------------------------------------------------------
        
  DibPalMedianCut:  Creates palette based on median cut algorithm
        
-------------------------------------------------------------------------*/
        
HPALETTE DibPalMedianCut (HDIB hdib, int iRes)
        
{
        
           BOXES                         boxes [256] ;
        
           HPALETTE                      hPalette ;
        
           int                          i, iArraySize, iCount, R, G, B, iTotCount, iDim, iEntry = 0 ;
        
           int                           *      piCount ;
        
         LOGPALETTE    *      plp ;
        
           MINMAX                        mm ;
        

                          // Validity checks
        
  
        
           if (DibBitCount (hdib) < 16)
        
                          return NULL ;
        
           if (iRes < 3 || iRes > 8)
        
                          return NULL ;
        
                          // Accumulate counts of pixel colors
        
    iArraySize = 1 << (3 * iRes) ;
        
    if (NULL == (piCount = calloc (iArraySize, sizeof (int))))
        
                          return NULL ;
        
    AccumColorCounts (hdib, piCount, iRes) ;
        
                   // Find the dimensions of the total box
        
    iDim = 1 << iRes ;
        
    mm.Rmin = mm.Gmin = mm.Bmin = iDim - 1 ;
        
    mm.Rmax = mm.Gmax = mm.Bmax = 0 ;
        

    iTotCount = 0 ;
        
           for (R = 0 ; R < iDim ; R++)
        
           for (G = 0 ; G < iDim ; G++)
        
           for (B = 0 ; B < iDim ; B++)
        
                          if ((iCount = piCount [PACK_RGB (R, G, B, iRes)]) > 0)
        
                          {
        
                                                 iTotCount += iCount ;
        
                                                 if (R < mm.Rmin) mm.Rmin = R ;
        
                                                 if (G < mm.Gmin) mm.Gmin = G ;
        
                                                 if (B < mm.Bmin) mm.Bmin = B ;
        
                                                 if (R > mm.Rmax) mm.Rmax = R ;
        
                                                 if (G > mm.Gmax) mm.Gmax = G ;
        
                                               if (B > mm.Bmax) mm.Bmax = B ;
        
                          }
        

                  // Cut the first box (iterative function).
        
                     // On return, the boxes structure will have up to 256 RGB values,
        
                  //     one for each of the boxes, and the number of pixels in
        
                  //  each box.
        
                  //  The iEntry value will indicate the number of non-empty boxes.
        

           CutBox (piCount, iTotCount, mm, iRes, 0, boxes, &iEntry) ;
        
           free (piCount) ;
        

                  // Sort the RGB table by the number of pixels for each color
        
           qsort (boxes, iEntry, sizeof (BOXES), Compare) ;
        
           plp = malloc (sizeof (LOGPALETTE) + (iEntry - 1) * sizeof (PALETTEENTRY)) ;
        
           if (plp == NULL)
        
                          return NULL ;
        
           plp->palVersion               = 0x0300 ;
        
           plp->palNumEntries    = iEntry ;
        

           for (i = 0 ; i < iEntry ; i++)
        
           {
        
                          plp->palPalEntry[i].peRed  = boxes[i].rgbBoxAv.rgbRed ;
        
                          plp->palPalEntry[i].peGreen= boxes[i].rgbBoxAv.rgbGreen ;
        
                          plp->palPalEntry[i].peBlue = boxes[i].rgbBoxAv.rgbBlue ;
        
                          plp->palPalEntry[i].peFlags= 0 ;
        
    }
        

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

第一个函数-DibPalDibTable-看起来应该很熟悉.它根据DIB的颜色表建立了调色板.这与本章前面的SHOWDIB3中所用到的PACKEDIB.C里的PackedDibCreatePalette函数相似.在SHOWDIB3中,只有当DIB有颜色表时才执行此函数.在8位显示模式下试图显示16位、24位或32位DIB时,此函数就没用了.

预设情况下,执行在256色显示模式下时,DIBBLE将首先尝试呼叫DibPalDibTable来根据DIB颜色表建立调色板.如果DIB没有颜色表,则DIBBLE将呼叫CreateHalftonePalette并将fHalftonePalette变量设定为TRUE.此逻辑发生在WM_USER_CREATEPAL消息处理期间.

DIBPAL.C也执行函数DibPalAllPurpose,因为此函数与SHOWDIB4中的CreateAllPurposePalette函数非常相似,所以它看起来也很熟悉.您也可以从DIBBLE菜单中选择此调色板.

在256色模式下显示位图最有趣的是,您可以直接控制Windows用于显示图像的颜色.如果您选择并显现调色板,则Winsows将使用此调色板中的颜色,而不是其它调色板中的颜色.

例如,您可以用DibPalUniformGrays函数来单独建立一种灰阶调色板.使用两种灰阶的调色板则只含有00-00-00(黑色)和FF-FF-FF(白色).用此调色板来输出图像将提供某些照片中常用的高对比「黑白」效果.使用3种灰阶将在黑色和白色中间添加中间灰色,使用4种灰阶将添加2种灰阶.

用8种灰阶,您就有可能看到明显的轮廓-相同灰阶的无规则斑点,虽然很明显地执行了最接近颜色算法,但是一般仍不带有任何审美判断.通常到16种灰阶就可以明显改善图像画质.使用32种灰阶差不多就可以消除全部轮廓了.而目前普遍认为64种灰阶是现在大多数显示设备的极限.在这点以上,再提升也没什么边际效益了.在6位颜色分辨率的设备上提供超过64种灰阶看不出有什么改进之处.

迄今为止,对于8位显示模式下显示16位、24位和32位彩色DIB,我们最多就是能够设计通用调色板(这对灰阶图像很有效,但通常不适于彩色图像)或者使用中间色调色板,它用混色显示与通用颜色调色板合用.

还应注意,当您在8位颜色模式下为大张16位、24位或32位DIB选择通用调色板时,为了要显示这些图像,程序将花费一些时间依据DIB的内容来建立GDI位图对象.如果不需要调色板,则程序根据DIB来建立DDB的时间会更少(用8位彩色模式显示大24位DIB时,比较SHOWDIB1和SHOWDIB4的性能,您也能看出这点区别).这是为什么呢?

它按最接近颜色搜寻.通常,用8位显示模式显示24位DIB时(或者将DIB转换为DDB),GDI必须将DIB中的每个像素都与静态20种颜色中的一种相贴近.完成此操作的唯一方法是决定哪种静态颜色与像素颜色最接近.这包括计算像素与三维RGB颜色中每种静态颜色的距离.这将花些时间,特别是在DIB图像中有上百万个图素时.

在建立232色调色板时,例如DIBBLE和SHOWDIB4中的通用调色板,您会很快将搜索最接近颜色的时间增加到超过11倍!GDI现在必须彻底检查232种颜色,而不是20种.那就是显示DIB的整个作业放慢的原因.

这里的教训是避免在8位显示模式下显示24位(或16位,或32位)DIB.您应该找出最接近DIB图像颜色范围的256色调色板,来将它们转换成8位DIB.这经常称为「最佳调色板」.当我研究这个问题的时候,Paul Heckbert编写的〈Color Image Quantization for Frame Buffer Displays〉(刊登在1982年7月出版的《Computer Graphics》)对此问题有所帮助.

均匀分布

建立256色调色板最简单的方法是选择范围统一的RGB颜色值,它与DibPalAllPurpose中的方法相似.此方法的优点是您不必检查DIB中的实际像素.这个函数是DibPalCreateUniformColors,它依据范围统一的RGB三原色索引建立调色板.

一个合理的分布包括8阶红色和绿色以及4阶蓝色(肉眼对蓝色较不敏感).调色板是RGB颜色值的集合,它是红色和绿色值0x00、0x24、0x49、0x6D、0x92、0xB6、0xDB和0xFF以及蓝色值0x00、0x55、0xAA和0xFF的所有可能的组合,共有256种颜色.另一种可能的统一分布调色板使用6阶红色、绿色和蓝色.此调色板是红色、绿色和蓝色值为0x00、0x33、0x66、0x99、0xCC和0xFF的所有可能的组合,调色板中的颜色数是6的3次方,即216.

这两个选项和其它几个选项都由DIBBLE提供.

「Popularity」算法

「Popularity」算法是256色调色板问题相当明显的解决方法.您要做的就是走遍位图中的所有图素,并找出256种最普通的RGB颜色值.这些就是您在调色板中使用的值.DIBPAL的DibPalPopularity函数中实作了这种算法.

不过,如果每种颜色都使用整个24位,而且假设需要用整数来计算所有的颜色,那么数组将占据64MB内存.另外,您可以发现位图中实际上没有(或很少)重复的24位像素值,这样就没有所谓常见的颜色了.

要解决这个问题,您可以只使用每个红色、绿色和蓝色值中最重要的n位-例如,6位而不是8位.因为大多数的彩色扫描仪和视讯显示卡都只有6位的分辨率,所以这样规定更有意义.这将数组减少到大小更合理的256KB或1MB.只使用5位能将可用的颜色总数减少到32,768.通常,使用5位要比6位的性能更好.对此,您可以用DIBBLE和一些图像颜色来自己检验.

「Median Cut」算法

DIBPAL.C中的DibPalMedianCut函数执行Paul Heckbert的Median Cut算法.此算法在概念上相当简单,但在程序代码中实作要比Popularity算法更困难,它适合递归函数.

画出RGB颜色立方体.图像中的每个图素都是此立方体中的一个点.一些点可能代表图像中的多个图素.找出包括图像中所有图素的立体方块,找出此方块的最大尺寸,并将方块分成两个,每个方块都包括相同数量的图素.对于这两个方块,执行相同的操作.现在您就有4个方块,将这4个方块分成8个,然后再分成16个、32个、64个、128个和256个.

现在您有256个方块,每个方块都包括相同数量的像素.取每个方块中像素RGB颜色值的平均值,并将结果用于调色板.

实际上,这些方块通常包含像素的数量并不相同.例如,通常包括单个点的方块会有更多的像素.这发生在黑色和白色上.有时,一些方块里头根本没有像素.如果这样,您就可以省下更多的方块,但是我决定不这样做.

另一种最佳化调色板的技术称为「octree quantization」,此技术由Jeff Prosise提出,并于1996年8月发表在《Microsoft Systems Journal》上(包含在MSDN的CD中).

转换格式

DIBBLE还允许将DIB从一种格式转换到另一种格式.这用到了DIBCONV文件中的DibConvert函数,如程序16-25所示.

程序16-25 DIBCONV文件
        
DIBCONV.H
        
/*----------------------------------------------------------------------------
        
  DIBCONV.H header file for DIBCONV.C
        
-----------------------------------------------------------------------------*/
        

HDIB DibConvert (HDIB hdibSrc, int iBitCountDst) ;
        
DIBCONV.C
        
/*---------------------------------------------------------------------------
        
  DIBCONV.C --  Converts DIBs from one format to another
        
                                                       (c) Charles Petzold, 1998
        
----------------------------------------------------------------------------*/
        
#include <windows.h>
        
#include "dibhelp.h"
        
#include "dibpal.h"
        
#include "dibconv.h"
        

HDIB DibConvert (HDIB hdibSrc, int iBitCountDst)
        
{
        
           HDIB                                 hdibDst ;
        
           HPALETTE                      hPalette ;
        
           int                                  i, x, y, cx, cy, iBitCountSrc, cColors ;
        
           PALETTEENTRY pe ;
        
           RGBQUAD                       rgb ;
        
           WORD                                 wNumEntries ;
        

           cx = DibWidth (hdibSrc) ;
        
           cy = DibHeight (hdibSrc) ;
        
           iBitCountSrc = DibBitCount (hdibSrc) ;
        

           if (iBitCountSrc == iBitCountDst)
        
                          return NULL ;
        
                  // DIB with color table to DIB with larger color table:
        
           if ((iBitCountSrc < iBitCountDst) && (iBitCountDst <= 8))
        
           {
        
                  cColors = DibNumColors (hdibSrc) ;
        
                  hdibDst = DibCreate (cx, cy, iBitCountDst, cColors) ;
        

                  for (i = 0 ; i < cColors ; i++)
        
                  {
        
                                         DibGetColor (hdibSrc, i, &rgb) ;
        
                                        DibSetColor (hdibDst, i, &rgb) ;
        
                 }
        

                  for (x = 0 ; x < cx ; x++)
        
                  for (y = 0 ; y < cy ; y++)
        
                  {
        
                          DibSetPixel (hdibDst, x, y, DibGetPixel (hdibSrc, x, y)) ;
        
                  }
        
           }
        
                 // Any DIB to DIB with no color table
        
           else if (iBitCountDst >= 16)
        
           {
        
                  hdibDst = DibCreate (cx, cy, iBitCountDst, 0) ;
        
                  for (x = 0 ; x < cx ; x++)
        
                  for (y = 0 ; y < cy ; y++)
        
                  {
        
                                         DibGetPixelColor (hdibSrc, x, y, &rgb) ;
        
                                       DibSetPixelColor (hdibDst, x, y, &rgb) ;
        
            }
        
           }
        
                  // DIB with no color table to 8-bit DIB
        
           else if (iBitCountSrc >= 16 && iBitCountDst == 8)
        
           {
        
                          hPalette = DibPalMedianCut (hdibSrc, 6) ;
        
                          GetObject (hPalette, sizeof (WORD), &wNumEntries) ;
        

                          hdibDst = DibCreate (cx, cy, 8, wNumEntries) ;
        
                          for (i = 0 ; i < (int) wNumEntries ; i++)
        
                          {
        
                                         GetPaletteEntries (hPalette, i, 1, &pe) ;
        
                                         rgb.rgbRed                    = pe.peRed ;
        
                                        rgb.rgbGreen                  = pe.peGreen ;
        
                                         rgb.rgbBlue                   = pe.peBlue ;
        
                                         rgb.rgbReserved               = 0 ;
        

                                         DibSetColor (hdibDst, i, &rgb) ;
        
                          }
        

                          for (x = 0 ; x < cx ; x++)
        
                          for (y = 0 ; y < cy ; y++)
        
                          {
        
                                                 DibGetPixelColor (hdibSrc, x, y, &rgb) ;
        

                                                 DibSetPixel (hdibDst, x, y,
        
                                 GetNearestPaletteIndex (hPalette,
        
                                  RGB (rgb.rgbRed, rgb.rgbGreen, rgb.rgbBlue))) ;
        
                          }
        
                          DeleteObject (hPalette) ;
        
                  }
        
                                  // Any DIB to monochrome DIB
        

                  else if (iBitCountDst == 1)
        
                  {
        
                          hdibDst = DibCreate (cx, cy, 1, 0) ;
        
                          hPalette = DibPalUniformGrays (2) ;
        

                          for (i = 0 ; i < 2 ; i++)
        
                          {
        
                                                GetPaletteEntries (hPalette, i, 1, &pe) ;
        

                                                 rgb.rgbRed   = pe.peRed ;
        
                                                 rgb.rgbGreen = pe.peGreen ;
        
                                                 rgb.rgbBlue  = pe.peBlue ;
        
                                                rgb.rgbReserved = 0 ;
        

                                                 DibSetColor (hdibDst, i, &rgb) ;
        
                          }
        

                          for (x = 0 ; x < cx ; x++)
        
                          for (y = 0 ; y < cy ; y++)
        
                          {
        
                                                 DibGetPixelColor (hdibSrc, x, y, &rgb) ;
        

                                                DibSetPixel (hdibDst, x, y,
        
                                  GetNearestPaletteIndex (hPalette,
        
                                 RGB (rgb.rgbRed, rgb.rgbGreen, rgb.rgbBlue))) ;
        
                          }
        
                          DeleteObject (hPalette) ;
        
           }
        
                          // All non-monochrome DIBs to 4-bit DIB
        
           else if (iBitCountSrc >= 8 && iBitCountDst == 4)
        
           {
        
                          hdibDst = DibCreate (cx, cy, 4, 0) ;
        
                          hPalette = DibPalVga () ;
        

                          for (i = 0 ; i < 16 ; i++)
        
                          {
        
                                                GetPaletteEntries (hPalette, i, 1, &pe) ;
        
                                                 rgb.rgbRed                           = pe.peRed ;
        
                                                 rgb.rgbGreen                  = pe.peGreen ;
        
                                                 rgb.rgbBlue                          = pe.peBlue ;
        
                                                 rgb.rgbReserved               = 0 ;
        

                                                 DibSetColor (hdibDst, i, &rgb) ;
        
                          }
        

                          for (x = 0 ; x < cx ; x++)
        
                          for (y = 0 ; y < cy ; y++)
        
                          {
        
                                                 DibGetPixelColor (hdibSrc, x, y, &rgb) ;
        

                                         DibSetPixel (hdibDst, x, y,
        
                         GetNearestPaletteIndex (hPalette,
        
                          RGB (rgb.rgbRed, rgb.rgbGreen, rgb.rgbBlue))) ;
        
                          }
        
                          DeleteObject (hPalette) ;
        
           }
        
                          // Should not be necessary
        
           else
        
                          hdibDst = NULL ;
        
           return hdibDst ;
        
}
        

将DIB从一种格式转换成另一种格式需要几种不同的方法.

要将带有颜色表的DIB转换成另一种也带有颜色表但有较大的像素宽度的DIB(亦即,将1位DIB转换成4位或8位DIB,或将4位DIB转换成8位DIB),所需要做的就是透过呼叫DibCreate来建立新的DIB,并在呼叫时带有希望的位数以及与原始DIB中的颜色数相等的颜色数.然后函数复制像素位和颜色表项目.

如果新的DIB没有颜色表(即位数是16、24或32),那么DIB只需要按新格式建立,而且通过呼叫DibGetPixelColor和DibSetPixelColor从现有的DIB中复制像素位.

下面的情况可能更普遍:现有的DIB没有颜色表(即位数是16、24或32),而新的DIB每像素占8位.这种情况下,DibConvert呼叫DibPalMedianCut来为图像建立最佳化的调色板.新DIB的颜色表设定为调色板中的RGB值.DibGetPixelColor函数从现有的DIB中获得像素颜色.透过呼叫GetNearestPaletteIndex来转换成8位DIB中的像素值,并透过呼叫DibSetPixel将像素值储存到DIB.

当DIB需要转换成单色DIB时,用包括两个项目-黑色和白色-的颜色表建立新的DIB.另外,GetNearestPaletteIndex有助于将现有DIB中的颜色转换成像素值0或1.类似地,当8个像素位或更多位的DIB要转换成4位DIB时,可从DibPalVga函数获得DIB颜色表,同时GetNearestPaletteIndex也有助于计算像素值.

尽管DIBBLE示范了如何开始写一个图像处理程序基础,但是程序最后还是没有全部完成,我们总是会想到还有些功能没有加进去里头.但是很可惜的是,我们现在得停止继续研究这些东西,而往下讨论别的东西了.

DIBBLE 程序清单如下,我只实现了File/Edit/Show菜单中的功能,以及Convert中的部分功能.原来的C语言程序中,Convert的功能上是有问题的,在从颜色少的转化为颜色多的过程中会报错.

;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc
Include masm32.inc
Include comdlg32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
IncludeLib masm32.lib
IncludeLib comdlg32.lib

include macro.asm

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
MemCopy PROTO :DWORD,:DWORD,:DWORD ; source - dest - length

DibGetPixel1 PROTO :DWORD,:DWORD,:DWORD
DibGetPixel4 PROTO :DWORD,:DWORD,:DWORD
DibGetPixel8 PROTO :DWORD,:DWORD,:DWORD
DibGetPixel16 PROTO :DWORD,:DWORD,:DWORD
DibGetPixel24 PROTO :DWORD,:DWORD,:DWORD
DibGetPixel32 PROTO :DWORD,:DWORD,:DWORD

DibSetPixel1 PROTO :DWORD,:DWORD,:DWORD,:DWORD
DibSetPixel4 PROTO :DWORD,:DWORD,:DWORD,:DWORD
DibSetPixel8 PROTO :DWORD,:DWORD,:DWORD,:DWORD
DibSetPixel16 PROTO :DWORD,:DWORD,:DWORD,:DWORD
DibSetPixel24 PROTO :DWORD,:DWORD,:DWORD,:DWORD
DibSetPixel32 PROTO :DWORD,:DWORD,:DWORD,:DWORD
DIBSTRUCT struct
ppRow DWORD ? ; must be first field for macros!
iSignature DWORD ?
hBitmap HBITMAP ?
pBits DWORD ? ;指向BYTE的指针
dsDIB DIBSECTION <>
;DIBSECTION STRUCT
; dsBm BITMAP <>
;BITMAP STRUCT
; bmType DWORD ?
; bmWidth DWORD ?
; bmHeight DWORD ?
; bmWidthBytes DWORD ?
; bmPlanes WORD ?
; bmBitsPixel WORD ?
; bmBits DWORD ?
;BITMAP ENDS
; dsBmih BITMAPINFOHEADER <>
;BITMAPINFOHEADER STRUCT
; biSize DWORD ?
; biWidth DWORD ?
; biHeight DWORD ?
; biPlanes WORD ?
; biBitCount WORD ?
; biCompression DWORD ?
; biSizeImage DWORD ?
; biXPelsPerMeter DWORD ?
; biYPelsPerMeter DWORD ?
; biClrUsed DWORD ?
; biClrImportant DWORD ?
;BITMAPINFOHEADER ENDS
; dsBitfields DWORD 3 dup (?)
; dshSection DWORD ?
; dsOffset DWORD ?
;DIBSECTION ENDS
iRShift DWORD 3 dup(0)
iLShift DWORD 3 dup(0)
DIBSTRUCT ends

; Structures used for implementing median cut algorithm
MINMAX struct ; defines dimension of a box
Rmin DWORD 0
Rmax DWORD 0
Gmin DWORD 0
Gmax DWORD 0
Bmin DWORD 0
Bmax DWORD 0
MINMAX ends

BOXES struct ; for Compare routine for qsort
iBoxCount DWORD 0
rgbBoxAv RGBQUAD <>
BOXES ends

BITMAPV5HEADER STRUCT
bV5Size DWORD ?
bV5Width DWORD ?
bV5Height DWORD ?
bV5Planes WORD ?
bV5BitCount WORD ?
bV5V5Compression DWORD ?
bV5SizeImage DWORD ?
bV5XPelsPerMeter DWORD ?
bV5YPelsPerMeter DWORD ?
bV5ClrUsed DWORD ?
bV5ClrImportant DWORD ?
bV5RedMask DWORD ?
bV5GreenMask DWORD ?
bV5BlueMask DWORD ?
bV5AlphaMask DWORD ?
bV5CSType DWORD ?
bV5Endpoints CIEXYZTRIPLE <>
bV5GammaRed DWORD ?
bV5GammaGreen DWORD ?
bV5GammaBlue DWORD ?
;V5 New added
bV5Intent DWORD ?
bV5ProfileData DWORD ?
bV5ProfileSize DWORD ?
bV5Reserved DWORD ?
BITMAPV5HEADER ENDS

HDIB typedef DWORD

WM_USER_SETSCROLLS EQU (WM_USER + 1)
WM_USER_DELETEDIB EQU (WM_USER + 2)
WM_USER_DELETEPAL EQU (WM_USER + 3)
WM_USER_CREATEPAL EQU (WM_USER + 4)

IDM_FILE_OPEN EQU 40001

IDM_FILE_SAVE EQU 40002
IDM_FILE_PRINT EQU 40003
IDM_FILE_PROPERTIES EQU 40004
IDM_APP_EXIT EQU 40005
IDM_EDIT_CUT EQU 40006
IDM_EDIT_COPY EQU 40007
IDM_EDIT_PASTE EQU 40008
IDM_EDIT_DELETE EQU 40009
IDM_EDIT_FLIP EQU 40010
IDM_EDIT_ROTATE EQU 40011
IDM_SHOW_NORMAL EQU 40012
IDM_SHOW_CENTER EQU 40013
IDM_SHOW_STRETCH EQU 40014
IDM_SHOW_ISOSTRETCH EQU 40015
IDM_PAL_NONE EQU 40016
IDM_PAL_DIBTABLE EQU 40017
IDM_PAL_HALFTONE EQU 40018
IDM_PAL_ALLPURPOSE EQU 40019
IDM_PAL_GRAY2 EQU 40020
IDM_PAL_GRAY3 EQU 40021
IDM_PAL_GRAY4 EQU 40022
IDM_PAL_GRAY8 EQU 40023
IDM_PAL_GRAY16 EQU 40024
IDM_PAL_GRAY32 EQU 40025
IDM_PAL_GRAY64 EQU 40026
IDM_PAL_GRAY128 EQU 40027
IDM_PAL_GRAY256 EQU 40028
IDM_PAL_RGB222 EQU 40029
IDM_PAL_RGB333 EQU 40030
IDM_PAL_RGB444 EQU 40031
IDM_PAL_RGB555 EQU 40032
IDM_PAL_RGB666 EQU 40033
IDM_PAL_RGB775 EQU 40034
IDM_PAL_RGB757 EQU 40035
IDM_PAL_RGB577 EQU 40036
IDM_PAL_RGB884 EQU 40037
IDM_PAL_RGB848 EQU 40038
IDM_PAL_RGB488 EQU 40039
IDM_PAL_OPT_POP4 EQU 40040
IDM_PAL_OPT_POP5 EQU 40041
IDM_PAL_OPT_POP6 EQU 40042
IDM_PAL_OPT_MEDCUT EQU 40043
IDM_CONVERT_01 EQU 40044
IDM_CONVERT_04 EQU 40045
IDM_CONVERT_08 EQU 40046
IDM_CONVERT_16 EQU 40047
IDM_CONVERT_24 EQU 40048
IDM_CONVERT_32 EQU 40049
IDM_APP_ABOUT EQU 40050

.DATA
szAppName TCHAR "Dibble",0
PDIBSTRUCT DWORD ? ;指向DIBSTRUCT结构体的指针

szdinfo BYTE "Dibble:Printing",0
dinfo DOCINFO <sizeof(DOCINFO),offset(szdinfo),0,0,0>
wShow DWORD IDM_SHOW_NORMAL
printdlg PRINTDLG <sizeof (PRINTDLG)>
szDib DB 'Dib ',0
wItem WORD IDM_PAL_NONE
szFileProperties db "Pixel width: %i",13,10,"Pixel height: %i",13,10,"Bits per pixel: %i",13,10,"Number of colors: %i",13,10,"Compression: %s",0
szCompression db "BI_RGB ",0,
"BI_RLE8 ",0,
"BI_RLE4 ",0,
"BI_BITFIELDS",0,
"Unknown ",0
rgbVG db 00, 00, 00,00,00, 00, 80h, 00,00, 80h, 00, 00,00, 80h, 80h, 00,\
80h, 00, 00, 00,80h, 00, 80h, 00,80h, 80h, 00, 00,80h, 80h, 80h, 00,\
0C0h, 0C0h, 0C0h, 00,00, 00, 0FFh, 00,00, 0FFh, 00, 00,00, 0FFh, 0FFh, 00
rgbVG2 db 0FFh, 00, 00, 00,0FFh, 00, 0FFh, 00,0FFh, 0FFh, 00, 00,0FFh, 0FFh, 0FFh, 00
.DATA?
hglbBitmap HBITMAP ?
hglbdib HDIB ?
hMenu HDIB ?
hglbPalette HPALETTE ?
fHalftonePalette DWORD ?

hInstance HINSTANCE ?
cxClient DWORD ?
cyClient DWORD ?
iVscroll DWORD ?
iHscroll DWORD ?
ofn OPENFILENAME <>
szglbFileName TCHAR MAX_PATH dup (?)
szTitleName TCHAR MAX_PATH dup (?)

.CODE

; DibIsValid: Returns TRUE if hdib points to a valid DIBSTRUCT
DibIsValid proc hdib:HDIB
LOCAL pdib:DWORD
mov eax,hdib
mov pdib,eax
.if (pdib == NULL)
mov eax,FALSE
ret
.endif

invoke IsBadReadPtr,pdib, sizeof (DIBSTRUCT)
.if eax!=FALSE
mov eax,FALSE
ret
.endif

mov esi,pdib
mov eax,[esi+4] ;pdib->iSignature
.if (eax!=20626944h) ;'Dib '
mov eax,FALSE
ret
.endif

mov eax,TRUE
ret
DibIsValid endp

; DibBitmapHandle: Returns the handle to the DIB section bitmap object
DibBitmapHandle proc hdib:HDIB

invoke DibIsValid,hdib
.if eax==FALSE
mov eax,NULL
ret
.endif

mov esi,hdib
mov eax,[esi+8] ;return ((PDIBSTRUCT) hdib)->hBitmap ;
ret
DibBitmapHandle endp

; DibWidth: Returns the bitmap pixel width
DibWidth proc hdib:HDIB
invoke DibIsValid,hdib
.if eax==FALSE
mov eax,0
ret
.endif

mov esi,hdib
mov eax,[esi+20] ;((PDIBSTRUCT) hdib)->ds.dsBm.bmWidth
ret
DibWidth endp

; DibHeight: Returns the bitmap pixel height
DibHeight proc hdib:HDIB
invoke DibIsValid,hdib
.if eax==FALSE
mov eax,0
ret
.endif
mov esi,hdib
mov eax,[esi+24] ;((PDIBSTRUCT) hdib)->ds.dsBm.bmHeight
ret
DibHeight endp

; DibBitCount: Returns the number of bits per pixel
DibBitCount proc hdib:HDIB
invoke DibIsValid,hdib
.if eax==FALSE
mov eax,0
ret
.endif
mov esi,hdib
mov eax,[esi+34] ;(PDIBSTRUCT) hdib)->ds.dsBm.bmBitsPixel
and eax,0FFFFh
ret
DibBitCount endp

; DibRowLength: Returns the number of bytes per row of pixels
DibRowLength proc hdib:HDIB

invoke DibIsValid,hdib
.if eax==FALSE
mov eax,0
ret
.endif

;4 * ((DibWidth (hdib) * DibBitCount (hdib) + 31) / 32) ;
invoke DibWidth,hdib
push eax
invoke DibBitCount,hdib
pop ecx
mul ecx
add eax,31
xor edx,edx
mov ecx,32
div ecx
shl eax,2
ret
DibRowLength endp

; DibNumColors: Returns the number of colors in the color table
DibNumColors proc hdib:HDIB
LOCAL pdib:DWORD
mov eax,hdib
mov pdib,eax

invoke DibIsValid,hdib
.if eax==FALSE
mov eax,0
ret
.endif

mov esi,pdib
mov eax,[esi+72] ;pdib->ds.dsBmih.biClrUsed

.if eax!=0
ret ;return pdib->ds.dsBmih.biClrUsed
.else
invoke DibBitCount,hdib
.if eax<= 8
mov ebx,1
mov ecx,eax
shl ebx,cl ;return 1 << DibBitCount (hdib)
ret
.endif
.endif
xor eax,eax
ret
DibNumColors endp

; DibMask: Returns one of the color masks
DibMask proc hdib:HDIB,i:DWORD
LOCAL pdib:DWORD

mov eax,hdib
mov pdib,eax
invoke DibIsValid,hdib

.if (eax==FALSE || i < 0 || i > 2)
xor eax,eax
ret
.endif
mov esi,pdib ;return pdib->ds.dsBitfields[i]
mov ebx,i
shl ebx,2 ;*4
mov eax,[esi+ebx+80]
ret
DibMask endp

; DibRShift: Returns one of the right-shift values
DibRShift proc hdib:HDIB,i:DWORD
LOCAL pdib:DWORD
mov eax,hdib
mov pdib,eax
invoke DibIsValid,hdib
.if (eax==FALSE || i < 0 || i > 2)
xor eax,eax
ret
.endif

mov esi,pdib
mov ebx,i
shl ebx,2
mov eax,[esi+ebx+92] ;return pdib->iRShift[i]
ret
DibRShift endp

; DibLShift: Returns one of the left-shift values
DibLShift proc hdib:HDIB,i:DWORD
LOCAL pdib:DWORD
mov eax,hdib
mov pdib,eax
invoke DibIsValid,hdib
.if (eax==FALSE || i < 0 || i > 2)
xor eax,eax
ret
.endif
mov esi,pdib
mov eax,i
shl eax,2
mov eax,[esi+eax+104] ;return pdib->iLShift[i]
ret
DibLShift endp

; DibCompression: Returns the value of the biCompression field
DibCompression proc hdib:HDIB
invoke DibIsValid,hdib
.if eax==FALSE
xor eax,eax
ret
.endif
mov esi,hdib
mov eax,[esi+56] ;return ((PDIBSTRUCT) hdib)->ds.dsBmih.biCompression ;
ret
DibCompression endp

; DibIsAddressable: Returns TRUE if the DIB is not compressed
DibIsAddressable proc hdib:HDIB
LOCAL iCompression:DWORD
invoke DibIsValid,hdib

.if eax==FALSE
mov eax,FALSE
ret
.endif

invoke DibCompression,hdib
mov iCompression,eax

.if ( iCompression == BI_RGB || iCompression == BI_BITFIELDS)
mov eax,TRUE
ret
.endif

mov eax,FALSE
ret
DibIsAddressable endp

; These functions return the sizes of various components of the DIB section
; AS THEY WOULD APPEAR in a packed DIB. These functions aid in converting
; the DIB section to a packed DIB and in saving DIB files.
DibInfoHeaderSize proc hdib:HDIB
invoke DibIsValid,hdib
.if eax==FALSE
xor eax,eax
ret
.endif
mov esi,hdib
mov eax,[esi+40] ;return ((PDIBSTRUCT) hdib)->ds.dsBmih.biSize
ret
DibInfoHeaderSize endp

DibMaskSize proc hdib:HDIB
LOCAL pdib:DWORD
mov eax,hdib
mov pdib,eax
invoke DibIsValid,hdib
.if eax==FALSE
xor eax,eax
ret
.endif
mov esi,pdib
mov eax,[esi+56]
.if eax == BI_BITFIELDS
mov eax,12 ;return 3 * sizeof (DWORD) ;
ret
.endif

xor eax,eax
ret
DibMaskSize endp

DibColorSize proc hdib:HDIB
invoke DibNumColors,hdib
mov ecx, sizeof (RGBQUAD) ;return DibNumColors (hdib) * sizeof (RGBQUAD)
mul ecx
ret
DibColorSize endp

DibInfoSize proc hdib:HDIB
;return DibInfoHeaderSize(hdib) + DibMaskSize(hdib) + DibColorSize(hdib)
invoke DibInfoHeaderSize,hdib
push eax
invoke DibMaskSize,hdib
push eax
invoke DibColorSize,hdib
pop ecx
add eax,ecx
pop ecx
add eax,ecx
ret
DibInfoSize endp

DibBitsSize proc hdib:HDIB
LOCAL pdib:DWORD
mov eax,hdib
mov pdib,eax
invoke DibIsValid,hdib
.if eax==FALSE
xor eax,eax
ret
.endif
mov esi,pdib
mov eax,[esi+60]
.if eax!=0
ret ;return pdib->ds.dsBmih.biSizeImage
.endif

invoke DibHeight,hdib ;return DibHeight (hdib) * DibRowLength (hdib)
push eax
invoke DibRowLength,hdib
pop ecx
mul ecx
ret
DibBitsSize endp

DibTotalSize proc hdib:HDIB
invoke DibInfoSize,hdib ;return DibInfoSize (hdib) + DibBitsSize (hdib)
push eax
invoke DibBitsSize,hdib
pop ecx
add eax,ecx
ret
DibTotalSize endp

; These functions return pointers to the various components of the DIB section.
DibInfoHeaderPtr proc hdib:HDIB
invoke DibIsValid,hdib
.if eax==FALSE
xor eax,eax
ret
.endif
mov eax,hdib
add eax,40 ; return & (((PDIBSTRUCT) hdib)->ds.dsBmih)
ret
DibInfoHeaderPtr endp

DibMaskPtr proc hdib:HDIB
LOCAL pdib:DWORD
mov eax,hdib
mov pdib,eax
invoke DibIsValid,hdib
.if eax==FALSE
xor eax,eax
ret
.endif

mov esi,pdib ; return pdib->ds.dsBitfields
mov eax,[esi+80]
ret
DibMaskPtr endp

DibBitsPtr proc hdib:HDIB
invoke DibIsValid,hdib
.if eax==FALSE
mov eax,NULL
ret
.endif
mov esi,hdib ; return ((PDIBSTRUCT) hdib)->pBits
mov eax,[esi+12]
ret

DibBitsPtr endp

; DibSetColor: Obtains entry from the DIB color table
DibGetColor proc hdib:HDIB,index:DWORD, prgb:DWORD ;prgb 是指向RGBQUAD的指针
LOCAL pdib:DWORD
LOCAL hdcMem:HDC
LOCAL iReturn:DWORD

mov eax,hdib
mov pdib,eax
invoke DibIsValid,hdib
.if eax==FALSE
xor eax,eax
ret
.endif
invoke CreateCompatibleDC,NULL
mov hdcMem,eax

mov esi,pdib
mov eax,[esi+8] ;pdib->hBitmap
invoke SelectObject,hdcMem, eax
invoke GetDIBColorTable,hdcMem, index, 1, prgb
mov iReturn,eax
invoke DeleteDC,hdcMem
.if iReturn==TRUE
mov eax,TRUE
.else
mov eax,FALSE
.endif
ret
DibGetColor endp

; DibGetColor: Sets an entry in the DIB color table
DibSetColor proc hdib:HDIB,index:DWORD, prgb:DWORD ;prgb 是指向RGBQUAD的指针
LOCAL pdib:DWORD
LOCAL hdcMem:HDC
LOCAL iReturn:DWORD
mov eax,hdib
mov pdib,eax
invoke DibIsValid,hdib
.if eax==FALSE
xor eax,eax
ret
.endif
invoke CreateCompatibleDC,NULL
mov hdcMem,eax
mov esi,pdib
mov eax,[esi+8]
invoke SelectObject,hdcMem, eax
invoke SetDIBColorTable,hdcMem, index, 1, prgb
mov iReturn,eax
invoke DeleteDC,hdcMem
.if iReturn==TRUE
mov eax,TRUE
.else
mov eax,FALSE
.endif
ret
DibSetColor endp

; DibPixelPtr: Returns a pointer to the pixel at position (x, y)
DibPixelPtr proc hdib:HDIB, x:DWORD, y:DWORD
invoke DibIsAddressable,hdib
.if eax==FALSE
mov eax,NULL
ret
.endif
invoke DibWidth,hdib
push eax
invoke DibHeight,hdib
pop ecx
.if (x < 0 || x >=ecx || y < 0 || y >= eax)
mov eax,NULL
ret
.endif

; return (((PDIBSTRUCT) hdib)->ppRow)[y] + (x * DibBitCount (hdib) >> 3)
invoke DibBitCount,hdib
mov ecx,x
mul ecx
shr eax,3
mov esi,hdib
mov ebx,[esi]
mov ecx,y
shl ecx,2
add ebx,ecx
add eax,ebx
ret
DibPixelPtr endp

; DibGetPixel: Obtains a pixel value at (x, y)
DibGetPixel proc hdib:HDIB, x:DWORD, y:DWORD
LOCAL pPixel:DWORD ;指向BYTE的指针

invoke DibPixelPtr,hdib, x, y
mov pPixel,eax
.if eax==FALSE
xor eax,eax
ret
.endif

invoke DibBitCount,hdib
.if eax==1
invoke DibGetPixel1,hdib,x,y
ret
.elseif eax==4
invoke DibGetPixel4,hdib,x,y
ret
.elseif eax==8
invoke DibGetPixel8,hdib,x,y
ret
.elseif eax==16
invoke DibGetPixel16,hdib,x,y
ret
.elseif eax==24
invoke DibGetPixel24,hdib,x,y
ret
.elseif eax==32

invoke DibGetPixel32,hdib,x,y
ret
.endif
xor eax,eax
ret
DibGetPixel endp

; DibSetPixel: Sets a pixel value at (x, y)
DibSetPixel proc hdib:HDIB, x:DWORD, y:DWORD,dwPixel:DWORD
LOCAL pPixel:DWORD ;指向BYTE的指针
invoke DibPixelPtr,hdib, x, y
mov pPixel,eax
.if eax==FALSE
mov eax,FALSE
ret
.endif

invoke DibBitCount,hdib
.if eax==1
invoke DibSetPixel1,hdib,x,y, dwPixel
.elseif eax==4
invoke DibSetPixel4,hdib,x,y, dwPixel
.elseif eax==8
invoke DibSetPixel8,hdib,x,y, dwPixel
.elseif eax==16
invoke DibSetPixel16,hdib,x,y, dwPixel
.elseif eax==24
invoke DibSetPixel24,hdib,x,y, dwPixel
.elseif eax==32
invoke DibSetPixel32,hdib,x,y, dwPixel
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
DibSetPixel endp

; DibGetPixelColor: Obtains the pixel color at (x, y)
DibGetPixelColor proc hdib:HDIB, x:DWORD, y:DWORD,prgb:DWORD ;RGBQUAD * prgb
LOCAL dwPixel:DWORD
LOCAL iBitCount:DWORD
LOCAL pdib:DWORD

mov eax,hdib
mov pdib,eax
; Get bit count; also use this as a validity check
invoke DibBitCount,hdib
mov iBitCount,eax
.if eax==0
mov eax,FALSE
ret
.endif

; Get the pixel value
invoke DibGetPixel,hdib, x, y
mov dwPixel,eax

; If the bit-count is 8 or less, index the color table
.if (iBitCount <= 8)
invoke DibGetColor,hdib,dwPixel, prgb
ret

; If the bit-count is 24, just use the pixel
.elseif (iBitCount == 24)
mov esi,dwPixel
mov edi,prgb
mov ax,[esi]
mov [edi],ax
mov al,[esi+2]
mov [edi+2],al

xor al,al
mov [esi+3],al

.else
mov esi,pdib
mov eax,[esi+56]

.if (iBitCount == 32 && eax == BI_RGB)

; If the bit-count is 32 and the biCompression field is BI_RGB,just use the pixel
mov ecx,prgb ;*prgb = * (RGBQUAD *) & dwPixel
mov edx,dwPixel
mov [ecx],edx

.else
; Otherwise, use the mask and shift values
; (for best performance, don't use DibMask and DibShift functions)
;prgb->rgbRed = (BYTE)(((pdib->ds.dsBitfields[0] & dwPixel) >> pdib->iRShift[0]) << pdib->iLShift[0])
mov esi,pdib
mov eax,[esi+80]
and eax,dwPixel
mov ecx,[esi+92]
shr eax,cl
mov eax,[esi+104]
shl eax,cl
mov edi,prgb
mov [edi+2],al
;prgb->rgbGreen=(BYTE((pdib->ds.dsBitfields[1] & dwPixel) >> pdib->iRShift[1]) << pdib->iLShift[1])
mov esi,pdib
mov eax,[esi+84]
and eax,dwPixel
mov ecx,[esi+96]
shr eax,cl
mov eax,[esi+108]
shl eax,cl
mov edi,prgb
mov [edi+1],al
;prgb->rgbBlue=(BYTE)(((pdib->ds.dsBitfields[2] & dwPixel) >> pdib->iRShift[2]) << pdib->iLShift[2])
mov esi,pdib
mov eax,[esi+88]
and eax,dwPixel
mov ecx,[esi+100]
shr eax,cl
mov eax,[esi+112]
shl eax,cl
mov edi,prgb
mov [edi],al
.endif
.endif
mov eax,TRUE
ret
DibGetPixelColor endp

; DibSetPixelColor: Sets the pixel color at (x, y)
DibSetPixelColor proc hdib:HDIB, x:DWORD, y:DWORD,prgb:DWORD ;RGBQUAD * prgb
LOCAL dwPixel:DWORD
LOCAL iBitCount:DWORD
LOCAL pdib:DWORD

mov eax,hdib
mov pdib,eax

; Don't do this function for DIBs with color tables
invoke DibBitCount,hdib
mov iBitCount,eax
.if (iBitCount <= 8)
mov eax,FALSE

; The rest is just the opposite of DibGetPixelColor
.elseif (iBitCount == 24)
;* (RGBTRIPLE *) & dwPixel = * (RGBTRIPLE *) prgb
mov esi,dwPixel
mov edi,prgb
mov ax,[esi]
mov [edi],ax
mov al,[esi+2]
mov [edi+2],al
; dwPixel &= 0x00FFFFFF ;
and dwPixel, 00FFFFFFh
.else
mov esi,pdib
mov eax,[esi+56]
.if (iBitCount == 32) && (eax == BI_RGB)
mov esi,prgb ;* (RGBQUAD *) & dwPixel = * prgb ;
mov edi,dwPixel
mov eax,[esi]
mov [edi],eax
.else
mov esi,prgb
mov edi,pdib
; dwPixel = (((DWORD) prgb->rgbRed >> pdib->iLShift[0]) << pdib->iRShift[0])
xor eax,eax
mov al,[esi+2]
mov ecx,[edi+104]
shr eax,cl
mov ecx,[edi+92]
shl eax,cl
mov dwPixel,eax

; dwPixel |= (((DWORD) prgb->rgbGreen >> pdib->iLShift[1]) << pdib->iRShift[1])
xor eax,eax
mov al,[esi+1]
mov ecx,[edi+108]
shr eax,cl
mov ecx,[edi+96]
shl eax,cl
or dwPixel,eax

; dwPixel |= (((DWORD) prgb->rgbBlue >> pdib->iLShift[2]) << pdib->iRShift[2])
xor eax,eax
mov al,[esi]
mov ecx,[edi+112]
shr eax,cl
mov ecx,[edi+100]
shl eax,cl
or dwPixel,eax
.endif
.endif

invoke DibSetPixel,hdib, x, y, dwPixel
mov eax,TRUE
ret
DibSetPixelColor endp

; Calculating shift values from color masks is required by the
; szTxtFromInfo function.
MaskToRShift proc dwMask:DWORD
LOCAL iShift:DWORD
.if (dwMask == 0)
xor eax,eax
ret
.endif

mov iShift,0
@@:
mov eax,dwMask
shr eax,1
mov dwMask,eax

inc iShift
mov eax,dwMask
and eax,1
.if eax==FALSE
jmp @b
.endif

mov eax,iShift
ret
MaskToRShift endp

MaskToLShift proc dwMask:DWORD
LOCAL iShift:DWORD
.if (dwMask == 0)
xor eax,eax
ret
.endif
@@:
mov eax,dwMask
shr eax,1
mov dwMask,eax
mov eax,dwMask
and eax,1
.if eax==FALSE
jmp @b
.endif

mov iShift,0
@@:
mov eax,dwMask
shr eax,1
mov dwMask,eax

inc iShift
mov eax,dwMask
and eax,1
.if eax!=FALSE
jmp @b
.endif

mov eax,8
sub eax,iShift
ret
MaskToLShift endp

; DibCreateFromInfo: All DIB creation functions ultimately call this one.
; This function is responsible for calling CreateDIBSection, allocating
; memory for DIBSTRUCT, and setting up the row pointer.
DibCreateFromInfo proc pbmi:DWORD ;指向 BITMAPINFO 结构体的指针
LOCAL pBits:DWORD ;指向BYTE的指针
LOCAL pdib:DWORD ;指向DIBSTRUCT的指针
LOCAL hBitmap:HBITMAP
LOCAL i, iRowLength, cy, y:DWORD

invoke CreateDIBSection,NULL, pbmi, DIB_RGB_COLORS,addr pBits, NULL, 0
mov hBitmap,eax
.if (hBitmap == NULL)
invoke Beep,1000,100
mov eax,NULL
ret
.endif

invoke GlobalAlloc,GMEM_ZEROINIT,sizeof (DIBSTRUCT)
mov pdib,eax
.if (eax==NULL)
invoke DeleteObject,hBitmap
mov eax,NULL
ret
.endif

mov esi,pdib
mov DWORD ptr [esi+4],20626944h ;'Dib ' pdib->iSignature = HDIB_SIGNATURE
mov eax,hBitmap
mov DWORD ptr [esi+8], eax ;pdib->hBitmap = hBitmap
mov eax,pBits
mov DWORD ptr [esi+12],eax ;pdib->pBits = pBits

mov esi,pdib
add esi,16
invoke GetObject,hBitmap, sizeof (DIBSECTION), esi

; Notice that we can now use the DIB information functions
; defined above.
; If the compression is BI_BITFIELDS, calculate shifts from masks
invoke DibCompression,pdib
.if (eax == BI_BITFIELDS)
mov i,0
@@:
;pdib->iLShift[i] = MaskToLShift (pdib->ds.dsBitfields[i]) ;
mov esi,pdib
mov eax,i
shl eax,2
mov eax,[esi+80+eax]
invoke MaskToLShift,eax
mov esi,pdib
mov eax,i
shl eax,2
mov eax,[esi+112+eax]
;pdib->iRShift[i] = MaskToRShift (pdib->ds.dsBitfields[i]) ;
mov esi,pdib
mov eax,i
shl eax,2
mov eax,[esi+80+eax]
invoke MaskToRShift,eax
mov esi,pdib
mov eax,i
shl eax,2
mov eax,[esi+100+eax]

inc i
mov eax,i
.if eax<3
jmp @b
.endif
; If the compression is BI_RGB, but bit-count is 16 or 32,
; set the bitfields and the masks
invoke Beep,1000,100
.else
invoke DibCompression,pdib
.if eax == BI_RGB
invoke DibBitCount,pdib
.if eax == 16
mov esi,pdib

mov DWORD ptr [esi+80],00007C00h
mov DWORD ptr [esi+84],000003E0h
mov DWORD ptr [esi+88],0000001Fh

mov DWORD ptr [esi+100],10
mov DWORD ptr [esi+104],5
mov DWORD ptr [esi+108],0

mov DWORD ptr [esi+112],3
mov DWORD ptr [esi+116],3
mov DWORD ptr [esi+120],3
.elseif (eax == 24 || eax == 32)
mov esi,pdib
mov DWORD ptr [esi+80],00FF0000h ;pdib->ds.dsBitfields[0] = 0x00FF0000
mov DWORD ptr [esi+84],0000FF00h ;pdib->ds.dsBitfields[1] = 0x0000FF00
mov DWORD ptr [esi+88],000000FFh ;pdib->ds.dsBitfields[2] = 0x000000FF

mov DWORD ptr [esi+100],16
mov DWORD ptr [esi+104],8
mov DWORD ptr [esi+108],0

mov DWORD ptr [esi+112],0
mov DWORD ptr [esi+116],0
mov DWORD ptr [esi+120],0
.endif
.endif
.endif

; Allocate an array of pointers to each row in the DIB
invoke DibHeight,pdib
mov cy,eax
shl eax,2
invoke GlobalAlloc,GMEM_ZEROINIT,eax
mov esi,pdib
mov [esi],eax

.if eax==NULL
invoke GlobalFree,pdib
invoke DeleteObject,hBitmap
mov eax,NULL
ret
.endif

; Initialize them.
invoke DibRowLength,pdib
mov iRowLength,eax

mov esi,pbmi
mov eax,[esi+8] ;pbmi->bmiHeader.biHeight
cmp eax,0
jle LowThanZero
; ie, bottom up
mov y,0
@@:
mov eax,cy ;pdib->ppRow[y] = pBits + (cy - y - 1) * iRowLength
sub eax,y
dec eax
mov ecx,iRowLength
mul ecx
add eax,pBits

mov ebx,y
shl ebx,2
mov esi,pdib
mov esi,[esi]
add esi,ebx
mov [esi],eax

inc y
mov eax,y
.if eax<cy
jmp @b
.endif
jmp JudgeEnd
LowThanZero:
; top down
mov y,0
@@:
mov eax,y ;pdib->ppRow[y] = pBits + y * iRowLength
mov ecx,iRowLength
mul ecx
add eax,pBits

mov ebx,y
shl ebx,2
mov esi,pdib
mov esi,[esi]
add esi,ebx
mov [esi],eax

inc y
mov eax,y
.if eax<cy
jmp @b
.endif
JudgeEnd:
mov eax,pdib
ret
DibCreateFromInfo endp

;DibDelete: Frees all memory for the DIB section
DibDelete proc hdib:HDIB
LOCAL pdib:DWORD
mov eax,hdib
mov pdib,eax

invoke DibIsValid,hdib
.if eax==FALSE
mov eax,FALSE
ret
.endif

mov esi,pdib
mov eax,[esi]
invoke GlobalFree,eax

mov esi,pdib
mov eax,[esi+8]
invoke DeleteObject ,eax

invoke GlobalFree,pdib

mov eax,TRUE
ret
DibDelete endp

; DibCreate: Creates an HDIB from explicit arguments
DibCreate proc cxDC:DWORD, cyDC:DWORD, cBits:DWORD, cColors:DWORD
LOCAL pbmi:DWORD ;指向BITMAPINFO结构体的指针
LOCAL dwInfoSize:DWORD
LOCAL hDib:HDIB
LOCAL cEntries:DWORD

.if (cxDC <= 0 || cyDC <= 0 || ((cBits != 1) && (cBits != 4) && (cBits != 8) && (cBits != 16) && (cBits != 24) && (cBits != 32)))
mov eax,NULL
ret
.endif

.if cColors != 0
mov eax,cColors
mov cEntries,eax
.elseif (cBits <= 8)
mov eax,1
mov ecx,cBits
shl eax,cl
mov cEntries,eax
.endif

;dwInfoSize = sizeof (BITMAPINFOHEADER) + (cEntries - 1) * sizeof (RGBQUAD);
mov eax,cEntries
dec eax
mov ecx,sizeof (RGBQUAD)
mul ecx
add eax,sizeof (BITMAPINFOHEADER)
mov dwInfoSize,eax

invoke GlobalAlloc,GMEM_ZEROINIT,dwInfoSize
mov pbmi,eax
.if eax==NULL
mov eax,NULL
ret
.endif

invoke RtlZeroMemory,pbmi,dwInfoSize ;invoke ZeroMemory,pbmi, dwInfoSize

mov esi,pbmi
mov DWORD ptr [esi],sizeof (BITMAPINFOHEADER)
mov eax,cxDC
mov DWORD ptr [esi+4],eax
mov eax,cyDC
mov DWORD ptr [esi+8],eax
mov WORD ptr [esi+12],1
mov eax,cBits
mov WORD ptr [esi+14],ax
mov eax,BI_RGB
mov [esi+16],eax
xor eax,eax
mov DWORD ptr [esi+20],eax
mov DWORD ptr [esi+24],eax
mov DWORD ptr [esi+28],eax
mov eax,cColors
mov DWORD ptr [esi+32],eax
mov DWORD ptr [esi+36],0
invoke DibCreateFromInfo,pbmi
mov hDib,eax
invoke GlobalFree,pbmi

mov eax,hDib
ret
DibCreate endp

; DibCopyToInfo: Builds BITMAPINFO structure.
; Used by DibCopy and DibCopyToDdb
DibCopyToInfo proc hdib:HDIB
LOCAL pbmi:DWORD ;指向 BITMAPINFO 结构体的指针
LOCAL i, iNumColors:DWORD
LOCAL prgb:DWORD ;指向 RGBQUAD 结构体的指针

invoke DibIsValid,hdib
.if eax==FALSE
mov eax,NULL
ret
.endif

; Allocate the memory
invoke DibInfoSize,hdib
invoke GlobalAlloc,GMEM_ZEROINIT,eax
mov pbmi,eax
.if eax==NULL
mov eax,NULL
ret
.endif
; Copy the information header
invoke DibInfoHeaderPtr,hdib

mov ebx,eax
invoke MemCopy,ebx,pbmi, sizeof (BITMAPINFOHEADER)

; Copy the possible color masks
mov eax,pbmi
add eax,sizeof (BITMAPINFOHEADER)
mov prgb,eax

invoke DibMaskSize,hdib
.if eax!=FALSE
invoke DibMaskPtr,hdib
invoke MemCopy,eax,prgb,12
xor eax,eax
mov esi,pbmi
mov al,[esi]
add eax,12
mov prgb,eax
.endif

; Copy the color table
invoke DibNumColors,hdib
mov iNumColors,eax

mov i,0
@@:
mov eax,prgb
mov ebx,i
shl ebx,2
add eax,ebx
invoke DibGetColor,hdib, i, eax
inc i
mov eax,i
.if eax<iNumColors
jmp @b
.endif

mov eax,pbmi
ret
DibCopyToInfo endp

;DibCopy: Creates a new DIB section from an existing DIB section,
; possibly swapping the DIB width and height.
DibCopy proc hdibSrc:HDIB, fRotate:BOOL
LOCAL pbmi:DWORD ;指向 BITMAPINFO 结构体的指针
LOCAL pBitsSrc, pBitsDst ;指向 BYTE 结构体的指针
LOCAL hdibDst:HDIB

invoke DibIsValid,hdibSrc
.if eax==FALSE
mov eax,NULL
.endif

invoke DibCopyToInfo,hdibSrc
mov pbmi,eax

.if (eax==NULL)
mov eax,NULL
ret
.endif

.if (fRotate!=FALSE)
invoke DibWidth,hdibSrc
mov esi,pbmi
mov [esi+8],eax ;pbmi->bmiHeader.biWidth = DibHeight (hdibSrc)
invoke DibHeight,hdibSrc
mov esi,pbmi
mov [esi+4],eax ;pbmi->bmiHeader.biHeight = DibWidth (hdibSrc)
.endif

invoke DibCreateFromInfo,pbmi
mov hdibDst,eax

invoke GlobalFree,pbmi
.if (hdibDst == NULL)
mov eax,NULL
ret
.endif

; Copy the bits
.if fRotate==FALSE
invoke DibBitsPtr,hdibSrc
mov pBitsSrc,eax

invoke DibBitsPtr,hdibDst
mov pBitsDst,eax
invoke DibBitsSize,hdibSrc
invoke MemCopy,pBitsSrc,pBitsDst, eax
.endif
mov eax, hdibDst
ret
DibCopy endp

;DibCopyToPackedDib is generally used for saving DIBs and for
; transferring DIBs to the clipboard. In the second case, the second
; argument should be set to TRUE so that the memory is allocated
; with the GMEM_SHARE flag.
DibCopyToPackedDib proc hdib:HDIB, fUseGlobal:BOOL
LOCAL pPackedDib:DWORD ;指向 BITMAPINFO 结构体的指针
LOCAL pBits:DWORD ;指向 BYTE 的指针
LOCAL dwDibSize:DWORD
LOCAL hdcMem:HDC
LOCAL hGlobal:HGLOBAL
LOCAL iNumColors:DWORD
LOCAL prgb:DWORD ;指向 RGBQUAD 结构体的指针
LOCAL pdib:DWORD

mov eax,hdib
mov pdib,eax

invoke DibIsValid,hdib
.if eax==FALSE
mov eax,NULL
ret
.endif

; Allocate memory for packed DIB
invoke DibTotalSize,hdib
mov dwDibSize,eax

.if fUseGlobal!=FALSE
invoke GlobalAlloc,GHND or GMEM_SHARE, dwDibSize
mov hGlobal,eax
invoke GlobalLock,hGlobal
mov pPackedDib,eax
.else
invoke GlobalAlloc,GMEM_ZEROINIT,dwDibSize
mov pPackedDib,eax
.endif

.if (pPackedDib == NULL)
mov eax,NULL
ret
.endif

; Copy the information header
mov eax,pdib
add eax,40
invoke MemCopy,eax, pPackedDib, sizeof (BITMAPINFOHEADER)

mov eax,pPackedDib
add eax,sizeof (BITMAPINFOHEADER)
mov prgb,eax ;prgb = (RGBQUAD *) ((BYTE *) pPackedDib + sizeof (BITMAPINFOHEADER))

;Copy the possible color masks
mov esi,pdib
mov eax,[esi+56] ;pdib->ds.dsBmih.biCompression
.if (eax == BI_BITFIELDS)
mov esi,pdib
mov eax,[esi+80]
invoke MemCopy,eax,prgb, 12
mov eax,prgb
add eax,12
mov prgb,eax ;prgb = (RGBQUAD *) ((BYTE *) prgb + 3 * sizeof (DWORD))
.else
; Copy the color table
invoke DibNumColors,hdib
mov iNumColors,eax
.if (eax!=0)
invoke CreateCompatibleDC,NULL
mov hdcMem,eax
mov esi,pdib
mov eax,[esi+8] ;pdib->hBitmap
invoke SelectObject,hdcMem,eax
invoke GetDIBColorTable,hdcMem, 0, iNumColors, prgb
invoke DeleteDC,hdcMem
.endif
;pBits = (BYTE *) (prgb + iNumColors)
mov eax,prgb
mov ebx,iNumColors
shl ebx,2
add eax,ebx
mov pBits,eax

; Copy the bits
invoke DibBitsSize,pdib

mov esi,pdib
mov ebx,[esi+12] ;pdib->pBits
invoke MemCopy, ebx, pBits,eax
;If last argument is TRUE, unlock global memory block and
; cast it to pointer in preparation for return
.if (fUseGlobal!=FALSE)
invoke GlobalUnlock,hGlobal
mov eax,hGlobal
mov pPackedDib,eax ;pPackedDib = (BITMAPINFO *) hGlobal ;
.endif
.endif
mov eax,pPackedDib
ret
DibCopyToPackedDib endp

; DibCopyFromPackedDib is generally used for pasting DIBs from the clipboard
DibCopyFromPackedDib proc pPackedDib:DWORD
LOCAL pBits:DWORD
LOCAL dwInfoSize, dwMaskSize, dwColorSize :DWORD
LOCAL iBitCount:DWORD
LOCAL pdib:DWORD

; Get the size of the information header and do validity check
mov esi,pPackedDib
mov eax,[esi]
mov dwInfoSize,eax

.if (dwInfoSize != sizeof (BITMAPCOREHEADER) && \
dwInfoSize != sizeof (BITMAPINFOHEADER) && \
dwInfoSize != sizeof (BITMAPV4HEADER) && \
dwInfoSize != sizeof (BITMAPV5HEADER))
mov eax,NULL
ret
.endif

; Get the possible size of the color masks
mov esi,pPackedDib
mov eax,[esi+16]
.if (dwInfoSize == sizeof (BITMAPINFOHEADER) && eax == BI_BITFIELDS)
mov dwMaskSize,12 ;dwMaskSize = 3 * sizeof (DWORD) ;
.else
mov dwMaskSize,0 ;dwMaskSize = 0 ;
.endif
; Get the size of the color table
.if (dwInfoSize == sizeof (BITMAPCOREHEADER))
mov esi,pPackedDib
mov eax,[esi+10]
mov iBitCount,eax ;iBitCount = ((BITMAPCOREHEADER *) pPackedDib)->bcBitCount
.if (iBitCount <= 8)
mov eax,1
mov ecx,iBitCount
shl eax,cl
mov ecx,sizeof (RGBTRIPLE)
mul ecx
mov dwColorSize,eax
.else
mov dwColorSize,0
.endif
.else ;all non-OS/2 compatible DIBs
mov esi,pPackedDib
mov eax,[esi+14] ;pPackedDib->bmiHeader.biClrUsed
.if (eax > 0)
mov esi,pPackedDib
mov eax,[esi+32]
mov ecx,sizeof (RGBQUAD)
mul ecx
mov dwColorSize,eax
.else
mov esi,pPackedDib
mov eax,[esi+54]
.if (eax <= 8)
mov eax,1
mov esi,pPackedDib
mov ecx,[esi+14]
shl eax,cl
mov ecx,sizeof (RGBQUAD)
mul ecx
mov dwColorSize,eax
.else
mov dwColorSize,0
.endif
.endif
.endif

; Finally, get the pointer to the bits in the packed DIB
mov eax,pPackedDib
add eax,dwInfoSize
add eax,dwMaskSize
add eax,dwColorSize
mov pBits,eax

; Create the HDIB from the packed-DIB pointer
invoke DibCreateFromInfo,pPackedDib
mov pdib,eax

; Copy the pixel bits
mov esi,pdib
mov eax,[esi+12]
push eax
invoke DibBitsSize,pdib
pop ebx
invoke MemCopy,pBits,ebx,eax
mov eax,pdib
ret
DibCopyFromPackedDib endp

; DibFileLoad: Creates a DIB section from a DIB file
DibFileLoad proc szFileName:DWORD ;(const TCHAR * szFileName)
LOCAL bmfh:BITMAPFILEHEADER
LOCAL pbmi:DWORD ;BITMAPINFO *pbmi
LOCAL bSuccess:BOOL
LOCAL dwInfoSize, dwBitsSize, dwBytesRead :DWORD
LOCAL hFile:HANDLE
LOCAL hDib:HDIB

; Open the file: read access, prohibit write access
invoke CreateFile,szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL
mov hFile,eax
.if hFile == INVALID_HANDLE_VALUE
mov eax,NULL
ret
.endif
; Read in the BITMAPFILEHEADER
invoke ReadFile,hFile,addr bmfh, sizeof (BITMAPFILEHEADER),addr dwBytesRead, NULL
mov bSuccess,eax

mov eax,dwBytesRead
xor ebx,ebx
mov bx,bmfh.bfType
.if (bSuccess==0) || ( eax != sizeof (BITMAPFILEHEADER)) || ( bx != "MB")
invoke Beep,1000,100
invoke CloseHandle,hFile
mov eax,NULL
ret
.endif

; Allocate memory for the information structure & read it in
mov eax, bmfh.bfOffBits
sub eax,sizeof (BITMAPFILEHEADER)
mov dwInfoSize,eax

invoke GlobalAlloc,GMEM_ZEROINIT,dwInfoSize
mov pbmi,eax
.if eax==NULL
invoke CloseHandle,hFile
mov eax,NULL
ret
.endif

invoke ReadFile,hFile, pbmi, dwInfoSize,addr dwBytesRead, NULL
mov bSuccess,eax

mov eax,dwBytesRead
.if (bSuccess==FALSE || ( eax != dwInfoSize))
invoke CloseHandle,hFile
invoke GlobalFree,pbmi
mov eax,NULL
ret
.endif

; Create the DIB
invoke DibCreateFromInfo,pbmi
mov hDib,eax

invoke GlobalFree,pbmi

.if (hDib == NULL)
invoke CloseHandle,hFile
mov eax,NULL
ret
.endif

; Read in the bits
mov eax, bmfh.bfSize
sub eax,bmfh.bfOffBits
mov dwBitsSize,eax

mov esi,hDib
mov ebx,[esi+12] ;trick 当这个地方删除会发生奇怪的事情
invoke ReadFile,hFile,ebx,dwBitsSize, addr dwBytesRead, NULL
mov bSuccess,eax
invoke CloseHandle,hFile

mov eax,dwBytesRead
.if (bSuccess==FALSE || ( eax != dwBitsSize))
invoke Beep,1000,100
invoke DibDelete,hDib
mov eax,NULL
ret
.endif
mov eax,hDib
ret
DibFileLoad endp

; DibFileSave: Saves a DIB section to a file
DibFileSave proc hdib:HDIB, szFileName:DWORD ;const TCHAR * szFileName)
LOCAL bmfh:BITMAPFILEHEADER
LOCAL pbmi:DWORD ;指向 BITMAPINFO 结构体的指针

LOCAL bSuccess:BOOL
LOCAL dwTotalSize, dwBytesWritten:DWORD
LOCAL hFile:HANDLE

invoke CreateFile,szFileName, GENERIC_WRITE, 0, NULL,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL

mov hFile,eax
.if hFile == INVALID_HANDLE_VALUE
mov eax,FALSE
ret
.endif

invoke DibTotalSize,hdib
mov dwTotalSize,eax

;bmfh.bfType = * (WORD *) "BM"
mov bx,"MB"
mov bmfh.bfType,bx

;bmfh.bfSize = sizeof (BITMAPFILEHEADER) + dwTotalSize
mov eax,sizeof (BITMAPFILEHEADER)
add eax,dwTotalSize
mov bmfh.bfSize,eax

xor ax,ax
mov bmfh.bfReserved1,ax ;bmfh.bfReserved1 = 0
mov bmfh.bfReserved2,ax ;bmfh.bfReserved2 = 0

invoke DibBitsSize,hdib
mov ebx,bmfh.bfSize
sub ebx,eax
mov bmfh.bfOffBits,ebx ;bmfh.bfOffBits = bmfh.bfSize - DibBitsSize (hdib)

; Write the BITMAPFILEHEADER
invoke WriteFile,hFile, addr bmfh, sizeof (BITMAPFILEHEADER),addr dwBytesWritten, NULL
mov bSuccess ,eax

.if (bSuccess==FALSE || (dwBytesWritten != sizeof (BITMAPFILEHEADER)))
invoke CloseHandle,hFile
invoke DeleteFile,addr szFileName
mov eax,FALSE
ret
.endif

; Get entire DIB in packed-DIB format
invoke DibCopyToPackedDib,hdib, FALSE
mov pbmi,eax
.if eax==NULL
invoke CloseHandle,hFile
invoke DeleteFile,addr szFileName
mov eax,FALSE
ret
.endif

; Write out the packed DIB
invoke WriteFile,hFile, pbmi, dwTotalSize, addr dwBytesWritten, NULL
mov bSuccess,eax
invoke CloseHandle,hFile
invoke GlobalFree,pbmi
mov eax,dwBytesWritten
.if (bSuccess==FALSE || ( eax != dwTotalSize))
invoke DeleteFile,addr szFileName
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
DibFileSave endp

; DibCopyToDdb: For more efficient screen displays
DibCopyToDdb proc hdib:HDIB, hwnd:HWND, hPalette:HPALETTE
LOCAL pbmi:DWORD ;指向 BITMAPINFO 结构体的指针
LOCAL hBitmap:HBITMAP
LOCAL hdc:HDC
invoke DibIsValid,hdib
.if eax==FALSE
mov eax,NULL
.endif

invoke DibCopyToInfo,hdib
mov pbmi,eax

.if eax==NULL
mov eax,NULL
.endif

invoke GetDC,hwnd
mov hdc,eax

.if hPalette!=FALSE
invoke SelectPalette,hdc, hPalette, FALSE
invoke RealizePalette,hdc
.endif

invoke DibInfoHeaderPtr,hdib
push eax
invoke DibBitsPtr,hdib
pop ebx

invoke CreateDIBitmap,hdc,ebx, CBM_INIT,eax, pbmi, DIB_RGB_COLORS
mov hBitmap,eax
invoke ReleaseDC,hwnd, hdc
invoke GlobalFree,pbmi
mov eax,hBitmap
ret
DibCopyToDdb endp

START:
invoke GetModuleHandle,NULL
invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0

WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND

mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc

mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0

push hInst
pop wndclass.hInstance

invoke LoadIcon,NULL, IDI_APPLICATION
mov wndclass.hIcon,eax

invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax

invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX

lea eax,szAppName
mov wndclass.lpszMenuName,eax
mov wndclass.lpszClassName,eax

mov wndclass.hIconSm,0

invoke RegisterClassEx, ADDR wndclass
.if (eax==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
ret
.endif
invoke CreateWindowEx,NULL,
ADDR szAppName, ;window class name
ADDR szAppName,
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax

invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd

StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:

mov eax,msg.wParam
ret
WinMain endp

; PaletteMenu: Uncheck and check menu item on palette menu
PaletteMenu proc hMenuPM:HMENU,wItemNew:WORD
invoke CheckMenuItem,hMenuPM, wItem, MF_UNCHECKED
mov ax,wItemNew
mov wItem,ax
invoke CheckMenuItem,hMenuPM, wItem, MF_CHECKED
ret
PaletteMenu endp

; DisplayDib: Displays or prints DIB actual size or stretched depending on menu selection
DisplayDib proc hdc:HDC, hBitmap:HBITMAP,x:DWORD, y:DWORD,cxClientDD, cyClientDD,wShowDD:DWORD, fHalftonePaletteDD:BOOL
LOCAL bitmap:BITMAP
LOCAL hdcMem:HDC
LOCAL cxBitmap, cyBitmap, iReturn:DWORD

invoke GetObject,hBitmap, sizeof (BITMAP),addr bitmap

mov eax,bitmap.bmWidth ;cxBitmap = bitmap.bmWidth
mov cxBitmap,eax
mov eax,bitmap.bmHeight ;cyBitmap = bitmap.bmHeight
mov cyBitmap,eax

invoke SaveDC,hdc
.if (fHalftonePaletteDD!=FALSE)
invoke SetStretchBltMode,hdc, HALFTONE
.else
invoke SetStretchBltMode,hdc, COLORONCOLOR
.endif
invoke CreateCompatibleDC,hdc
mov hdcMem ,eax
invoke SelectObject,hdcMem, hBitmap

.if wShowDD==IDM_SHOW_NORMAL
.if (fHalftonePaletteDD!=FALSE)
mov ebx,cxBitmap ;ebx=min (cxClientDD, cxBitmap - x)
sub ebx,x
.if cxClientDD<ebx
mov ebx,cxClientDD
.endif

mov ecx,cyClientDD ;ecx=min (cyClientDD, cyBitmap - y)
sub ecx,y
.if cxClientDD<ecx
mov ecx,cyClientDD
.endif

invoke StretchBlt,hdc, 0, 0, ebx,ecx,hdcMem, x, y, ebx,ecx,SRCCOPY
mov iReturn,eax
.else
mov ebx,cxBitmap ;ebx=min (cxClientDD, cxBitmap - x)
sub ebx,x
.if cxClientDD<ebx
mov ebx,cxClientDD
.endif

mov ecx,cyBitmap ;ecx=min (cyClientDD, cyBitmap - y)
sub ecx,y
.if cxClientDD<ecx
mov ecx,cyClientDD
.endif

invoke BitBlt,hdc,0, 0, ebx,ecx,hdcMem, x, y, SRCCOPY
mov iReturn,eax
.endif
.elseif wShowDD==IDM_SHOW_CENTER
.if (fHalftonePaletteDD!=FALSE)
mov ebx,cxClientDD ;ebx=(cxClientDD - cxBitmap) / 2
sub ebx,cxBitmap
shr ebx,1

mov ecx,cyClientDD ;ecx=(cyClientDD - cyBitmap) / 2
sub ecx,cyBitmap
shr ecx,1

invoke StretchBlt,hdc, ebx,ecx,cxBitmap, cyBitmap,hdcMem, 0, 0, cxBitmap, cyBitmap, SRCCOPY
mov iReturn,eax
.else
mov ebx,cxClientDD ;ebx=(cxClientDD - cxBitmap) / 2
sub ebx,cxBitmap
mov eax,ebx ;这个地方是有符号的,如果只是div会出现问题
cdq
sub eax,edx
mov ebx,eax
sar ebx,1

mov ecx,cyClientDD ;ecx=(cyClientDD - cyBitmap) / 2
sub ecx,cyBitmap
mov eax,ecx
cdq
sub eax,edx
mov ecx,eax
sar ecx,1

invoke BitBlt,hdc,ebx,ecx,cxBitmap, cyBitmap,hdcMem, 0, 0, SRCCOPY
mov iReturn,eax

.endif
.elseif wShowDD==IDM_SHOW_STRETCH
invoke StretchBlt,hdc,0, 0, cxClientDD, cyClientDD, hdcMem,0, 0, cxBitmap, cyBitmap, SRCCOPY
mov iReturn,eax
.elseif wShowDD==IDM_SHOW_ISOSTRETCH
invoke SetMapMode,hdc, MM_ISOTROPIC
invoke SetWindowExtEx,hdc, cxBitmap, cyBitmap, NULL
invoke SetViewportExtEx,hdc, cxClientDD, cyClientDD, NULL
mov eax,cxBitmap
cdq
sub eax,edx
mov ebx,eax
sar ebx,1
push ebx
mov eax,cyBitmap
cdq
sub eax,edx
mov ecx,eax
sar ecx,1
pop ebx
invoke SetWindowOrgEx,hdc, ebx, ecx, NULL

mov eax,cxClientDD
cdq
sub eax,edx
mov ebx,eax
sar ebx,1
push ebx
mov eax,cyClientDD
cdq
sub eax,edx
mov ecx,eax
sar ecx,1
pop ebx
invoke SetViewportOrgEx,hdc,ebx, ecx, NULL
invoke StretchBlt,hdc, 0, 0, cxBitmap, cyBitmap,hdcMem,0, 0, cxBitmap, cyBitmap, SRCCOPY
mov iReturn,eax
.endif
invoke DeleteDC,hdcMem
invoke RestoreDC,hdc, -1
mov eax,iReturn
ret
DisplayDib endp

; DibPalDibTable: Creates a palette from the DIB color table
DibPalDibTable proc hdib:HDIB
LOCAL hPalette:HPALETTE
LOCAL i, iNum:DWORD
LOCAL plp:DWORD ;指向 LOGPALETTE 结构体的指针
LOCAL rgb:RGBQUAD

invoke DibNumColors,hdib
mov iNum,eax
.if ( eax==0)
mov eax,NULL
ret
.endif

;plp = malloc (sizeof (LOGPALETTE) + (iNum - 1) * sizeof (PALETTEENTRY))
mov eax,iNum
dec eax
mov ecx,sizeof (PALETTEENTRY)
mul ecx
add eax,sizeof (LOGPALETTE)
invoke GlobalAlloc,GMEM_ZEROINIT,eax
mov plp,eax

mov WORD ptr [esi],300h ;plp->palVersion = 0x0300
mov eax,iNum
mov WORD ptr [esi+2],ax ;plp->palNumEntries = iNumColors


mov i,0
@@:
invoke DibGetColor,hdib, i,addr rgb
mov esi,plp
mov eax,i
shl eax,2
add esi,eax
mov al,rgb.rgbRed
mov [esi],al
mov al,rgb.rgbGreen
mov [esi+1],al
mov al,rgb.rgbBlue
mov [esi+2],al
mov BYTE ptr [esi+3],0
inc i
mov eax,i
.if eax<iNum
jmp @b
.endif

invoke CreatePalette,plp
mov hPalette,eax
invoke GlobalFree,plp
mov eax,hPalette
ret
DibPalDibTable endp

;Quickie no-bounds-checked pixel gets and sets
;#define DibPixelPtr1(hdib, x, y) (((* (PBYTE **) hdib) [y]) + ((x) >> 3))
DibPixelPtr1 proc hdib, x, y:DWORD ;返回一个指向 BYTE 的指针
mov eax,hdib
mov ecx,[eax]
mov edx,x
shr edx,3
mov eax,y
mov ecx,[ecx+eax*4]
add ecx,edx
mov eax,ecx
ret
DibPixelPtr1 endp

;#define DibPixelPtr4(hdib, x, y) (((* (PBYTE **) hdib) [y]) + ((x) >> 1))
;#define DibPixelPtr8(hdib, x, y) (((* (PBYTE **) hdib) [y]) + (x) )
;#define DibPixelPtr16(hdib, x, y) ((WORD *) (((* (PBYTE **) hdib) [y]) + (x) * 2))
;#define DibPixelPtr24(hdib, x, y) ((RGBTRIPLE *) (((* (PBYTE **) hdib) [y]) + (x) * 3))
;#define DibPixelPtr32(hdib, x, y) ((DWORD *) (((* (PBYTE **) hdib) [y]) + (x) * 4))
;#define DibGetPixel1(hdib, x, y) (0x01 & (* DibPixelPtr1 (hdib, x, y) >> (7 - ((x) & 7))))
DibGetPixel1 proc hdib, x, y:DWORD
invoke DibPixelPtr1,hdib, x, y
xor ebx,ebx
mov bl,[eax]
mov eax,ebx
mov ecx,x
and ecx,7
mov edx,7
sub edx,ecx
mov ecx,edx
shr eax,cl
and eax,1
ret
DibGetPixel1 endp

;#define DibSetPixel1(hdib, x, y, p) ((* DibPixelPtr1 (hdib, x, y) &= ~( 1<< (7 - ((x) & 7)))),
;(* DibPixelPtr1 (hdib, x, y) |= ((p) << (7 - ((x) & 7)))))
DibSetPixel1 proc hdib, x, y, p:DWORD
invoke DibPixelPtr1,hdib, x, y
mov esi,eax
mov ebx,x
and ebx,7
mov ecx,7
sub ecx,ebx
mov ebx,1
shl ebx,cl
not ebx
and BYTE ptr [esi],bl

invoke DibPixelPtr1,hdib, x, y
mov esi,eax
mov ebx,x
and ebx,7
mov ecx,7
sub ecx,ebx
mov ebx,p
shl ebx,cl
or byte ptr [esi],bl

mov eax,esi
ret
DibSetPixel1 endp

DibPixelPtr4 proc hdib, x, y:DWORD ;返回一个指向 BYTE 的指针
mov eax,hdib
mov ecx,[eax]
mov edx,x
shr edx,1
mov eax,y
mov ecx,[ecx+eax*4]
add ecx,edx
mov eax,ecx
ret
DibPixelPtr4 endp

DibGetPixel4 proc hdib, x, y:DWORD
invoke DibPixelPtr4,hdib, x, y
xor ebx,ebx
mov bl,[eax]
mov eax,ebx
mov ebx,x
and ebx,1
.if ebx!=FALSE
mov cl,0
.else
mov cl,4
.endif
shr eax,cl
and eax,0Fh
ret
DibGetPixel4 endp

DibSetPixel4 proc hdib, x, y, p:DWORD
invoke DibPixelPtr4,hdib, x, y
mov esi,eax
mov ebx,x
and ebx,1
.if ebx!=FALSE
mov cl,4
.else
mov cl,0
.endif
mov ebx,0Fh
shl ebx,cl
and BYTE ptr [esi],bl

invoke DibPixelPtr4,hdib, x, y
mov esi,eax
mov ebx,x
and ebx,1
.if ebx!=FALSE
mov cl,0
.else
mov cl,4
.endif
mov ebx,p
shl ebx,cl
or BYTE ptr [esi],bl

mov eax,esi
ret
DibSetPixel4 endp


DibPixelPtr8 proc hdib, x, y:DWORD ;返回一个指向 BYTE 的指针
mov eax,hdib
mov ecx,[eax]
mov edx,x
mov eax,y
mov ecx,[ecx+eax*4]
add ecx,edx
mov eax,ecx
ret
DibPixelPtr8 endp

DibGetPixel8 proc hdib, x, y:DWORD
invoke DibPixelPtr8,hdib, x, y
xor ebx,ebx
mov bl,[eax]
mov eax,ebx
ret
DibGetPixel8 endp

DibSetPixel8 proc hdib, x, y, p:DWORD
invoke DibPixelPtr8,hdib, x, y
mov esi,eax
mov ebx,p
mov BYTE ptr [esi],bl

mov eax,esi
ret
DibSetPixel8 endp

DibPixelPtr16 proc hdib, x, y:DWORD ;返回一个指向 WORD 的指针
mov eax,hdib
mov ecx,[eax]
mov edx,x
add edx,x ;x*2
mov eax,y
mov ecx,[ecx+eax*4]
add ecx,edx
mov eax,ecx
ret
DibPixelPtr16 endp

DibGetPixel16 proc hdib, x, y:DWORD
invoke DibPixelPtr16,hdib, x, y
xor ebx,ebx
mov bx,[eax]
mov eax,ebx
ret
DibGetPixel16 endp

DibSetPixel16 proc hdib, x, y, p:DWORD
invoke DibPixelPtr16,hdib, x, y
mov esi,eax
mov ebx,p
mov WORD ptr [esi],bx
mov eax,esi
ret
DibSetPixel16 endp

DibPixelPtr24 proc hdib, x, y:DWORD ;返回一个指向 RGBTRIPLE 的指针
mov eax,hdib
mov ecx,[eax]
mov edx,x
add edx,x
add edx,x ;x*3
mov eax,y
mov ecx,[ecx+eax*4]
add ecx,edx
mov eax,ecx
ret
DibPixelPtr24 endp

DibGetPixel24 proc hdib, x, y:DWORD
invoke DibPixelPtr24,hdib, x, y
xor ebx,ebx
mov bl,[eax+2]
shl ebx,8
mov bl,[eax+1]
shl ebx,8
mov bl,[eax]

mov eax,ebx
ret
DibGetPixel24 endp

DibSetPixel24 proc hdib, x, y, p:DWORD
invoke DibPixelPtr24,hdib, x, y
mov esi,eax
mov ebx,p
mov [esi],bl
shr ebx,8
mov [esi+1],bl
shr ebx,8
mov [esi+2],bl

mov eax,esi
ret
DibSetPixel24 endp

DibPixelPtr32 proc hdib, x, y:DWORD ;返回一个指向 RGBTRIPLE 的指针
mov eax,hdib
mov ecx,[eax]
mov edx,x
add edx,x
add edx,edx ;x*4
mov eax,y
mov ecx,[ecx+eax*4]
add ecx,edx
mov eax,ecx
ret
DibPixelPtr32 endp

DibGetPixel32 proc hdib, x, y:DWORD
invoke DibPixelPtr32,hdib, x, y
mov eax,[eax]
ret
DibGetPixel32 endp

DibSetPixel32 proc hdib, x, y, p:DWORD
invoke DibPixelPtr32,hdib, x, y
mov ebx,p
mov [eax],ebx

mov eax,esi
ret
DibSetPixel32 endp

; DibRotateRight: Calls optimized DibSetPixelx and DibGetPixelx
DibRotateRight proc hdibSrc:HDIB
LOCAL hdibDst:HDIB
LOCAL cxDR, cy, x, y:DWORD

invoke DibIsAddressable,hdibSrc
.if eax==FALSE
mov eax,NULL
ret
.endif

invoke DibCopy,hdibSrc, TRUE
mov hdibDst,eax
.if eax==NULL
mov eax,NULL
ret
.endif

invoke DibWidth,hdibSrc
mov cxDR,eax

invoke DibHeight,hdibSrc
mov cy,eax

invoke DibBitCount,hdibSrc
.if eax==1
mov x,0
LoopX1:
mov y,0
LoopY1:
invoke DibGetPixel1,hdibSrc, x, y
mov ebx,cy
sub ebx,y
dec ebx
invoke DibSetPixel1,hdibDst, ebx, x,eax
inc y
mov eax,y
.if eax<cy
jmp LoopY1
.endif

inc x
mov eax,x
.if eax<cxDR
jmp LoopX1
.endif
.elseif eax==4
mov x,0
LoopX4:
mov y,0
LoopY4:
invoke DibGetPixel4,hdibSrc, x, y
mov ebx,cy
sub ebx,y
dec ebx
invoke DibSetPixel4,hdibDst, ebx, x,eax
inc y
mov eax,y
.if eax<cy
jmp LoopY4
.endif

inc x
mov eax,x
.if eax<cxDR
jmp LoopX4
.endif
.elseif eax==8
mov x,0
LoopX8:
mov y,0
LoopY8:
invoke DibGetPixel8,hdibSrc, x, y
mov ebx,cy
sub ebx,y
dec ebx
invoke DibSetPixel8,hdibDst, ebx, x,eax

inc y
mov eax,y
.if eax<cy
jmp LoopY8
.endif

inc x
mov eax,x
.if eax<cxDR
jmp LoopX8
.endif
.elseif eax==16
mov x,0
LoopX16:
mov y,0
LoopY16:
invoke DibGetPixel16,hdibSrc, x, y
mov ebx,cy
sub ebx,y
dec ebx
invoke DibSetPixel16,hdibDst, ebx, x,eax
inc y
mov eax,y
.if eax<cy
jmp LoopY8
.endif

inc x
mov eax,x
.if eax<cxDR
jmp LoopX8
.endif

.elseif eax==24
mov x,0
LoopX24:
mov y,0
LoopY24:
invoke DibGetPixel24,hdibSrc, x, y
mov ebx,cy
sub ebx,y
dec ebx
invoke DibSetPixel24,hdibDst, ebx, x,eax
inc y
mov eax,y
.if eax<cy
jmp LoopY24
.endif

inc x
mov eax,x
.if eax<cxDR
jmp LoopX24
.endif
.elseif eax==32
mov x,0
LoopX32:
mov y,0
LoopY32:
invoke DibGetPixel32,hdibSrc, x, y
mov ebx,cy
sub ebx,y
dec ebx
invoke DibSetPixel32,hdibDst, ebx, x,eax
inc y
mov eax,y
.if eax<cy
jmp LoopY32
.endif

inc x
mov eax,x
.if eax<cxDR
jmp LoopX32
.endif
.endif

mov eax,hdibDst
ret
DibRotateRight endp

; DibFlipHorizontal: Calls non-optimized DibSetPixel and DibGetPixel
DibFlipHorizontal proc hdibSrc:HDIB
LOCAL hdibDst:HDIB
LOCAL cxDF, cy, x, y:DWORD

invoke DibIsAddressable,hdibSrc
.if eax==FALSE
mov eax,NULL
.endif

invoke DibCopy,hdibSrc, FALSE
mov hdibDst,eax
.if (eax==NULL)
mov eax,NULL
ret
.endif

invoke DibWidth,hdibSrc
mov cxDF,eax

invoke DibHeight,hdibSrc
mov cy,eax

mov x,0
LoopXDF:
mov y,0
LoopYDF:
invoke DibGetPixel,hdibSrc, x, y
mov ebx,cy
sub ebx,y
dec ebx
invoke DibSetPixel,hdibDst,x, ebx, eax
inc y
mov eax,y
.if eax<cy
jmp LoopYDF
.endif

inc x
mov eax,x
.if eax<cxDF
jmp LoopXDF
.endif

mov eax,hdibDst
ret
DibFlipHorizontal endp

; used in palette optimization routines
PACK_RGB proc R,G,B,iRes:DWORD
mov eax,R
mov ecx,iRes
mov ebx,G
shl ebx,cl
add eax,ebx
add cl,cl
mov ebx,B
shl ebx,cl
add eax,ebx
ret
PACK_RGB endp

; FindAverageColor: In a box
FindAverageColor proc piCount:DWORD, mmFC:DWORD,iRes:DWORD, prgb:DWORD ;mmFC 是指向 MINMAX 结构体的指针
LOCAL R, G, B, iR, iG, iB, iTotal, iCount:DWORD
; Initialize some variables

xor eax,eax
mov iB,eax
mov iG,eax
mov iR,eax
mov iTotal,eax ;iTotal = iR = iG = iB = 0 ;

;Loop through all colors in the box
mov R,0
LoopR1:
mov G,0
LoopG1:
mov B,0
LoopB1:
;Get the number of pixels of that color
invoke PACK_RGB,R, G, B, iRes
mov iCount,eax

;Weight the pixel count by the color value
mov eax,iCount
mov ecx,R
mul ecx
add eax,iR
mov iR,eax

mov eax,iCount
mov ecx,G
mul ecx
add eax,iG
mov iG,eax

mov eax,iCount
mov ecx,B
mul ecx
add eax,iB
mov iB,eax

mov eax,iCount
add eax,iTotal
mov iTotal,eax
inc B
mov eax,B
mov esi,mmFC
mov ebx,[esi+20]
.if eax<ebx
jmp LoopB1
.endif
inc G
mov eax,G
mov esi,mmFC
mov ebx,[esi+12]
.if eax<ebx
jmp LoopG1
.endif
inc R
mov eax,R
mov esi,mmFC
mov ebx,[esi+4]
.if eax<ebx
jmp LoopR1
.endif

; Find the average color
mov esi,prgb

xor edx,edx
mov eax,iR
mov ecx,iTotal
div ecx
mov ecx,8
sub ecx,iRes
shl eax,cl
mov [esi+3],al ;prgb->rgbRed = (BYTE) ((iR / iTotal) << (8 - iRes)) ;

xor edx,edx
mov eax,iG
mov ecx,iTotal
div ecx
mov ecx,8
sub ecx,iRes
shl eax,cl
mov [esi+2],al ;prgb->rgbGreen = (BYTE) ((iG / iTotal) << (8 - iRes)) ;

xor edx,edx
mov eax,iB
mov ecx,iTotal
div ecx
mov ecx,8
sub ecx,iRes
shl eax,cl
mov [esi ],al ;prgb->rgbBlue = (BYTE) ((iB / iTotal) << (8 - iRes)) ;

; Return the total number of pixels in the box
mov eax,iTotal
ret
FindAverageColor endp

; CutBox: Divide a box in two
CutBox proc piCount:DWORD, iBoxCount:DWORD, mmCB:DWORD,iRes:DWORD, iLevel:DWORD, pboxes:DWORD, piEntry:DWORD ;mmCB 是指向 MINMAX 结构体的指针
LOCAL iCount, R, G, B:DWORD
LOCAL mmNew:MINMAX

; If the box is empty, return
.if (iBoxCount == 0)
ret
.endif

;If the nesting level is 8, or the box is one pixel, we're ready
; to find the average color in the box and save it along with
; the number of pixels of that color
mov esi,mmCB
mov eax,[esi]
mov ebx,[esi+8]
mov ecx,[esi+16]
.if (iLevel == 8 || ( eax == [esi+4] && ebx == [esi+12] && ecx == [esi+20]))
mov eax,piEntry
mov eax,[eax]
mov ebx,pboxes
add ebx,4
lea eax,[ebx+eax*4] ;&pboxes[*piEntry].rgbBoxAv
invoke FindAverageColor,piCount, mmCB, iRes, eax
push eax
mov eax,piEntry
mov eax,[eax]
mov ebx,pboxes
pop ecx
mov [ebx+eax*4],ecx ;pboxes[*piEntry].iBoxCount

add piEntry,4 ;(*piEntry) ++ ;
; Otherwise, if blue is the largest side, split it
jmp CutBoxEnd
.endif

;Otherwise, if blue is the largest side, split it
mov esi,mmCB
mov eax,[esi+20]
sub eax,[esi+16]
mov ebx,[esi+4]
sub ebx,[esi]
mov ecx,[esi+12]
sub ecx,[esi+8]
.if ((eax > ebx) && (eax > ecx))
;Initialize a counter and loop through the blue side
mov iCount,0
mov B,0
LoopB2:
mov G,0
LoopG2:
mov R,0
LoopR2:

;Get the number of pixels of that color
invoke PACK_RGB,R, G, B, iRes
add eax,iCount
inc R
mov eax,R
mov esi,mmCB
mov ebx,[esi+4]
.if eax<ebx
jmp LoopR2
.endif
inc G
mov eax,G
mov esi,mmCB
mov ebx,[esi+12]
.if eax<ebx
jmp LoopG2
.endif

;If it's more than half the box count, we're there
mov eax,iBoxCount
shr eax,1
.if (iCount >= eax)
jmp @f
.endif

;If the next blue value will be the max, we're there
mov esi,mmCB
mov eax,[esi+20]
dec eax
.if ( B == eax)
jmp @f
.endif
inc B
mov eax,B
mov esi,mmCB
mov ebx,[esi+20]
.if eax<ebx
jmp LoopB2
.endif
@@:
; Cut the two split boxes.
; The second argument to CutBox is the new box count.
; The third argument is the new min and max values.
mov esi,mmCB ;mmNew = mm
mov eax,[esi]
mov mmNew.Rmin,eax
mov eax,[esi+4]
mov mmNew.Rmax,eax
mov eax,[esi+8]
mov mmNew.Gmin,eax
mov eax,[esi+12]
mov mmNew.Gmax,eax
mov eax,[esi+16]
mov mmNew.Bmin,eax
mov eax,[esi+20]
mov mmNew.Bmax,eax

;mmNew.Bmin = mm.Bmin ;
mov eax,B
mov mmNew.Bmax,eax ;mmNew.Bmax = B ;

mov eax,iLevel
inc eax
invoke CutBox,piCount, iCount,addr mmNew, iRes, eax,pboxes, piEntry
jmp CutBoxEnd
.endif

; Otherwise, if red is the largest side, split it (just like blue)
mov esi,mmCB
mov ebx,[esi+4]
sub ebx,[esi]
mov ecx,[esi+12]
sub ecx,[esi+8]
.if (ebx>ecx)
mov iCount,0
mov esi,mmCB
mov eax,[esi]
mov R,eax
LoopR3:
mov esi,mmCB
mov eax,[esi+16]
mov B,eax
LoopB3:
mov esi,mmCB
mov eax,[esi+8]
mov G,eax
LoopG3:

;Get the number of pixels of that color
invoke PACK_RGB,R, G, B, iRes
add eax,iCount
inc G
mov eax,G
mov esi,mmCB
mov ebx,[esi+4]
.if eax<ebx
jmp LoopG2
.endif
inc B
mov eax,B
mov esi,mmCB
mov ebx,[esi+12]
.if eax<ebx
jmp LoopB2
.endif

mov eax,iBoxCount
shr eax,1
.if (iCount >= eax)
jmp @f
.endif

mov esi,mmCB
mov eax,[esi+4]
dec eax
.if ( R == eax)
jmp @f
.endif
@@:
mov esi,mmCB ;mmNew = mm
mov eax,[esi]
mov mmNew.Rmin,eax
mov eax,[esi+4]
mov mmNew.Rmax,eax
mov eax,[esi+8]
mov mmNew.Gmin,eax
mov eax,[esi+12]
mov mmNew.Gmax,eax
mov eax,[esi+16]
mov mmNew.Bmin,eax
mov eax,[esi+20]
mov mmNew.Bmax,eax

;mmNew.Rmax = R
mov eax,R
mov mmNew.Rmax,eax

mov eax,iLevel
inc eax
invoke CutBox,piCount, iCount,addr mmNew, iRes, eax,pboxes, piEntry

mov eax,R
inc eax
mov mmNew.Rmin,eax

mov esi,mmCB
mov eax,[esi+4]
mov mmNew.Rmax,eax ;mmNew.Rmax = mm.Rmax ;
mov ecx,iBoxCount
sub ecx,iCount
mov ebx,iLevel
inc ebx
invoke CutBox,piCount, ecx,addr mmNew, iRes, ebx,pboxes, piEntry
jmp CutBoxEnd
.endif

; Otherwise, split along the green size
mov iCount,0

mov esi,mmCB
mov eax,[esi+8]
mov G,eax
LoopG4:
mov esi,mmCB
mov eax,[esi+16]
mov B,eax
LoopB4:
mov esi,mmCB
mov eax,[esi]
mov R,eax
LoopR4:
;Get the number of pixels of that color
invoke PACK_RGB,R, G, B, iRes
add eax,iCount

inc R
mov eax,R
mov esi,mmCB
mov ebx,[esi+4]
.if eax<ebx
jmp LoopG4
.endif
inc B
mov eax,B
mov esi,mmCB
mov ebx,[esi+12]
.if eax<ebx
jmp LoopB4
.endif

mov eax,iBoxCount
shr eax,1
.if (iCount >= eax)
jmp @f
.endif

mov esi,mmCB
mov eax,[esi+12]
dec eax
.if ( G == eax)
jmp @f
.endif
@@:
mov esi,mmCB ;mmNew = mm
mov eax,[esi]
mov mmNew.Rmin,eax
mov eax,[esi+4]
mov mmNew.Rmax,eax
mov eax,[esi+8]
mov mmNew.Gmin,eax
mov eax,[esi+12]
mov mmNew.Gmax,eax
mov eax,[esi+16]
mov mmNew.Bmin,eax
mov eax,[esi+20]
mov mmNew.Bmax,eax

;mmNew.Gmax = G
mov eax,G
mov mmNew.Gmax,eax

mov ebx,iBoxCount
sub ebx,iCount
mov ecx,iLevel
inc ecx
invoke CutBox,piCount, ebx,addr mmNew, iRes, ecx,pboxes, piEntry

mov eax,G
inc eax
mov mmNew.Gmin,eax

mov esi,mmCB
mov eax,[esi+12]
mov mmNew.Gmax,eax
mov ecx,iBoxCount
sub ecx,iCount
mov ebx,iLevel
inc ebx
invoke CutBox,piCount, ecx,addr mmNew, iRes, ebx,pboxes, piEntry
jmp CutBoxEnd
CutBoxEnd:
ret
CutBox endp

; DibPalMedianCut: Creates palette based on median cut algorithm
DibPalMedianCut proc hdib:HDIB,iRes:DWORD
LOCAL boxes [256]:BOXES
LOCAL hPalette:HPALETTE
LOCAL i, iArraySize, iCount, R, G, B, iTotCount, iDim, iEntry:DWORD
LOCAL piCount:DWORD ;指向 DWORD 的指针
LOCAL plp:DWORD ;指向 LOGPALETTE 结构体的指针
LOCAL mmDP:MINMAX
mov iEntry,0
; Validity checks
invoke DibBitCount,hdib
.if ( eax< 16)
mov eax,NULL
ret
.endif
.if (iRes < 3 || iRes > 8)
mov eax,NULL
ret
.endif
; Accumulate counts of pixel colors
mov eax,iRes
mov ecx,3
mul ecx
mov ebx,1
mov cl,al
shl ebx,cl
mov iArraySize,ebx
shl ebx,2 ;ebx=iArraySize*sizeof (int)

invoke GlobalAlloc,GMEM_ZEROINIT,ebx
mov piCount,eax
.if (eax==NULL)
mov eax,NULL
ret
.endif
;invoke AccumColorCounts,hdib, piCount, iRes
; Find the dimensions of the total box
mov eax,1
mov ecx,iRes
shl eax,cl
mov iDim ,eax
mov eax,iDim
mov mmDP.Bmin,eax
mov mmDP.Gmin,eax
mov mmDP.Rmin,eax

xor eax,eax
mov mmDP.Rmax,eax
mov mmDP.Gmax,eax
mov mmDP.Bmax,eax

mov iTotCount,0

mov R,0
LoopR:
mov G,0
LoopG:
mov B,0
LoopB:
invoke PACK_RGB,R, G, B, iRes
mov esi,piCount
shl eax,2
add esi,eax
mov eax,[esi]
mov iCount,eax
.if ( eax > 0)
mov eax,iCount
add eax,iTotCount
mov iTotCount,eax
mov eax,R
.if (eax < mmDP.Rmin)
mov mmDP.Rmin, eax
.endif
mov eax,G
.if (eax < mmDP.Gmin)
mov mmDP.Gmin, eax
.endif
mov eax,B
.if (eax < mmDP.Bmin)
mov mmDP.Bmin, eax
.endif
mov eax,R
.if (eax > mmDP.Rmax)
mov mmDP.Rmax, eax
.endif
mov eax,G
.if (eax > mmDP.Gmax)
mov mmDP.Gmax, eax
.endif
mov eax,B
.if (eax > mmDP.Bmax)
mov mmDP.Bmax, eax
.endif
.endif
inc B
mov eax,B
.if eax<iDim
jmp LoopB
.endif
inc G
mov eax,G
.if eax<iDim
jmp LoopG
.endif
inc R
mov eax,R
.if eax<iDim
jmp LoopR
.endif

;Cut the first box (iterative function).
; On return, the boxes structure will have up to 256 RGB values,
; one for each of the boxes, and the number of pixels in
; each box.
;The iEntry value will indicate the number of non-empty boxes.
invoke CutBox,piCount, iTotCount,addr mmDP, iRes, 0,addr boxes,addr iEntry
invoke GlobalFree,piCount
; Sort the RGB table by the number of pixels for each color
;invoke qsort,boxes, iEntry, sizeof (BOXES), Compare

mov eax,iEntry
dec eax
mov ecx,sizeof (PALETTEENTRY)
mul ecx
add eax,sizeof (LOGPALETTE)
invoke GlobalAlloc,GMEM_ZEROINIT,eax
mov plp ,eax

.if (plp == NULL)
mov eax,NULL
ret
.endif

mov esi,plp
mov WORD ptr [esi],300h ;plp->palVersion = 0x0300
mov eax,iEntry
mov WORD ptr [esi+2],ax ;plp->palNumEntries = iEntry

lea esi,boxes
mov edi,plp
mov i,0
@@:
add esi,4
add edi,4
mov al,[esi+2]
mov [edi+2],al ;plp->palPalEntry[i].peRed = boxes[i].rgbBoxAv.rgbRed
mov al,[esi+1]
mov [edi+1],al ;plp->palPalEntry[i].peGreen= boxes[i].rgbBoxAv.rgbGreen
mov al,[esi]
mov [edi],al ;plp->palPalEntry[i].peBlue = boxes[i].rgbBoxAv.rgbBlue
xor al,al
mov [edi],al ;plp->palPalEntry[i].peFlags= 0
add esi,4
add edi,4
inc i
mov eax,i
.if eax<iEntry
jmp @b
.endif

invoke CreatePalette,plp
mov hPalette,eax
invoke GlobalFree,plp
mov eax, hPalette
ret
DibPalMedianCut endp

; DibPalUniformGrays: Creates a palette of iNum grays, uniformly spaced
DibPalUniformGrays proc iNum:DWORD
LOCAL hPalette:HPALETTE
LOCAL i:DWORD
LOCAL plp:DWORD ;指向 LOGPALETTE 结构体的指针

mov eax,iNum
dec eax
mov ecx,sizeof (PALETTEENTRY)
mul ecx
add eax,sizeof (LOGPALETTE)
invoke GlobalAlloc,GMEM_ZEROINIT,eax ;plp = malloc (sizeof (LOGPALETTE) + (iNum - 1) * sizeof (PALETTEENTRY)) ;
mov plp,eax

mov esi,eax
mov WORD ptr [esi],300h ;plp->palVersion = 0x0300
mov eax,iNum
mov WORD ptr [esi+2],ax ;plp->palNumEntries = iNumColors

mov i,0
@@:

mov esi,plp
add esi,4
mov eax,i
shl eax,2
add esi,eax

mov eax,i
shl eax,8
sub eax,i ;i * 255
xor edx,edx
mov ecx,iNum
dec ecx
div ecx

mov [esi],al
mov [esi+1],al
mov [esi+2],al
mov BYTE ptr [esi+3],0
inc i
mov eax,i
.if eax<iNum
jmp @b
.endif

invoke CreatePalette,plp
mov hPalette,eax

invoke GlobalFree,plp
mov eax,hPalette
mov eax, hPalette
ret
DibPalUniformGrays endp

; DibPalVga: Creates a palette based on standard 16 VGA colors
DibPalVga proc
LOCAL hPalette:HPALETTE
LOCAL i:DWORD
LOCAL plp:DWORD ;指向 LOGPALETTE 结构体的指针

mov eax,sizeof (PALETTEENTRY)
mov ecx,15
mul ecx
add eax,sizeof (LOGPALETTE)

invoke GlobalAlloc,GMEM_ZEROINIT,eax ;plp = malloc (sizeof (LOGPALETTE) + 15 * sizeof (PALETTEENTRY))
mov plp,eax
mov edi,eax
mov WORD ptr [edi],300h ;plp->palVersion = 0x0300
mov eax,16
mov WORD ptr [edi+2],ax ;plp->palNumEntries = 16


lea esi,rgbVG
mov edi,plp
add edi,4
mov i,0
@@:
mov al,[esi]
mov [edi],al
mov al,[esi+1]
mov [edi+1],al
mov al,[esi+2]
mov [edi+2],al
mov BYTE ptr [edi+3],0
add esi,4
add edi,4
inc i

mov eax,i
.if eax<16
jmp @b
.endif

invoke CreatePalette,plp
mov hPalette,eax

invoke GlobalFree,plp

mov eax,hPalette
ret
DibPalVga endp


DibConvert proc hdibSrc:HDIB, iBitCountDst:DWORD
LOCAL hdibDst:HDIB
LOCAL hPalette:HPALETTE
LOCAL i, x, y, cxDC, cy, iBitCountSrc, cColors:DWORD
LOCAL pe:PALETTEENTRY
LOCAL rgb:RGBQUAD
LOCAL wNumEntries:WORD

invoke DibWidth,hdibSrc
mov cxDC,eax

invoke DibHeight,hdibSrc
mov cy,eax

invoke DibBitCount,hdibSrc
mov iBitCountSrc,eax

.if (eax == iBitCountDst)
mov eax,NULL
ret
.endif

; DIB with color table to DIB with larger color table:
.if ((eax < iBitCountDst) && (iBitCountDst <= 8))
invoke DibNumColors,hdibSrc
mov cColors,eax

invoke DibCreate,cxDC, cy, iBitCountDst, cColors
mov hdibDst,eax

mov i,0
@@:
invoke DibGetColor,hdibSrc, i,addr rgb
invoke DibSetColor,hdibDst, i,addr rgb
inc i
mov eax,i
.if eax<cColors
jmp @B
.endif

mov x,0
LoopX1:
mov y,0
LoopY1:
invoke DibGetPixel,hdibSrc, x, y
invoke DibSetPixel,hdibDst, x, y,eax
inc y
mov eax,y
.if eax<cy
jmp LoopY1
.endif
inc x
mov eax,x
.if eax<cxDC
jmp LoopX1
.endif

; Any DIB to DIB with no color table
.elseif (iBitCountDst >= 16)
invoke DibCreate,cxDC, cy, iBitCountDst, 0
mov hdibDst,eax
mov x,0
LoopX2:
mov y,0
LoopY2:
invoke DibGetPixelColor,hdibSrc, x, y,addr rgb
invoke DibSetPixelColor,hdibDst, x, y,addr rgb
inc y
mov eax,y
.if eax<cy
jmp LoopY2
.endif
inc x
mov eax,x
.if eax<cxDC
jmp LoopX2
.endif
; DIB with no color table to 8-bit DIB
.elseif (iBitCountSrc >= 16 && iBitCountDst == 8)
;暂未实现,它所引用的也未经过调试! invoke DibPalMedianCut,hdibSrc, 6
mov hPalette,eax
invoke GetObject,hPalette, sizeof (WORD),addr wNumEntries
invoke DibCreate,cxDC, cy, 8, wNumEntries
mov hdibDst,eax

mov i,0
@@: invoke GetPaletteEntries,hPalette, i, 1,addr pe
mov al,pe.peRed
mov rgb.rgbRed,al
mov al,pe.peGreen
mov rgb.rgbGreen,al
mov al,pe.peBlue
mov rgb.rgbBlue,al
xor al,al
mov rgb.rgbReserved,al
invoke DibSetColor,hdibDst, i ,addr rgb
inc i
mov eax,i
.if ax<wNumEntries
jmp @B
.endif

mov x,0
LoopX3:
mov y,0
LoopY3:
invoke DibGetPixelColor,hdibSrc, x, y,addr rgb
xor eax,eax
mov al,rgb.rgbRed
shl eax,8
mov al,rgb.rgbGreen
shl eax,8
mov al,rgb.rgbBlue
invoke GetNearestPaletteIndex,hPalette,eax
invoke DibSetPixel,hdibDst, x, y, eax
inc y
mov eax,y
.if eax<cy
jmp LoopY3
.endif
inc x
mov eax,x
.if eax<cxDC
jmp LoopX3
.endif
invoke DeleteObject,hPalette
; Any DIB to monochrome DIB
.elseif (iBitCountDst == 1)
invoke DibCreate,cxDC, cy, 1, 0
mov hdibDst,eax

invoke DibPalUniformGrays,2
mov hPalette ,eax

mov i,0
@@:
invoke GetPaletteEntries,hPalette,i, 1,addr pe
mov al,pe.peRed
mov rgb.rgbRed,al
mov al,pe.peGreen
mov rgb.rgbGreen,al
mov al,pe.peBlue
mov rgb.rgbBlue,al
xor al,al
mov rgb.rgbReserved,al
invoke DibSetColor,hdibDst, i,addr rgb
inc i
mov eax,i
.if eax<2
jmp @B
.endif

mov x,0
LoopX4:
mov y,0
LoopY4:

invoke DibGetPixelColor,hdibSrc, x, y,addr rgb
xor eax,eax
mov al,rgb.rgbRed
shl eax,8
mov al,rgb.rgbGreen
shl eax,8
mov al,rgb.rgbBlue
invoke GetNearestPaletteIndex,hPalette,eax
mov eax,0FFFFFFh
invoke DibSetPixel,hdibDst, x, y, eax
inc y
mov eax,y
.if eax<cy
jmp LoopY4
.endif
inc x
mov eax,x
.if eax<cxDC
jmp LoopX4
.endif

invoke DeleteObject,hPalette
; All non-monochrome DIBs to 4-bit DIB
.elseif (iBitCountSrc >= 8 && iBitCountDst == 4)

invoke DibCreate,cxDC, cy, 4, 0
mov hdibDst,eax

invoke DibPalVga
mov hPalette ,eax

mov i,0
@@:
invoke GetPaletteEntries,hPalette, i, 1,addr pe
mov al,pe.peRed
mov rgb.rgbRed,al
mov al,pe.peGreen
mov rgb.rgbGreen,al
mov al,pe.peBlue
mov rgb.rgbBlue,al
xor al,al
mov rgb.rgbReserved,al

invoke DibSetColor,hdibDst, i,addr rgb
inc i
mov eax,i
.if eax<16
jmp @B
.endif

mov x,0
LoopX5:
mov y,0
LoopY5:
invoke DibGetPixelColor,hdibSrc, x, y,addr rgb

xor eax,eax
mov al,rgb.rgbBlue ;如果这个地方搞反了,颜色上会不正
shl eax,8
mov al,rgb.rgbGreen
shl eax,8
mov al,rgb.rgbRed
invoke GetNearestPaletteIndex,hPalette,eax
invoke DibSetPixel,hdibDst, x, y, eax
inc y
mov eax,y
.if eax<cy
jmp LoopY5
.endif
inc x
mov eax,x
.if eax<cxDC
jmp LoopX5
.endif

invoke DeleteObject,hPalette
; Should not be necessary
.else
mov eax,NULL
mov hdibDst,eax
.endif

mov eax,hdibDst
ret
DibConvert endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
LOCAL fSuccess:BOOL
LOCAL pGlobal:DWORD ;指向BYTE的指针
LOCAL hdc,hdcPrn:HDC
LOCAL hGlobal:HGLOBAL
LOCAL hdibNew:HDIB
LOCAL iEnable, cxPage, cyPage, iConvert:DWORD
LOCAL ps :PAINTSTRUCT
LOCAL siWP:SCROLLINFO
LOCAL szBuffer [256]:TCHAR

.if uMsg == WM_CREATE
; Save the menu handle in a static variable
invoke GetMenu,hwnd
mov hMenu,eax

; Initialize the OPENFILENAME structure for the File Open and File Save dialog boxes.
mov eax,sizeof (OPENFILENAME)
mov ofn.lStructSize,eax
mov eax,hwnd
mov ofn.hwndOwner,eax
mov eax,NULL
mov ofn.hInstance,eax
mov ofn.lpstrFilter,CTEXT("Bitmap Files (*.BMP)\0*.bmp\0 All Files (*.*)\0*.*\0\0")
mov eax,NULL
mov ofn.lpstrCustomFilter,eax
xor eax,eax
mov ofn.nMaxCustFilter,eax
mov ofn.nFilterIndex,eax
lea eax,szglbFileName
mov ofn.lpstrFile,eax ; Set in Open and Close functions
mov eax,MAX_PATH
mov ofn.nMaxFile,eax
mov eax,NULL
mov ofn.lpstrFileTitle,eax ;Set in Open and Close functions
mov eax,MAX_PATH
mov ofn.nMaxFileTitle,eax
mov eax,NULL
mov ofn.lpstrInitialDir,eax
mov ofn.lpstrTitle,eax
mov eax,OFN_OVERWRITEPROMPT
mov ofn.Flags,eax ; Set in Open and Close functions
xor eax,eax
mov ofn.nFileOffset,ax
mov ofn.nFileExtension,ax
mov ofn.lpstrDefExt,CTEXT ("bmp")
mov eax,0
mov ofn.lCustData,eax
mov eax,NULL
mov ofn.lpfnHook,eax
mov ofn.lpTemplateName,eax
xor eax,eax
ret
.elseif uMsg == WM_SIZE
; Save the client area width and height in static variables.
mov eax,lParam
and eax,0FFFFh
mov cxClient,eax

mov eax,lParam
shr eax,16
mov cyClient,eax

mov eax,FALSE ; wParam = FALSE
mov wParam,eax

jmp @f ; fall through

; WM_USER_SETSCROLLS: Programmer-defined Message!
; Set the scroll bars. If the display mode is not normal,
; make them invisible. If wParam is TRUE, reset the
; scroll bar position.
.elseif uMsg == WM_USER_SETSCROLLS
@@:
.if (hglbdib == NULL || wShow != IDM_SHOW_NORMAL)
mov siWP.cbSize,sizeof (SCROLLINFO)
mov siWP.fMask,SIF_RANGE
mov siWP.nMin,0
mov siWP.nMax,0

invoke SetScrollInfo,hwnd, SB_VERT,addr siWP, TRUE
invoke SetScrollInfo,hwnd, SB_HORZ,addr siWP, TRUE
.else
; First the vertical scroll
mov siWP.cbSize,sizeof (SCROLLINFO)
mov siWP.fMask,SIF_ALL
invoke GetScrollInfo,hwnd, SB_VERT,addr siWP
mov siWP.nMin,0

invoke DibHeight,hglbdib
mov siWP.nMax,eax
mov eax,cyClient
mov siWP.nPage,eax

.if (wParam!=FALSE)
mov siWP.nPos,0
.endif
invoke SetScrollInfo,hwnd, SB_VERT,addr siWP, TRUE
invoke GetScrollInfo,hwnd, SB_VERT,addr siWP

mov eax,siWP.nPos
mov iVscroll,eax

; Then the horizontal scroll

invoke GetScrollInfo,hwnd, SB_HORZ,addr siWP
mov siWP.nMin,0
invoke DibWidth,hglbdib
mov siWP.nMax,eax
mov eax,cxClient
mov siWP.nPage, eax

.if (wParam!=FALSE)
mov siWP.nPos,0
.endif

invoke SetScrollInfo,hwnd, SB_HORZ,addr siWP, TRUE
invoke GetScrollInfo,hwnd, SB_HORZ,addr siWP

mov eax,siWP.nPos
mov iHscroll,eax
.endif
xor eax,eax
ret
; WM_VSCROLL: Vertically scroll the DIB
.elseif uMsg == WM_VSCROLL
mov eax,sizeof (SCROLLINFO)
mov siWP.cbSize,eax
mov eax,SIF_ALL
mov siWP.fMask,eax
invoke GetScrollInfo,hwnd, SB_VERT,addr siWP
mov eax,siWP.nPos
mov iVscroll,eax

mov eax,wParam
.if ax== SB_LINEUP
mov eax,siWP.nPos
dec eax
mov siWP.nPos,eax
.elseif ax== SB_LINEDOWN
mov eax,siWP.nPos
inc eax
mov siWP.nPos,eax
.elseif ax== SB_PAGEUP
mov eax,siWP.nPos
sub eax,siWP.nPage
mov siWP.nPos,eax
.elseif ax== SB_PAGEDOWN
mov eax,siWP.nPos
add eax,siWP.nPage
mov siWP.nPos,eax
.elseif ax== SB_THUMBTRACK
mov eax,siWP.nTrackPos
mov siWP.nPos,eax
.endif
mov eax,SIF_POS
mov siWP.fMask,eax
invoke SetScrollInfo,hwnd, SB_VERT,addr siWP, TRUE
invoke GetScrollInfo,hwnd, SB_VERT,addr siWP

mov eax,siWP.nPos
.if (eax != iVscroll)
mov ebx,iVscroll
sub ebx,siWP.nPos
invoke ScrollWindow,hwnd, 0, ebx, NULL, NULL
mov eax,siWP.nPos
mov iVscroll,eax
invoke UpdateWindow,hwnd
.endif

xor eax,eax
ret
; WM_HSCROLL: Horizontally scroll the DIB
.elseif uMsg == WM_HSCROLL
mov eax,sizeof (SCROLLINFO)
mov siWP.cbSize,eax

mov eax,SIF_ALL
mov siWP.fMask,eax
invoke GetScrollInfo,hwnd, SB_HORZ,addr siWP
mov eax, siWP.nPos
mov iHscroll,eax

mov eax,wParam
.if ax==SB_LINELEFT
mov eax,siWP.nPos
dec eax
mov siWP.nPos,eax
.elseif ax== SB_LINERIGHT
mov eax,siWP.nPos
inc eax
mov siWP.nPos,eax
.elseif ax== SB_PAGELEFT
mov eax,siWP.nPos
sub eax,siWP.nPage
mov siWP.nPos,eax
.elseif ax== SB_PAGERIGHT
mov eax,siWP.nPos
add eax,siWP.nPage
mov siWP.nPos,eax
.elseif ax== SB_THUMBTRACK
mov eax,siWP.nTrackPos
mov siWP.nPos,eax
.endif
mov eax,SIF_POS
mov siWP.fMask,eax
invoke SetScrollInfo,hwnd, SB_HORZ,addr siWP, TRUE
invoke GetScrollInfo,hwnd, SB_HORZ,addr siWP

mov eax,siWP.nPos
.if (eax != iHscroll)
mov ebx,iHscroll
sub ebx,siWP.nPos
invoke ScrollWindow,hwnd, ebx, 0, NULL, NULL
mov eax,siWP.nPos
mov iHscroll,eax
invoke UpdateWindow,hwnd
.endif

xor eax,eax
ret

.elseif uMsg == WM_USER_DELETEDIB
.if hglbdib!=FALSE
invoke DibDelete,hglbdib
mov eax,NULL
mov hglbdib,eax
.endif
invoke SendMessage,hwnd, WM_USER_DELETEPAL, 0, 0
xor eax,eax
ret
; This programmer-defined message deletes an existing palette
; in preparation for defining a new one.
.elseif uMsg == WM_USER_DELETEPAL
.if hglbPalette!=FALSE
invoke DeleteObject,hglbPalette
mov eax,NULL
mov hglbPalette,eax
mov eax,FALSE
mov fHalftonePalette,eax
invoke PaletteMenu,hMenu, IDM_PAL_NONE
.endif
.if (hglbBitmap!=FALSE)
invoke DeleteObject,hglbBitmap
.endif
xor eax,eax
ret
.elseif uMsg == WM_INITMENUPOPUP
.if (hglbdib!=FALSE)
mov eax,MF_ENABLED
.else
mov eax,MF_GRAYED
.endif
mov iEnable,eax

invoke EnableMenuItem,hMenu, IDM_FILE_SAVE, iEnable
invoke EnableMenuItem,hMenu, IDM_FILE_PRINT, iEnable
invoke EnableMenuItem,hMenu, IDM_FILE_PROPERTIES, iEnable
invoke EnableMenuItem,hMenu, IDM_EDIT_CUT, iEnable
invoke EnableMenuItem,hMenu, IDM_EDIT_COPY, iEnable
invoke EnableMenuItem,hMenu, IDM_EDIT_DELETE, iEnable

invoke DibIsAddressable,hglbdib
.if eax!=FALSE
mov eax,MF_ENABLED
.else
mov eax,MF_GRAYED
.endif
mov iEnable,eax

invoke EnableMenuItem,hMenu, IDM_EDIT_ROTATE, iEnable
invoke EnableMenuItem,hMenu, IDM_EDIT_FLIP, iEnable
invoke EnableMenuItem,hMenu, IDM_CONVERT_01, iEnable
invoke EnableMenuItem,hMenu, IDM_CONVERT_04, iEnable
invoke EnableMenuItem,hMenu, IDM_CONVERT_08, iEnable
invoke EnableMenuItem,hMenu, IDM_CONVERT_16, iEnable
invoke EnableMenuItem,hMenu, IDM_CONVERT_24, iEnable
invoke EnableMenuItem,hMenu, IDM_CONVERT_32, iEnable

invoke DibBitCount,hglbdib
.if eax==1
invoke EnableMenuItem,hMenu, IDM_CONVERT_01, MF_GRAYED
.elseif eax==4
invoke EnableMenuItem,hMenu, IDM_CONVERT_04, MF_GRAYED
.elseif eax==8
invoke EnableMenuItem,hMenu, IDM_CONVERT_08, MF_GRAYED
.elseif eax==16
invoke EnableMenuItem,hMenu, IDM_CONVERT_16, MF_GRAYED
.elseif eax==24
invoke EnableMenuItem,hMenu, IDM_CONVERT_24, MF_GRAYED
.elseif eax==32
invoke EnableMenuItem,hMenu, IDM_CONVERT_32, MF_GRAYED
.endif
invoke DibColorSize,hglbdib
and eax,hglbdib
cmp eax,0
jl @f
mov eax,MF_ENABLED
jmp @1
@@:
mov eax,MF_GRAYED
@1:
mov iEnable,eax

invoke EnableMenuItem,hMenu, IDM_PAL_DIBTABLE, iEnable
invoke DibIsAddressable,hglbdib
push eax
invoke DibBitCount,hglbdib
pop ebx
and eax,ebx
.if (eax > 8)
mov eax,MF_ENABLED
.else
mov eax,MF_GRAYED
.endif
mov iEnable,eax

invoke EnableMenuItem,hMenu, IDM_PAL_OPT_POP4, iEnable
invoke EnableMenuItem,hMenu, IDM_PAL_OPT_POP5, iEnable
invoke EnableMenuItem,hMenu, IDM_PAL_OPT_POP6, iEnable
invoke EnableMenuItem,hMenu, IDM_PAL_OPT_MEDCUT, iEnable
invoke IsClipboardFormatAvailable,CF_DIB
.if eax!=FALSE
mov eax,MF_ENABLED
.else
mov eax,MF_GRAYED
.endif
invoke EnableMenuItem,hMenu, IDM_EDIT_PASTE,eax
xor eax,eax
ret
.elseif uMsg == WM_COMMAND
mov iConvert, 0

mov eax,wParam
and eax,0FFFFh

.if eax == IDM_FILE_OPEN
;Show the File Open dialog box
invoke GetOpenFileName,addr ofn
.if (eax==FALSE)
xor eax,eax
ret
.endif
;If there's an existing DIB and palette, delete them
invoke SendMessage,hwnd, WM_USER_DELETEDIB, 0, 0
;Load the DIB into memory

invoke LoadCursor,NULL, IDC_WAIT
invoke SetCursor,eax
invoke ShowCursor,TRUE

invoke DibFileLoad,addr szglbFileName
mov hglbdib,eax


invoke ShowCursor,FALSE
invoke LoadCursor,NULL, IDC_ARROW
invoke SetCursor,eax
; Reset the scroll bars
invoke SendMessage,hwnd, WM_USER_SETSCROLLS, TRUE, 0
; Create the palette and DDB
invoke SendMessage,hwnd, WM_USER_CREATEPAL, TRUE, 0
.if (hglbdib==FALSE)
invoke MessageBox,hwnd, CTEXT ("Cannot load DIB file!"), addr szAppName, MB_OK or MB_ICONEXCLAMATION
.endif
invoke InvalidateRect,hwnd, NULL, TRUE
xor eax,eax
ret
.elseif eax == IDM_FILE_SAVE
; Show the File Save dialog box
invoke GetSaveFileName,addr ofn
.if eax==FALSE
xor eax,eax
ret
.endif

; Save the DIB to memory
invoke LoadCursor,NULL, IDC_WAIT
invoke SetCursor,eax
invoke ShowCursor,TRUE

invoke DibFileSave,hglbdib,addr szglbFileName
mov fSuccess,eax

invoke ShowCursor,FALSE
invoke LoadCursor,NULL, IDC_ARROW
invoke SetCursor,eax

.if (fSuccess==FALSE)
invoke MessageBox,hwnd, CTEXT ("Cannot save DIB file!"),addr szAppName, MB_OK or MB_ICONEXCLAMATION
.endif
xor eax,eax
ret

.elseif eax == IDM_FILE_PRINT

.if hglbdib==FALSE
xor eax,eax
ret
.endif
; Get printer DC
mov eax,PD_RETURNDC or PD_NOPAGENUMS or PD_NOSELECTION
mov printdlg.Flags,eax

invoke PrintDlg,addr printdlg
.if eax==FALSE
xor eax,eax
ret
.endif

mov eax,printdlg.hDC
mov hdcPrn,eax
.if (eax==NULL)
invoke MessageBox,hwnd, CTEXT ("Cannot obtain Printer DC"),addr szAppName, MB_ICONEXCLAMATION or MB_OK
xor eax,eax
ret
.endif
; Check if the printer can print bitmaps
invoke GetDeviceCaps,hdcPrn, RASTERCAPS
and eax,RC_BITBLT
.if (eax==FALSE)
invoke DeleteDC,hdcPrn
invoke MessageBox,hwnd, CTEXT ("Printer cannot print bitmaps"),addr szAppName, MB_ICONEXCLAMATION or MB_OK
xor eax,eax
ret
.endif

; Get size of printable area of page
invoke GetDeviceCaps,hdcPrn, HORZRES
mov cxPage,eax
invoke GetDeviceCaps,hdcPrn, VERTRES
mov cyPage,eax

mov eax,FALSE
mov fSuccess,eax

; Send the DIB to the printer
invoke LoadCursor,NULL, IDC_WAIT
invoke SetCursor,eax
invoke ShowCursor,TRUE

invoke StartDoc,hdcPrn,addr dinfo
cmp eax,0
jl @f
invoke StartPage,hdcPrn
cmp eax,0
jl @f
invoke DibBitmapHandle,hglbdib
invoke DisplayDib,hdcPrn, eax, 0, 0,cxPage, cyPage, IDM_SHOW_NORMAL, FALSE

invoke EndPage ,hdcPrn
cmp eax,0
jl @f
mov eax,TRUE
mov fSuccess,eax
invoke EndDoc,hdcPrn
@@:

invoke ShowCursor,FALSE
invoke LoadCursor,NULL, IDC_ARROW
invoke SetCursor,eax
invoke DeleteDC,hdcPrn

.if ( fSuccess==FALSE)
invoke MessageBox,hwnd, CTEXT ("Could not print bitmap"),addr szAppName, MB_ICONEXCLAMATION or MB_OK
.endif
xor eax,eax
ret

.elseif eax == IDM_FILE_PROPERTIES
.if hglbdib==FALSE
xor eax,eax
ret
.endif
invoke DibCompression,hglbdib
.if eax>3
mov eax,3
.endif
mov ecx,12
mul ecx
lea edi,szCompression
add edi,eax
push edi
invoke DibWidth,hglbdib
push eax
invoke DibHeight,hglbdib
push eax
invoke DibBitCount,hglbdib
push eax
invoke DibNumColors,hglbdib
mov esi,eax
pop ebx
pop ecx
pop edx
pop edi
invoke wsprintf,addr szBuffer, addr szFileProperties,
edx, ecx,
ebx, esi,edi
invoke MessageBox,hwnd,addr szBuffer,addr szAppName, MB_ICONEXCLAMATION or MB_OK
xor eax,eax
ret
.elseif eax == IDM_APP_EXIT
invoke SendMessage,hwnd, WM_CLOSE, 0, 0
xor eax,eax
ret
.elseif (eax == IDM_EDIT_COPY)||(eax==IDM_EDIT_CUT)
invoke DibCopyToPackedDib,hglbdib, TRUE
mov hGlobal,eax
.if (eax==FALSE)
xor eax,eax
ret
.endif
invoke OpenClipboard,hwnd
invoke EmptyClipboard
invoke SetClipboardData,CF_DIB, hGlobal
invoke CloseClipboard

mov eax,wParam

.if (ax == IDM_EDIT_COPY)
xor eax,eax
ret
.endif
invoke Beep,1000,100
; fall through for IDM_EDIT_CUT
jmp @f
.elseif eax == IDM_EDIT_DELETE
@@:
invoke SendMessage,hwnd, WM_USER_DELETEDIB, 0, 0
invoke InvalidateRect,hwnd, NULL, TRUE
xor eax,eax
ret
.elseif eax == IDM_EDIT_PASTE
invoke OpenClipboard,hwnd
invoke GetClipboardData,CF_DIB
mov hGlobal,eax

invoke GlobalLock,hGlobal
mov pGlobal,eax

;If there's an existing DIB and palette,delete them.
; Then convert the packed DIB to an HDIB.
.if (pGlobal!=FALSE)
invoke SendMessage,hwnd, WM_USER_DELETEDIB, 0, 0
invoke DibCopyFromPackedDib,pGlobal
mov hglbdib ,eax
invoke SendMessage,hwnd, WM_USER_CREATEPAL, TRUE, 0
.endif
invoke GlobalUnlock,hGlobal
invoke CloseClipboard
; Reset the scroll bars
invoke SendMessage,hwnd, WM_USER_SETSCROLLS, TRUE, 0
invoke InvalidateRect,hwnd, NULL, TRUE
xor eax,eax
ret
.elseif eax == IDM_EDIT_ROTATE
invoke DibRotateRight,hglbdib
mov hdibNew,eax
.if eax!=FALSE
invoke DibDelete,hglbdib
invoke DeleteObject,hglbBitmap
mov eax,hdibNew
mov hglbdib,eax

invoke DibCopyToDdb,hglbdib, hwnd, hglbPalette
mov hglbBitmap,eax
invoke SendMessage,hwnd, WM_USER_SETSCROLLS, TRUE, 0
invoke InvalidateRect,hwnd, NULL, TRUE
.else
invoke MessageBox,hwnd, CTEXT ("Not enough memory"),addr szAppName, MB_OK or MB_ICONEXCLAMATION
.endif
xor eax,eax
ret
.elseif eax == IDM_EDIT_FLIP
invoke DibFlipHorizontal,hglbdib
mov hdibNew,eax
.if eax!=FALSE
invoke DibDelete,hglbdib
invoke DeleteObject,hglbBitmap
mov eax,hdibNew
mov hglbdib,eax
invoke DibCopyToDdb,hglbdib, hwnd, hglbPalette
mov hglbBitmap,eax
invoke InvalidateRect,hwnd, NULL, TRUE

.else
invoke MessageBox,hwnd, CTEXT ("Not enough memory"),addr szAppName, MB_OK or MB_ICONEXCLAMATION
.endif
xor eax,eax
ret
.elseif eax == IDM_EDIT_FLIP
;invoke DibFlipHorizontal,hglbdib
mov hdibNew,eax
.if eax!=FALSE
invoke DibDelete,hglbdib
invoke DeleteObject,hglbBitmap
mov eax,hdibNew
mov hglbdib,eax

invoke DibCopyToDdb,hglbdib, hwnd, hglbPalette
mov hglbBitmap,eax
invoke InvalidateRect,hwnd, NULL, TRUE
.else
invoke MessageBox,hwnd, CTEXT ("Not enough memory"),addr szAppName, MB_OK or MB_ICONEXCLAMATION
.endif
xor eax,eax
ret
.elseif (eax == IDM_SHOW_NORMAL)||(eax == IDM_SHOW_CENTER)||(eax == IDM_SHOW_STRETCH)||(eax == IDM_SHOW_ISOSTRETCH)
invoke CheckMenuItem,hMenu, wShow, MF_UNCHECKED
mov eax,wParam
shl eax,16
shr eax,16
mov wShow,eax
invoke CheckMenuItem,hMenu, wShow, MF_CHECKED
invoke SendMessage,hwnd, WM_USER_SETSCROLLS, FALSE, 0
invoke InvalidateRect,hwnd, NULL, TRUE
mov eax,eax
ret
.elseif (eax == IDM_CONVERT_32)||(eax == IDM_CONVERT_24)||(eax == IDM_CONVERT_16)
add iConvert,8
jmp @f
.elseif (eax == IDM_CONVERT_08)
@@:
add iConvert,4
jmp @f
.elseif (eax == IDM_CONVERT_04)
@@:
add iConvert,3
jmp @f
.elseif (eax == IDM_CONVERT_01)
@@:
add iConvert,1
@@:
invoke LoadCursor,NULL, IDC_WAIT
invoke SetCursor,eax
invoke ShowCursor,TRUE

invoke DibConvert,hglbdib, iConvert
mov hdibNew,eax

invoke ShowCursor,FALSE
invoke LoadCursor,NULL, IDC_ARROW
invoke SetCursor,eax
.if (hdibNew!=FALSE)
invoke SendMessage,hwnd, WM_USER_DELETEDIB, 0, 0
mov eax,hdibNew
mov hglbdib,eax
invoke SendMessage,hwnd, WM_USER_CREATEPAL, TRUE, 0
invoke InvalidateRect,hwnd, NULL, TRUE
.else
invoke MessageBox,hwnd, CTEXT ("Not enough memory"),addr szAppName, MB_OK or MB_ICONEXCLAMATION
.endif

xor eax,eax
ret
.endif
xor eax,eax
ret
.elseif uMsg == WM_USER_CREATEPAL
.if (hglbdib!=FALSE)
invoke GetDC,hwnd
mov hdc,eax
invoke GetDeviceCaps,hdc, RASTERCAPS
and eax,RC_PALETTE
.if (eax == FALSE)
invoke PaletteMenu,hMenu, IDM_PAL_NONE
.else

invoke DibPalDibTable,hglbdib
mov hglbPalette,eax

.if (eax!=FALSE)
invoke PaletteMenu,hMenu, IDM_PAL_DIBTABLE
.else
invoke CreateHalftonePalette,hdc
mov hglbPalette,eax
.if (hglbPalette != FALSE )
mov eax,TRUE
mov fHalftonePalette,eax
invoke PaletteMenu,hMenu, IDM_PAL_HALFTONE
.endif
.endif
.endif
.if (wParam!=FALSE)
invoke DibCopyToDdb,hglbdib, hwnd, hglbPalette
mov hglbBitmap,eax
.endif

.endif
xor eax,eax
ret
.elseif uMsg == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax

.if (hglbPalette!=FALSE)
invoke SelectPalette,hdc, hglbPalette, FALSE
invoke RealizePalette,hdc
.endif
.if (hglbBitmap!=FALSE)
.if fHalftonePalette!=FALSE
invoke DibBitmapHandle,hglbdib
.else
mov eax,hglbBitmap
.endif
invoke DisplayDib,hdc,eax,iHscroll, iVscroll,cxClient, cyClient,wShow, fHalftonePalette
.endif
invoke EndPaint,hwnd,addr ps
xor eax,eax
ret
.elseif uMsg == WM_DESTROY
.if (hglbdib!=0)
invoke DibDelete,hglbdib
.endif

.if (hglbBitmap!=0)
invoke DeleteObject,hglbBitmap
.endif

.if (hglbPalette!=0)
invoke DeleteObject,hglbPalette
.endif

invoke PostQuitMessage,NULL
xor eax,eax
ret
.endif

invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
WndProc endp
END START

下面是本章程序中的几个调试联系,你可以尝试使用一切手段来调试,当然,除了直接看上面的答案.

1.问什么我的showdib6.asm和书上的运行结果不同?看灰度图像的时候要“黑”的多,问题究竟出在什么地方呢?

有问题的程序和可执行文件下载

正常的

有问题程序的

汇编语言中对于结构体的处理和C语言类似,但是在处理指向结构体的指针时,并不方便,比如说 plp->palPalEntry[i].peRed = (BYTE) min (255, 4 * i) 这样的.汇编语言可以使用assume来指定一个,但是我觉得这样的方法并不妥当,一来,申明和去掉申明很麻烦;二来,这样做会掩盖实际的地址.因此,我是直接使用基地址+偏移量的方式来完成要求. 本期涉及到的结构体非常多,我的做法是列出一张表,也计算好偏移,用的时候直接参考. 下载



<<<上一篇
欢迎访问AoGo汇编小站:http://www.aogosoft.com