第十四章 位图和Bitblt(位块传输)

来源:互联网 发布:热血三国马装数据 编辑:程序博客网 时间:2024/06/05 03:58

位块传输

我前面提到过,您可以把整个视讯显示器看作是一幅大位图。您在屏幕上见到的图素由储存在视讯显示卡上内存中的位来描述。任何视讯显示的矩形区域也都是一个位图,其大小是它所包含的行列数。

让我们从将图像从视讯显示的一个区域复制到另一个区域,开始我们在位图世界的旅行吧!这个是强大的BitBlt函数的工作。

Bitblt(读作「bit blit」)代表「位块传输(bit-block transfer)」。BLT起源于一条汇编语言指令,该指令在DEC PDP-10上用来传输内存块。术语「bitblt」第一次用在图像上与Xerox Palo Alto Research Center(PARC)设计的SmallTalk系统有关。在SmallTalk中,所有的图形输出操作都使用bitblt。程序写作者有时将blt用作动词,例如:「Then I wrote some code to blt the happy face to the screen and play a wave file.」

BitBlt函数移动的是图素,或者(更明确地)是一个位映像图块。您将看到,术语「传输(transfer)」与BitBlt函数不尽相同。此函数实际上对图素执行了一次位操作,而且可以产生一些有趣的结果。

简单的BitBlt

程序14-1所示的BITBLT程序用BitBlt函数将程序系统的菜单图标(位于程序Windows的左上角)复制到它的显示区域。

程序14-1 BITBLT
        
BITBLT.C

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

BITBLT.C -- BitBlt Demonstration

(c) Charles Petzold, 1998

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

#include <windows.h>

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

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

PSTR szCmdLine, int iCmdShow)

{

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

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_INFORMATION) ;

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 ("BitBlt Demo"),

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT,

NULL, NULL, hInstance, NULL) ;


ShowWindow (hwnd, iCmdShow) ;

UpdateWindow (hwnd) ;

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

{

TranslateMessage (&msg) ;

DispatchMessage (&msg) ;

}

return msg.wParam ;

}


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

{

static int cxClient, cyClient, cxSource, cySource ;

HDC hdcClient, hdcWindow ;

int x, y ;

PAINTSTRUCT ps ;



switch (message)

{

case WM_CREATE:

cxSource = GetSystemMetrics (SM_CXSIZEFRAME) +

GetSystemMetrics (SM_CXSIZE) ;

cySource = GetSystemMetrics (SM_CYSIZEFRAME) +

GetSystemMetrics (SM_CYCAPTION) ;

return 0 ;

case WM_SIZE:

cxClient = LOWORD (lParam) ;

cyClient = HIWORD (lParam) ;

return 0 ;


case WM_PAINT:

hdcClient = BeginPaint (hwnd, &ps) ;

hdcWindow = GetWindowDC (hwnd) ;



for (y = 0 ; y < cyClient ; y += cySource)

for (x = 0 ; x < cxClient ; x += cxSource)

{

BitBlt (hdcClient, x, y, cxSource, cySource,

hdcWindow, 0, 0, SRCCOPY) ;

}


ReleaseDC (hwnd, hdcWindow) ;

EndPaint (hwnd, &ps) ;

return 0 ;


case WM_DESTROY:

PostQuitMessage (0) ;

return 0 ;

}

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

}

但为什么只用了一个BitBlt呢?实际上,那个BITBLT用系统菜单图标的多个副本来填满显示区域(在此情况下是信息方块中普遍使用的IDI_INFORMATION图示),如图14-1所示。

 

 

图14-1 BITBLT的屏幕显示

BitBlt函数从称为「来源」的设备内容中将一个矩形区的图素传输到称为「目的(destination)」的另一个设备内容中相同大小的矩形区。此函数的语法如下:

BitBlt (hdcDst, xDst, yDst, cx, cy, hdcSrc, xSrc, ySrc, dwROP) ;

来源和目的设备内容可以相同。

在BITBLT程序中,目的设备内容是窗口的显示区域,设备内容句柄从BeginPaint函数获得。来源设备内容是应用程序的整个窗口,此设备内容句柄从GetWindowDC获得的。很明显地,这两个设备内容指的是同一个实际设备(视讯显示器)。不过,这两个设备内容的坐标原点不同。

xSrc和ySrc参数指明了来源图像左上角的坐标位置。在BITBLT中,这两个参数设为0,表示图像从来源设备内容(也就是整个窗口)的左上角开始,cx和cy参数是图像的宽度和高度。BITBLT根据从GetSytemMetrics函数获得的信息来计算这些值。

xDst和yDst参数表示了复制图像位置左上角的坐标位置。在BITBLT中,这两个参数设定为不同的值以便多次复制图像。对于第一次BitBlt呼叫,这两个参数设制为0,将图像复制到显示区域的左上角位置。

BitBlt的最后一个参数是位映像操作型态。我将简短地讨论一下这个值。

请注意,BitBlt是从实际视讯显示内存传输图素,而不是从系统菜单图标的其它图像传输。如果您移动BITBLT窗口以使部分系统菜单图标移出屏幕,然后调整BITBLT窗口的尺寸使其重画,这时您将发现BITBLT显示区域中显示的是菜单图示的一部分。BitBlt函数不再存取整个图像。

在BitBlt函数中,来源和目的设备内容可以相同。您可以重新编写BITBLT以使WM_PAINT处理执行以下内容:

BitBlt (hdcClient, 0, 0, cxSource, cySource,

hdcWindow, 0, 0, SRCCOPY) ;

for (y = 0 ; y < cyClient ; y += cySource)

for (x = 0 ; x < cxClient ; x += cxSource)

{

if (x > 0 || y > 0)

BitBlt (hdcClient, x, y, cxSource, cySource,

hdcClient, 0, 0, SRCCOPY) ;

}

这将与前面显示的BITBLT一样产生相同的效果,只是显示区域左上角比较模糊。

在BitBlt内的最大限制是两个设备内容必须是兼容的。这意味着或者其中之一必须是单色的,或者两者的每个图素都相同的位数。总而言之,您不能用此方法将屏幕上的某些图形复制到打印机。

拉伸位图

在BitBlt函数中,目的图像与来源图像的尺寸是相同的,因为函数只有两个参数来说明宽度和高度。如果您想在复制时拉伸或者压缩图像尺寸,可以使用StretchBlt函数。StretchBlt函数的语法如下:

StretchBlt (hdcDst, xDst, yDst, cxDst, cyDst,

hdcSrc, xSrc, ySrc, cxSrc, cySrc, dwROP) ;

此函数添加了两个参数。现在的函数就分别包含了目的和来源各自的宽度和高度。STRETCH程序展示了StretchBlt函数,如程序14-2所示。

程序14-2  STRETCH

STRETCH.C

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

STRETCH.C -- StretchBlt Demonstration

(c) Charles Petzold, 1998

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

#include <windows.h>

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

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

PSTR szCmdLine, int iCmdShow)

{

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

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_INFORMATION) ;

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 ("StretchBlt Demo"),

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT,

CW_USEDEFAULT,

CW_USEDEFAULT,

CW_USEDEFAULT,

NULL, NULL, hInstance, NULL) ;

ShowWindow (hwnd, iCmdShow) ;

UpdateWindow (hwnd) ;


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

{

TranslateMessage (&msg) ;

DispatchMessage (&msg) ;

}

return msg.wParam ;

}


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

{

static int cxClient, cyClient, cxSource, cySource ;

HDC hdcClient, hdcWindow ;

PAINTSTRUCT ps ;



switch (message)

{

case WM_CREATE:

cxSource = GetSystemMetrics (SM_CXSIZEFRAME) +

GetSystemMetrics (SM_CXSIZE) ;


cySource = GetSystemMetrics (SM_CYSIZEFRAME) +

GetSystemMetrics (SM_CYCAPTION) ;

return 0 ;


case WM_SIZE:

cxClient = LOWORD (lParam) ;

cyClient = HIWORD (lParam) ;

return 0 ;


case WM_PAINT:

hdcClient = BeginPaint (hwnd, &ps) ;

hdcWindow = GetWindowDC (hwnd) ;


StretchBlt (hdcClient, 0, 0, cxClient, cyClient,

hdcWindow, 0, 0, cxSource, cySource, MERGECOPY) ;


ReleaseDC (hwnd, hdcWindow) ;

EndPaint (hwnd, &ps) ;

return 0 ;


case WM_DESTROY:

PostQuitMessage (0) ;

return 0 ;

}

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

}

此程序只有呼叫了StretchBlt函数一次,但是利用此函数以系统菜单图标填充了整个显示区域,如图14-2所示。

 

 

图14-2 STRETCH的屏幕显示

BitBlt和StretchBlt函数中所有的坐标与大小都是依据逻辑单位的。但是当您在BitBlt函数中定义了两个不同的设备内容,而这两个设备内容虽然参考同一个实际设备,却各自有着不同的映像模式,这时将发生什么结果呢?如果出现这种情况,呼叫BitBlt产生的结果就显得不明确了:cx和cy参数都是逻辑单位,而它们同样应用于来源设备内容和目的设备内容中的矩形区。所有的坐标和尺寸必须在实际的位传输之前转换为设备坐标。因为cx和cy值同时用于来源和目的设备内容,所以此值必须转换为设备内容自己的单位。

当来源和目的设备内容相同,或者两个设备内容都使用MM_TEXT图像模式时,设备单位下的矩形尺寸在两个设备内容中会是相同的,然后才由Windows进行图素对图素的转换。不过,如果设备单位下的矩形尺寸在两个设备内容中不同时,则Windows就把此工作转交给更通用的StretchBlt函数。

StretchBlt也允许水平或垂直翻转图像。如果cxSrc和cxDst标记(转换成设备单位以后)不同,那么StretchBlt就建立一个镜像:左右翻转。在STRETCH程序中,通过将xDst参数改为cxClient并将cxDst参数改成-cxClient,您就可以做到这一点。如果cySrc和cyDst不同,则StretchBlt会上下翻转图像。要在STRETCH程序中测试这一点,可将yDst参数改为cyClient并将cyDst参数改成-cyClient。

StretchBlt模式

使用StretchBlt会碰到一些与位图大小缩放相关的一些根本问题。在扩展一个位图时,StretchBlt必须复制图素行或列。如果放大倍数不是原图的整数倍,那么此操作会造成产生的图像有些失真。

如果目的矩形比来源矩形小,那么StretchBlt在缩小图像时就必须把两行(或列)或者多行(或列)的图素合并到一行(或列)。完成此操作有四种方法,它根据设备内容伸展模式属性来选择其中一种方法。您可使用SetStretchBltMode函数来修改这个属性。

SetStretchBltMode (hdc, iMode) ;

iMode可取下列值:

  • BLACKONWHITE或者STRETCH_ANDSCANS(内定)如果两个或多个图素得合并成一个图素,那么StretchBlt会对图素执行一个逻辑AND运算。这样的结果是只有全部的原始图素是白色时该图素才为白色,其实际意义是黑色图素控制了白色图素。这适用于白背景中主要是黑色的单色位图。
  • WHITEONBLACK或STRETCH_ORSCANS 如果两个或多个图素得合并成一个图素,那么StretchBlt执行逻辑OR运算。这样的结果是只有全部的原始图素都是黑色时才是黑色,也就是说由白色图素决定颜色。这适用于黑色背景中主要是白色的单色位图。
  • COLORONCOLOR或STRETCH_DELETESCANS StretchBlt简单地消除图素行或列,而没有任何逻辑组合。这是通常是处理彩色位图的最佳方法。
  • HALFTONE或STRETCH_HALFTONE Windows根据组合起来的来源颜色来计算目的的平均颜色。这将与半调调色盘联合使用, 第十六章将展示这一程序。

Windows还包括用于取得目前伸展模式的GetStretchBltMode函数。

位映像操作

BITBLT和STRETCH程序简单地将来源位图复制给了目的位图,在过程中也可能进行了缩放。这是把SRCCOPY作为BitBlt和StretchBlt函数最后一个参数的结果。SRCCOPY只是您能在这些函数中使用的256个位映像操作中的一个。让我们先在STRETCH程序中做一个别的实验,然后再系统地研究位映像操作。

尽量用NOTSRCCOPY来代替SRCCOPY。与它们名称一样,位映像操作在复制位图时转换其颜色。在显示区域窗口,所有的颜色转换:黑色变成白色、白色变成黑色,蓝色变成黄色。现在试一下SRCINVERT,您将得到同样效果。如果试一下BLACKNESS,正如其名称一样,整个显示区域都将变成黑色,而WHITENESS则使其变成白色。

现在试一试用下列三条叙述来代替StretchBlt呼叫:

SelectObject (hdcClient, CreateHatchBrush (HS_DIAGCROSS, RGB (0, 0, 0)));

StretchBlt (hdcClient, 0, 0, cxClient, cyClient,

hdcWindow, 0, 0, cxSource, cySource, MERGECOPY) ;

DeleteObject (hdcClient, GetStockObject (WHITE_BRUSH)) ;

这次,您将在图像上看到一个菱形的画刷,这是什么?

我在前面说过,BitBlt和StretchBlt函数不是简单的位块传输。此函数实际在下面三种图像间执行位操作。

  • Source 来源位图,拉伸或压缩(如果有必要)到目的矩形的尺寸。
  • Destination 在BitBlt或StretchBlt呼叫之前的目的矩形。
  • Pattern 在目的设备内容中选择的目前画刷,水平或垂直地复制到目的矩形范围内。

结果是复制到了目的矩形中。

位映像操作与我们在第五章遇到的绘图模式在概念上相似。绘图模式采用图像对象的控件方式,例如一条线就组合成一个目的。我们知道有16种绘图模式-也就是说,对象中的0和1画出时,唯一结果就是目的中0和1的组合。

使用BitBlt和StretchBlt的位映像操作包含了三个对象的组合,这将产生256种位映像操作。有256种方法来组合来源位图、目的位图和图案。有15种位映像操作已经命名-其中一些名称其实还不能够清楚清楚说明其意义-它们定义在WINGDI.H里头,其余的都有数值,列在/Platform SDK/Graphics and Multimedia Services/GDI/Raster Operation Codes/Ternary Raster Operations之中。

有名称的15种ROP代码见表14-4。

表14-4

 

图案(P):1 1 1 1 0 0 0 0

来源(s):1 1 0 0 1 1 0 0

目的(D):1 0 1 0 1 0 1 0

布尔操作

ROP代码

名称

结果:

0 0 0 0 0 0 0 0

0

0x000042

BLACKNESS

 

0 0 0 1 0 0 0 1

~(S?#160;D)

0x1100A6

NOTSRCERASE

 

0 0 1 1 0 0 1 1

~S

0x330008

NOTSRCCOPY

 

0 1 0 0 0 1 0 0

S & ~D

0x440328

SRCERASE

 

0 1 0 1 0 1 0 1

~D

0x550009

DSTINVERT

 

0 1 0 1 1 0 1 0

P ^ D

0x5A0049

PATINVERT

 

0 1 1 0 0 1 1 0

S ^ D

0x660046

SRCINVERT

 

1 0 0 0 1 0 0 0

S & D

0x8800C6

SRCAND

 

1 0 1 1 1 0 1 1

~S?#160;D

0xBB0226

MERGEPAINT

 

1 1 0 0 0 0 0 0

P & S

0xC000CA

MERGECOPY

 

1 1 0 0 1 1 0 0

S

0xCC0020

SRCCOPY

 

1 1 1 0 1 1 1 0

S?#160;D

0xEE0086

SRCPAINT

 

1 1 1 1 0 0 0 0

P

0xF00021

PATCOPY

 

1 1 1 1 1 0 1 1

P?#160; ~S?#160; D

0xFB0A09

PATPAINT

 

1 1 1 1 1 1 1 1

1

0xFF0062

WHITENESS

此表格对于理解和使用位映像操作非常重要,因此我们应花点时间来研究。

在这个表格中,「ROP代码」行的值将传递给BitBlt或StretchBlt的最后一个参数;在「名称」行中的值在WINGDI.H定义。ROP代码的低字组协助设备驱动程序传输位映像操作。高字组是0到255之间的数值。此数值与第2列的图案的位相同,这是在图案、来源和显示在顶部的目的之间进行位操作的结果。「布尔运算」列按C语法显示图案、来源和目的的组合方式。

要开始了解此表,最简单的办法是假定您正处理一个单色系统(每图素1位)其中0代表黑色,1代表白色。BLACKNESS操作的结果是不管是来源、目的和图案是什么,全部为零,因此目的将显示黑色。类似地,WHITENESS总导致目的呈白色。

现在假定您使用位映像操作PATCOPY。这导致结果位与图案位相同,而忽略了来源和目的位图。换句话说,PATCOPY简单地将目前图案复制给了目的矩形。

PATPAINT位映像操作包含一个更复杂的操作。其结果相同于在图案、目的和反转的来源之间进行位或操作。当来源位图是黑色(0)时,其结果总是白色(1);当来源是白色(1)时,只要图案或目的为白色,则结果就是白色。换句话说,只有来源为白色而图案和目的都是黑色时,结果才是黑色。

彩色显示时每个图素都使用了多个位。BitBlt和StretchBlt函数对每个颜色位都分别提供了位操作。例如,如果目的是红色而来源为蓝色,SRCPAINT位映像操作把目的变成洋红色。注意,操作实际是按显示卡内储存的位执行的。这些位所对应的颜色取决于显示卡的调色盘的设定。Windows完成了此操作,以便位映像操作能达到您预计的结果。不过,如果您修改了调色盘(我将在第十六章讨论),位映像操作将产生无法预料的结果。

如要得到位映像操作较好的应用程序,请参见本章后面的「非矩形位图图像」一节。

图案Blt

除了BitBlt和StretchBlt以外,Windows还包括一个称为PatBlt (「pattern block transfer:图案块传输」)的函数。这是三个「blt」函数中最简单的。与BitBlt和StretchBlt不同,它只使用一个目的设备内容。PatBlt语法是:

PatBlt (hdc, x, y, cx, cy, dwROP) ;

x、y、cx和cy参数字于逻辑单位。逻辑点(x,y)指定了矩形的左上角。矩形宽为cx单位,高为cy单位。这是PatBlt修改的矩形区域。PatBlt在画刷与目的设备内容上执行的逻辑操作由dwROP参数决定,此参数是ROP代码的子集-也就是说,您可以只使用那些不包括来源目的设备内容的ROP代码。下表列出了PatBlt支持的16个位映像操作:

表14-5

 

图案(P):1 1 0 0

目的(D):1 0 1 0

布尔操作

ROP代码

名称

结果:

0 0 0 0

0

0x000042

BLACKNESS

 

0 0 0 1

~(P | D)

0x0500A9

  

0 0 1 0

~P & D

0x0A0329

  

0 0 1 1

~P

0x0F0001

  

0 1 0 0

P & ~D

0x500325

  

0 1 0 1

~D

0x550009

DSTINVERT

 

0 1 1 0

P ^ D

0x5A0049

PATINVERT

 

0 1 1 1

~(P & D)

0x5F00E9

  

1 0 0 0

P & D

0xA000C9

  

1 0 0 1

~(P ^ D)

0xA50065

  

1 0 1 0

D

0xAA0029

  

1 0 1 1

~P | D

0xAF0229

  

1 1 0 0

P

0xF00021

PATCOPY

 

1 1 0 1

P | ~D

0xF50225

  

1 1 1 0

P | D

0xFA0089

  

1 1 1 1

1

0xFF0062

WHITENESS

下面列出了PatBlt一些更常见用途。如果想画一个黑色矩形,您可呼叫

PatBlt (hdc, x, y, cx, cy, BLACKNESS) ;

要画一个白色矩形,请用

PatBlt (hdc, x, y, cx, cy, WHITENESS) ;

函数

PatBlt (hdc, x, y, cx, cy, DSTINVERT) ;

用于改变矩形的颜色。如果目前设备内容中选择了WHITE_BRUSH,那么函数

PatBlt (hdc, x, y, cx, cy, PATINVERT) ;

也改变矩形。

您可以再次呼叫FillRect函数来用画笔充满一个矩形区域:

FillRect (hdc, &rect, hBrush) ;

FillRect函数相同于下列代码:

hBrush = SelectObject (hdc, hBrush) ;

PatBlt (hdc, rect.left, rect.top,

rect.right - rect.left,

rect.bottom - rect.top, PATCOPY) ;

SelectObject (hdc, hBrush) ;

实际上,此程序代码是Windows用于执行FillRect函数的动作。如果您呼叫

InvertRect (hdc, &rect) ;

Windows将其转换成函数:

PatBlt (hdc,       rect.left, rect.top,

rect.right - rect.left,

rect.bottom - rect.top, DSTINVERT) ;

在介绍PatBlt函数的语法时,我说过点(x,y)指出了矩形的左上角,而且此矩形宽度为cx单位,高度为cy单位。此叙述并不完全正确。BitBlt、PatBlt和StretchBlt是最合适的GDI画图函数,它们根据从一个角测得的逻辑宽度和高度来指定逻辑直角坐标。矩形边框用到的其它所有GDI画图函数都要求根据左上角和右下角的坐标来指定坐标。对于MM_TEXT映像模式,上面讲述的PatBlt参数就是正确的。然而对于公制的映像模式来说,就不正确。如果您使用一的cx和cy值,那么点(x,y)将是矩形的左下角。如果希望点(x,y)是矩形的左上角,那么cy参数必须设为矩形的负高度。

如果想更精确,用PatBlt修改颜色的矩形将通过cx的绝对值获得逻辑宽度,通过cy的绝对值获得逻辑高度。这两个参数可以是负值。由逻辑点(x, y)和(x + cx, y + cy)给定的两个角定义了矩形。矩形的左上角通常属于PatBlt修改的区域。右上角则超出了矩形的范围。根据映像模式和cx、cy参数的符号,矩形左上角的点应为(x, y)、(x, y + cy)、(x + cx, y)或者(x + cx, y + cy)。

如果给MM_LOENGLISH设定了映像模式,并且您想在显示区域左上角的一小块正方形上使用PatBlt,您可以使用

PatBlt (hdc, 0, 0, 100, -100, dwROP) ;

PatBlt (hdc, 0, -100, 100, 100, dwROP) ;

PatBlt (hdc, 100, 0, -100, -100, dwROP) ;

PatBlt (hdc, 100, -100, -100, 100, dwROP) ;

给PatBlt设定正确参数最容易的方法是将x和y设为矩形左上角。如果映像模式定义y坐标随着向上卷动显示而增加,那么请使用负的cy参数。如果映像模式定义x坐标向左增加(很少有人用),则需要使用负的cx参数。