http://hi.baidu.com/123az/blog/item/f482f51980e8e04642a9ad6c.html

资源文件中的MATTHEW.BMP文件是我侄子的一幅黑白数字照片,宽200图素,高320图素,每图素8位。不过,另外制作个BITMASK只是因为此文件的内容是任何东西都可以。

注意,BITMASK将窗口背景设为亮灰色。这样就确保我们能正确地屏蔽位图,而不只是将其涂成白色。

下面让我们看一下WM_CREATE的处理程序:BITMASK用LoadBitmap函数获得hBitmapImag变量中原始图像的句柄。用GetObject函数可取得位图的宽度高度。然后将位图句柄选进句柄为hdcMemImag的内存设备内容中。

程序建立的下一个单色位图与原来的图大小相同,其句柄储存在hBitmapMask,并选进句柄为hdcMemMask的内存设备内容中。在内存设备内容中,使用GDI函数,屏蔽位图就涂成了黑色背景和一个白色的椭圆:

SelectObject (hdcMemMask, GetStockObject (BLACK_BRUSH)) ;

Rectangle (hdcMemMask, 0, 0, cxBitmap, cyBitmap) ;

SelectObject (hdcMemMask, GetStockObject (WHITE_BRUSH)) ;

Ellipse (hdcMemMask, 0, 0, cxBitmap, cyBitmap) ;

因为这是一个单色的位图,所以黑色区域的位是0,而白色区域的位是1。

然后BitBlt呼叫就按此屏蔽修改了原图像:

BitBlt (hdcMemImag, 0, 0, cxBitmap, cyBitmap,

hdcMemMask, 0, 0, SRCAND) ;

SRCAND位映像操作在来源位(屏蔽位图)和目的位(原图像)之间执行了位AND操作。只要屏蔽位图是白色,就显示目的;只要屏蔽是黑色,则目的就也是黑色。现在原图像中就形成了一个黑色包围的椭圆区域。

现在让我们看一下WM_PAINT处理程序。此程序同时改变了选进内存设备内容中的图像位图和屏蔽位图。两次BitBlt呼叫完成了这个魔术,第一次在窗口上执行屏蔽位图的BitBlt:

BitBlt (hdc, x, y, cxBitmap, cyBitmap, hdcMemMask, 0, 0, 0x220326) ;

这里使用了一个没有名称的位映像操作。逻辑运算子是D & ~S。回忆来源-即屏蔽位图-是黑色(位值0)包围的一个白色(位值1)椭圆。位映像操作首先将来源反色,也就是改成白色包围的黑色椭圆。然后位操作在这 个已转换的来源和目的(即窗口上)之间执行位AND操作。当目的和位值1「AND」时保持不变;与位值0「AND」时,目的将变黑。因此,BitBlt操 作将在窗口上画一个黑色的椭圆。

第二次的BitBlt呼叫则在窗口中绘制图像位图:

BitBlt (hdc, x, y, cxBitmap, cyBitmap, hdcMemImag, 0, 0, SRCPAINT) ;

位映像操作在来源和目的之间执行位「OR」操作。由于来源位图的外面是黑色,因此保持目的不变;而在椭圆区域内,目的是黑色,因此图像就原封不动地复制了过来。执行结果如图14-9所示。

注意事项:

有时您需要一个很复杂的屏蔽-例如,抹去原始图像的整个背景。您将需要在画图程序中手工建立然后将其储存到成文件。

 

 

图14-9 BITMASK的屏幕显示

如果正在为Windows NT编写类似的应用程序,那么您可以使用与MASKBIT程序类似的MaskBlt函数,而只需要更少的函数呼叫。Windows NT还包括另一个类似BitBlt的函数,Windows 98不支持该函数。此函数是PlgBlt(「平行四边形位块移动:parallelogram blt」)。这个函数可以对图像进行旋转或者倾斜位图图像。

最后,如果在您的机器上执行BITMASK程序,您就只会看见黑色、白色和两个灰色的阴影,这是因为您执行的显示模式是16色或256色。对于16色模式,显示效果无法改进,但在256色模式下可以改变调色盘以显示灰阶。您将在第十六章学会如何设定调色盘。

简单的动画

小张的位图显示起来非常快,因此可以将位图和Windows定时器联合使用,来完成一些基本的动画。

现在开始这个弹球程序。

BOUNCE程序,如程序14-10所示,产生了一个在窗口显示区域弹来弹去的小球。该程序利用定时器来控制小球的行进速度。小球本身是一幅位图, 程序首先通过建立位图来建立小球,将其选进内存设备内容,然后呼叫一些简单的GDI函数。程序用BitBlt从一个内存设备内容将这个位图小球画到显示器 上。

程序14-10 BOUNCE
        
BOUNCE.C

/*---------------------------------------------------------------------------

BOUNCE.C -- Bouncing Ball Program

(c) Charles Petzold, 1998

----------------------------------------------------------------------------*/

#include <windows.h>

#define ID_TIMER 1


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

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

PSTR szCmdLine, int iCmdShow)

{

static TCHAR szAppName[] = TEXT ("Bounce") ;

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 = NULL ;

wndclass.lpszClassName = szAppName ;



if (!RegisterClass (&wndclass))

{

MessageBox ( NULL, TEXT ("This program requires Windows NT!"),

szAppName, MB_ICONERROR) ;

return 0 ;

}



hwnd = CreateWindow ( szAppName, TEXT ("Bouncing Ball"),

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT,

NULL, NULL, hInstance, NULL) ;



ShowWindow (hwnd, iCmdShow) ;

UpdateWindow (hwnd) ;

while (GetMessage (&msg, NULL, 0, 0))

{

TranslateMessage (&msg) ;

DispatchMessage (&msg) ;

}

return msg.wParam ;

}


LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

static HBITMAP hBitmap ;

static int cxClient, cyClient, xCenter, yCenter, cxTotal, cyTotal,

cxRadius, cyRadius, cxMove, cyMove, xPixel, yPixel ;

HBRUSH hBrush ;

HDC hdc, hdcMem ;

int iScale ;



switch (iMsg)

{

case WM_CREATE:

hdc = GetDC (hwnd) ;

xPixel = GetDeviceCaps (hdc, ASPECTX) ;

yPixel = GetDeviceCaps (hdc, ASPECTY) ;

ReleaseDC (hwnd, hdc) ;



SetTimer (hwnd, ID_TIMER, 50, NULL) ;

return 0 ;



case WM_SIZE:

xCenter = (cxClient = LOWORD (lParam)) / 2 ;

yCenter = (cyClient = HIWORD (lParam)) / 2 ;



iScale = min (cxClient * xPixel, cyClient * yPixel) / 16 ;



cxRadius = iScale / xPixel ;

cyRadius = iScale / yPixel ;



cxMove = max (1, cxRadius / 2) ;

cyMove = max (1, cyRadius / 2) ;



cxTotal = 2 * (cxRadius + cxMove) ;

cyTotal = 2 * (cyRadius + cyMove) ;



if (hBitmap)

DeleteObject (hBitmap) ;

hdc = GetDC (hwnd) ;

hdcMem = CreateCompatibleDC (hdc) ;

hBitmap = CreateCompatibleBitmap (hdc, cxTotal, cyTotal) ;

ReleaseDC (hwnd, hdc) ;



SelectObject (hdcMem, hBitmap) ;

Rectangle (hdcMem, -1, -1, cxTotal + 1, cyTotal + 1) ;



hBrush = CreateHatchBrush (HS_DIAGCROSS, 0L) ;

SelectObject (hdcMem, hBrush) ;

SetBkColor (hdcMem, RGB (255, 0, 255)) ;

Ellipse (hdcMem, cxMove, cyMove, cxTotal - cxMove, cyTotal - cyMove) ;

DeleteDC (hdcMem) ;

DeleteObject (hBrush) ;

return 0 ;



case WM_TIMER:

if (!hBitmap)

break ;



hdc = GetDC (hwnd) ;

hdcMem = CreateCompatibleDC (hdc) ;

SelectObject (hdcMem, hBitmap) ;



BitBlt (hdc, xCenter - cxTotal / 2,

yCenter - cyTotal / 2, cxTotal, cyTotal,

hdcMem, 0, 0, SRCCOPY) ;



ReleaseDC (hwnd, hdc) ;

DeleteDC (hdcMem) ;



xCenter += cxMove ;

yCenter += cyMove ;



if ((xCenter + cxRadius >= cxClient) || (xCenter - cxRadius <= 0))

cxMove = -cxMove ;



if ((yCenter + cyRadius >= cyClient) || (yCenter - cyRadius <= 0))

cyMove = -cyMove ;



return 0 ;



case WM_DESTROY:

if (hBitmap)

DeleteObject (hBitmap) ;



KillTimer (hwnd, ID_TIMER) ;

PostQuitMessage (0) ;

return 0 ;

}

return DefWindowProc (hwnd, iMsg, wParam, lParam) ;

}

BOUNCE每次收到一个WM_SIZE消息时都重画小球。这就需要与视讯显示器兼容的内存设备内容:

hdcMem = CreateCompatibleDC (hdc) ;

小球的直径设为窗口显示区域高度或宽度中较短者的十六分之一。不过,程序构造的位图却比小球大:从位图中心到位图四个边的距离是小球半径的1.5倍:

hBitmap = CreateCompatibleBitmap (hdc, cxTotal, cyTotal) ;

将位图选进内存设备内容后,整个位图背景设成白色:

Rectangle (hdcMem, -1, -1, xTotal + 1, yTotal + 1) ;

那些不固定的坐标使矩形边框在位图之外着色。一个对角线开口的画刷选进内存设备内容,并将小球画在位图的中央:

Ellipse (hdcMem, xMove, yMove, xTotal - xMove, yTotal - yMove) ;

当小球移动时,小球边界的空白会有效地删除前一时刻的小球图像。在另一个位置重画小球只需在BitBlt呼叫中使用SRCCOPY的ROP代码:

BitBlt (hdc, xCenter - cxTotal / 2, yCenter - cyTotal / 2, cxTotal, cyTotal,

hdcMem, 0, 0, SRCCOPY) ;

BOUNCE程序只是展示了在显示器上移动图像的最简单的方法。在一般情况下,这种方法并不能令人满意。如果您对动画感兴趣,那么除了在来源和目的 之间执行或操作以外,您还应该研究其它的ROP代码(例如SRCINVERT)。其它动画技术包括Windows调色盘(以及 AnimatePalette函数)和CreateDIBSection函数。对于更高级的动画您只好放弃GDI而使用DirectX接口了。

窗口外的位图

SCRAMBLE程序,如程序14-11所示,编写非常粗糙,我本来不应该展示这个程序,但它示范了一些有趣的技术,而且在交换两个显示矩形内容的BitBlt操作的程序中,用内存设备内容作为临时储存空间。

程序14-11  SCRAMBLE

SCRAMBLE.C

/*---------------------------------------------------------------------------

SCRAMBLE.C -- Scramble (and Unscramble) Screen

(c) Charles Petzold, 1998

-----------------------------------------------------------------------------*/

#include <windows.h>


#define NUM 300


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


int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

PSTR szCmdLine, int iCmdShow)

{

static int iKeep [NUM][4] ;

HDC hdcScr, hdcMem ;

int cx, cy ;

HBITMAP hBitmap ;

HWND hwnd ;

int i, j, x1, y1, x2, y2 ;


if (LockWindowUpdate (hwnd = GetDesktopWindow ()))

{

hdcScr = GetDCEx (hwnd, NULL, DCX_CACHE | DCX_LOCKWINDOWUPDATE) ;

hdcMem = CreateCompatibleDC (hdcScr) ;

cx = GetSystemMetrics (SM_CXSCREEN) / 10 ;

cy = GetSystemMetrics (SM_CYSCREEN) / 10 ;

hBitmap = CreateCompatibleBitmap (hdcScr, cx, cy) ;



SelectObject (hdcMem, hBitmap) ;

srand ((int) GetCurrentTime ()) ;



for (i = 0 ; i < 2 ; i++)

for (j = 0 ; j < NUM ; j++)

{

if (i == 0)

{

iKeep [j] [0] = x1 = cx * (rand () % 10) ;

iKeep [j] [1] = y1 = cy * (rand () % 10) ;

iKeep [j] [2] = x2 = cx * (rand () % 10) ;

iKeep [j] [3] = y2 = cy * (rand () % 10) ;

}

else

{

x1 = iKeep [NUM - 1 - j] [0] ;

y1 = iKeep [NUM - 1 - j] [1] ;

x2 = iKeep [NUM - 1 - j] [2] ;

y2 = iKeep [NUM - 1 - j] [3] ;

}

BitBlt (hdcMem, 0, 0, cx, cy, hdcScr, x1, y1, SRCCOPY) ;

BitBlt (hdcScr, x1, y1, cx, cy, hdcScr, x2, y2, SRCCOPY) ;

BitBlt (hdcScr, x2, y2, cx, cy, hdcMem, 0, 0, SRCCOPY) ;



Sleep (10) ;

}



DeleteDC (hdcMem) ;

ReleaseDC (hwnd, hdcScr) ;

DeleteObject (hBitmap) ;



LockWindowUpdate (NULL) ;

}

return FALSE ;

}

你可能感兴趣的:(windows,timer,null,application,callback,winapi)