您的位置:首页 > 其它

14.3 位块传输

2015-11-25 19:23 309 查看
摘录于《Windows程序(第5版,珍藏版).CHarles.Petzold 著》P511

        如前所述,可以把整个视频显示当成一个大的位图。你在屏幕上看到的像素被表示为位,存在视频显示适配板卡的内存中。视频显示的任何矩形区域也是一个位图,它的尺寸是它所包含的行数和列数。

        让我们把图像从视频显示的一个区域复制到另一个区域,一次来开始我们位图世界的旅程吧!要完成这个任务依靠功能强大的 BitBlt 函数就可以了。

        BitBlt(读作“bit blit”)表示“位块传输”。BLT 来自于 DEC PDP-10 机器上用来做内存块传输的编译语言指令。“bitblt”这个名称最早用于图形时在施乐帕洛阿尔托研究中心(PARC)所涉及的
SmallTalk 系统中。在 SmallTalk 中,所有的图形输出操作都是基于 bitblt 的。程序员有时把“blt”用作动词,就像在这句话中用到的那样:“然后我写了一些代码把笑脸 blt 到屏幕上,并播放一个波形文件。”

        BitBlt 函数是像素移动者,或者,更生动地说,点阵撞击着。你很快会看到,“传输”这个词并不能精确描述 BitBlt 函数。它实际上是对像素进行位与位之间的操作,并可以营造出很多有趣的效果。

14.3.1  简单的 BitBlt

        BITBLT 程序用 BitBlt 函数把程序的系统菜单图标(在程序窗口的左上角)复制到它的客户区。

/*------------------------------------------
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-2 所示。



图 14-2  BITBLT 的显示

        BitBlt 函数把像素从一个设备环境(叫做“源”)的矩形区域,传输到另一个设备环境(也就是“目标”)中的一个同样大小的矩形区域。这个函数的语法如下:

BitBlt(hdcDst, xDst, yDst, cx, cy, hdcSrc, xSrc, yScr, dwROP);
源和目标设备可以是一样的。

        在 BITBLT 程序中,目标设备环境是窗口的客户区;设备环境的句柄是从 BeginPaint 函数中获得的。源设备环境环境是应用程序的整个窗口;设备环境句柄是用 GetWindowDC 获得的。显然这两个设备环境指的是同一个物理设备(视频显示)。然而,这两个设备环境的坐标原点不一样。

        xSrc 和 ySrc 参数指定源图像的左上角的坐标位置。在 BITBLT 中,这两个参数被设为 0,表示图像从源设备环境的左上角开始(也就是整个窗口)。cx 和 cy 参数指定图像的宽和高。BITBLT 使用从 GetSystemMetrics 函数中获得的信息来计算这些值。

        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 中最重要的限制是两个设备环境一定要“兼容”。也就是说要么两者中有一个是单色的,要么两者都要有同样的像素位数。简而言之,你没法靠把屏幕上的东西 blt 到打印设备环境中来得到硬拷贝。

14.3.2  拉伸位图

        在 BitBlt 函数中,目标图像跟源图像大小一样,是因为函数只有两个参数来指定宽和高。如果想在复制时拉伸或压缩图像的大小,可以用 StretchBlt 函数:

StretchBlt (hdcDst, xDst, yDst, cxDst, cyDst,
hdcSrc, xSrc, ySrc, cxSrc, cySrc, dwROP);
这个函数加了两个参数。它现在包括目标和源各自的宽和高。STRETCH 程序演示了 StretchBlt 函数的用法。

/*------------------------------------------
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_CXSIZE) -
GetSystemMetrics(SM_CYSIZEFRAME);

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-4 所示的那样。

        BitBlt 和 StretchBlt 函数的所有坐标和尺寸都是基于逻辑单位的。但如果你在 BitBlt 函数中有两个不同的设备环境,他们指向同一个物理设备,但是使用了不同的映射方式,那会怎么样?如果这样的话,对 BitBlt 的调用就会产生歧义:cx 和 cy 参数是用逻辑单位,它们对源设备环境和目标设备环境中的矩形同时有效。所有的坐标和尺寸在真正的位传输发生前必须被转成设备坐标。因为
cx 和 cy 被同时用在源和目标设备环境中,它们的值必须被独立的转成每个设备环境的设备单位(像素)。



图 14-4  STRETCH 显示

        当源和目标设备环境相同的时候,或是两个设备环境都用 MM_TEXT 映射模式时,在两个设备环境中的以设备单位表示的矩形的大小也会一样。Windows 就能进行简单的位到位的传输。但是,当以设备单位表示的矩形的大小在设备环境中不一样时,Windows 就会调用更灵活的 StretchBlt 函数。

        StretchBlt 还能让你横向或纵向地翻转图像。如果 cxSrc 和 cxDst 的符号(当转成设备单位时)不一样,StretchBlt 就会生成一个镜像图像: 左变成右,右变成左。你在 STRETCH 程序中将 xDst 参数改成 cxClient,把 cxDst 参数改成 -cxClient,就能验证这个功能。如果 cySrc 和 cyDst 不同,StretchBlt 会把图像上下翻转。你在
STRETCH 程序中将 yDst 参数改成 cyClient,把 cyDst 参数改成 -cyClient,就能检验这个功能。

14.3.3  StretchBlt 模式

        缩放位图总会带来些问题,StretchBlt 函数也不例外。当放大一个位图时,StretchBlt 必须复制一些像素行或列。如果放大不是整数倍,这个过程会造成图像失真。

        如果目标矩形比源矩形要小,StretchBlt 必须缩小图像,把两行或多行,或是两列或多列的像素合并成一行或一列。取决于设备环境的拉伸模式属性,这个操作有四种方法。你可以用 SetStretchBltMode 函数来改变这个属性:

SetStretchBltMode (hdc, iMode);
以下为 iMode 的可取值。

BLACKONWHITE 或 STRETCH_ANDSCANS(默认值) 
如果两个或多个像素必须被结合成一个像素,StretchBlt 将对像素进行逻辑与操作。产生的像素只有当所有的初始像素都是白时,才是白色。也就是说黑色像素比白色像素占优势。这对以白色为底,图像主要是黑色的单色位图来说比较好。
WHITEONBLACK 或 STRETCH_ORSCANS  如果两个或多个像素必须被结合成一个像素,StretchBlt 将对像素进行逻辑或操作。产生的像素只有当所有的初始像素都是黑时,才是黑色。也就是说白色像素比黑色像素占优势。这对以黑色为底,图像主要是白色的单色位图来说比较好。
COLORONCOLOR 或 STRETCH_DELETESCANS  StretchBlt 只是简单地去掉像素行或列,而不做任何逻辑操作。这对彩色位图常常是最佳的方法。
HALFONE 或 STRETCH_HALFTONE  Windows 根据要结合的源的颜色,计算平均目标颜色。它和半色调调色板一起使用,如在第 16 章所演示的那样。
        Windows 还提供了 GetStretchBltMode 函数来取得当前的拉伸模式。

14.3.4  光栅操作

        BITBLT 和 STRETCH 程序只是简单地把源位图复制到目标位图,在这个过程中也许会拉伸它。这是 BitBlt 和 StretchBlt 函数指定最后一个参数为 SRCCOPY 的结果。而 SRCCOPY 只是在这些函数中你能够使用的 256 种光栅操作中的一种。让我们先在 STRETCH 程序中试试其他几种,然后再系统地来研究光栅操作。

        先试着把 SRCCOPY 换成 NOTSRCCOPY。就像这个名称所提示的那样,这个光栅操作在复制位图时把颜色反转了。在窗口客户区,所有的颜色都会变成反色。黑色变成了白色,白色变成黑色,蓝色变成了黄色。再试试 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(SelectObject(hdcClient, GetStockObject(WHITE_BRUSH)));
这次你能看到图像上面好像叠加了一个阴影图案。这到底发生了什么呢?

        就像我之前提到过的,BitBlt 和 StretchBlt 函数不是简单地进行位块传输。这些函数实际上在以下三个图像之间做位与位的操作:

源:源位图被拉伸或压缩成(如果有必要的话)目标矩形同样的大小。
目标:在 BitBlt 或 StretchBlt 调用之前的目标矩形。
图案(pattern):在目标设备上当前所选的画刷,在横向和纵向上不断重复,直到大小和目标矩形的一样。
运算的结果被复制到目标矩形中。

        光栅操作在概念上跟第 5 章见到的绘图模式类似。绘图模式决定图形对象,如一条线,怎样跟目标相结合。你可能还记得有 16 种绘图模式——也就是,当所绘制的对象中的 0 和 1 跟目标的 0 和 1 相结合时,所能得到的所有不同结果。

        BitBlt 和 StretchBlt 中用到的光栅操作涉及三个对象的结合,这样我们得到 256 种操作。也就是说,我们有 256 种方法来把源位图、目标位图和图案结合起来。这其中的 15 种光栅操作都有名称,其中有些很难懂,而这些名称都定义在 WINGDI.H 中。其他的则是数值,在 /Platform SDK/Graphics and Multimedia Services/GDI/RasterOperation
Codes/Ternary Raster Operations 列出。

        有名称的 15 种光栅操作(ROP)代码列表如下:

图案  (P):

源  (S):

目标  (D):
1 1 1 1 0 0 0 0

1 1 0 0 1 1 0 0

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 | 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
60 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 | D 0xBB0226 MERGEPAINT
 1 1 0 0 0 0 0 0 P & S 0xC000CA MERGECOPY
111 1 0 0 1 1 0 0 S 0xCC0020 SRCCOPY
 1 1 1 0 1 1 1 0 S | D 0xEE0086 SRCPAINT
 1 1 1 1 0 0 0 0 P 0xF00021 PATCOPY
 1 1 1 1 1 0 1 1 P | ~S | D 0xFB0A09 PATPAINT
 1 1 1 1 1 1 1 1 1 0xFF0062 WHITENESS
这张表对理解和使用光栅操作很重要,让我们花点时间来研究它。
        在这表中,ROP 代码栏的值是我们传给 BitBlt 或 StretchBlt 的最后一个参数的数值。在名称栏中的名称在 WINGDI.H 中被定义为这些值。ROP 代码的低位字的数值帮助设备驱动来完成光栅操作。高位字则是一个介于0 和 255 的数值。这个值跟在第二栏中显示的位样式一样,它是在图案、源和目标的位之间的位操作的结果。逻辑操作栏用 C 语言的语法显示了图案、源和目标是怎样被结合在一起的。

        要想理解这张表,让我们假设你在使用单色系统(每像素 1 位),0 代表黑色,1 代表白色,这样会简单一些。BLACKNESS 造成的结果是全零,不管源、目标和图案是什么样的,目标都会被涂成黑色。同样,WHITENESS 总是会把目标涂成白色。

        现在假设你在用光栅操作 PATCOPY。这会造成结果位跟图案位一样。而源和目标位会被忽略。换句话说,PATCOPY 总是简单地把当前图案复制到目标矩形中。

        PATPAINT 光栅操作则涉及稍复杂的操作。结果会跟在图案、目标和源的反转之间进行逻辑位或操作的结果一样。如果源位图是黑色的(位 0),结果会总是白色(位 1)。当源是白色,如果图案或者目标是白色,那么结果也会是白色。换句话说,只有在源是白色,而且图案和目标两者都是黑色时,目标才会是黑色。

        彩色显示每个像素都使用多位。BitBlt 和 StretchBlt 函数对这些彩色位的每一位进行位与位的操作。比如,如果目标是红色的,而源是蓝色,SRCPAINT 光栅操作会把目标涂成洋红色。请记住,这些操作都是对实际存储在视频卡内存中的位进行的。而这些位怎样对应到颜色取决于视频卡的调色板是如何设置的。Windows 这么做是为了使光栅操作像你预期的那样工作。但是,如果改变调色板的话(像即将在第 16 章里讨论的那样),光栅操作会产生意外的结果。

        本章后面的 14.4.10 节讲述了一个很好的使用了光栅操作的应用程序。

14.3.4  图案 Blt

        除了 BitBlt 和 StretchBlt,Windows 还有一个称为 PatBlt(“图案块传输”)的函数。这个函数是三个“blt”函数里最简单的一个。与 BitBlt 和 StretchBlt 不同,PatBlt 只使用目标设备环境。PatBlt 的语法如下:

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


        x, y, cx 和 cy 参数都是基于逻辑单位的。逻辑坐标点(x, y)规定了矩形的左上角。矩形宽度为 cx 个单位,高度为 cy 个单位。这是 PatBlt 更改的矩形区域。PatBlt 在画刷和目标设备环境上执行的逻辑操作由 dwROP 参数决定。dwROP 是 ROP 代码的子集,也就是说,你只能用那些不涉及源设备环境的 ROP 代码。下表给出了 PatBlt 函数支持的 16 种光栅操作。

图案  (P):

目标  (D):
1 1 0 0

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 
60 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 
111 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 
161 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);


        回顾以下 FillRet 函数,它用一个画笔填充一块矩形区域:

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 个单位。这个说法并不完全准确。在 GDI 绘图函数里,只有 BitBlt、PatBlt 和 StretchBlt 以从单个顶点开始测量的逻辑宽度和高度规定了逻辑矩形坐标。GDI 的所有其他使用矩形边框的绘图函数都要求使用左上角和右下角坐标。对于 MM_TEXT
映射模式,以上对 PatBlt 函数参数的描述是准确的。但是,对于度量映射模式,该描述并不精准。如果 cx 和 cy 值是正的,坐标点(x, y)则是矩形的左下角。如果想让(x, y)称为矩形的左下角,参数 cy 必须设置成矩形高度的负值。

        更准确地说,PatBlt 填色的矩形有一个逻辑宽度,即 cx 的绝对值,它还有一个逻辑高度,即 cy 的绝对值。这两个参数可以是负数。矩形由两个顶点定义,分别是逻辑坐标点(x, y)和(x+cy, 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 参数需要取负值。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: