第5章 绘图基础_5.1-5.4 GDI绘图

时间:2023-03-09 18:43:37
第5章 绘图基础_5.1-5.4 GDI绘图
5.1 GDI的原理和结构

(1)提供一种特殊机制彻底隔离应用程序与不同输出设备(eg.显示器或打印机),以便支持 与设备无关的图形。

光栅设备(如显示器、激光打印机):图像是由点构成的矩阵

图形输出设备

矢量设备(如绘图仪):使用 线条来绘制图形

(2)Windows GDI允许使用逻辑坐标系统来保证程序与硬件的独立,也可以统用设备坐标系统 (单位:像素)来迎合硬件的需求。

(3)GDI总体上是一个静态显示系统,对动画的支持有限。DirectX可支持动画。

5.2 设备环境

5.2.1 获取设备环境句柄

(1)在WM_PAINT中获取的是无效区的句柄

hDC = BeginPaint(hWnd,&ps);

//其他代码

EndPaint(hWnd,&ps);

(2)在非WM_PAINT中

获取整个客户区DC

获取整个窗口DC(含非客户区)

hDC = GetDC(hWnd);

//GetDC(NULL)时为屏幕DC

//其他代码

Release(hWnd,hDC);

hDC = GetWindowDC(hWnd);

//其他代码

Release(hWnd,hDC);

(3)更通用的方法(未必一定要窗口相关联,也可以是内存或打印机的DC)

  //①整个屏幕DC

  hDC =CreateDC(TEXT(“DISPLAY”),NULL,NULL,NULL);

  DeleteDC(hDC);

  //②内存DC

  hdcMem= CreateCompatibleDC(hDC);

  DeleteDC(hdcMem);

  //③获得图元文件的设备环境句柄

  hdcMeta= CreateMetaFile(pszFileName);

  hmf =CloseMetaFile(hdcMeta);

(4)只需要获取设备环境信息,而不在其上绘制:CreateIC——(Information Context)

5.2.2 获取设环境的信息

(1)设备的分辨率

  ①显示器:每毫米(或英寸)的像素总数。

  ②打印机:每毫米(或英寸)的点数,1磅≈1/72英寸。

(2)GetDeviceCaps(hdc,iIndex)

iIndex

iValue(返回值)

备注

HORZRES

以像素为单位的设备宽度

1、当hdc为整个屏幕DC时,与GetSystemMetrics获得的信息一致。

2、当hdc为打印机时,获得的是打机印显示区域的宽度和高度。

VERTRES

以像素为单位的设备高度

HORZSIZE

以毫米为单位的屏幕宽度

1、Windows98下:

HORZSIZE = 25.4×HORZRES/LOGPIXELSX.

VERTSIZE = 25.4×VERTRES/LOGPIXELSY

2、Windows NT下:为“标准显示器”的大小,即320×240,但可通过上述公 式算出这两个真实的值出来。

3、对于打印机是物理尺寸,对于显示器是逻辑尺寸。

VERTSIZE

以毫米为单位的屏幕高度

LOGPIXELSX

每英寸的水平像素数

1、对于打印机:可以用上述公式运算出来。

2、对于显示器:与Windows设置的字体有关:正常字体96dpi,大字体为120dpi

LOGPIXELSX

每英寸的垂直像素数

5.2.3 关于GetDeviceCaps的扩展学习

(1)屏幕物理分辨率与屏幕分辨率的区别

  ①物理分辨率:屏幕的最高分辨率,显示器一生产出来,就固定下来了。

  ②屏幕像素规模(HORZRES和VERTRES)与屏幕分辨率(屏幕dpi)

  A、屏幕像素规模:如1024×768

  B、屏幕dpi = 25.4×HORZRES/HORZSIZE或 25.4×VERTRES/VERTSIZE

  屏幕dpi是可改变。降低像素规模时(如1024×768改成800×600),dpi可能会变化,也可能不变。经测试,台式机和笔记本上dpi变化有所不同。

显示效果

说明

笔记本

LED显示器

屏幕显示区域缩窄,但像素大小不变

同时影响HORZRES和HORZSIZE, 但dpi不变

台式机

CRT显示器

屏幕显示区域不变,但像素增大

(LED某些分辨率下也会出现)

只影响HORZRES,屏幕dpi变大

  ③下面以台式机CRT显示器为例,讨论屏幕dpi变化的情况下,显示的图像大小变化:

  如15.6英寸(13.5×7.6)在全屏幕模式下,当设置为1366×768像素时,dpi 为100。当设置683×384下,dpi为50。明显,屏幕上每个像素大小,第2种情况应比第1种大 1倍,因为GPU会把屏幕上的4个像素当1个像素(2×2)用。这就导致了显示的图像变大、变 模糊了。当然,如果是在笔记本显示器下测试,因dpi不变,显示出来的图像大小也就不变了。因 此当改变了屏幕像素规模时,一定要看下屏幕dpi是否变化,才能判断出显示出来的图像大小是否 有变化。

(2)逻辑dpi(=LOGPIXELSX或LOGPIXELSY),Windows下默认为96dpi。

  ①为什么要使用逻辑dpi,而不直接使用屏幕实际的分辨率

  因为早期显示器并不存储物理尺寸等信息,所以Windows无法获取显示器的真实尺寸,也 就没办法得到屏幕分辨率(dpi),这将导致windows在输出时,不知该将1英寸长度的物体,转为 多少个像素输出。所以只能采用市面上常见的显示器的分辨率,硬性规定一个默认值(如Windows 下为96dpi),如此便可以将1英寸长度转化为96个像素输出(注意,这时屏幕上看到的那段长度 不一定就是严格的1英寸长,因为96个像素被输往屏幕后,要根据屏幕上每个像素的实际大小来算 出显示的实际长度,而屏幕像素大小要通过其dpi计算得到)。现在由于EDID技术,将物理尺寸等 信息直接存进显示器,所以将物体按屏幕实际dpi(不是逻辑dpi)显示将成为一种趋势,因为这 可以让我们的显示器按照物体的真实尺寸来显示的。

  ②逻辑dpi的含义:将1英寸的长度转化为相应的像素,以这样的像素量输出。

  如:10磅字体,在逻辑96dpi下将被转化为10/72*96,即13个像素输出;在120dpi下将被 转化为16个像素输出。至于在屏幕上这96个像素(或120)显示出来的实际大小,要根据屏幕dpi 去算出来。通常,在屏幕dpi相等的情况下(如1024×768,设dpi为100),逻辑dpi为120时 显示出来的字要更大,因为13<16。这两种字体在屏幕上的实际长度分别为13/100英寸和 16/100英寸。

  ③改变屏幕dpi与改变逻辑dpi的不同。

实验:将96px×96px,物理尺寸为1×1英寸蓝色方块输往屏幕

A、将屏幕dpi由200dpi→100dpi

(如1600×1200→800×600,假设这两种情况都是全屏下。否则,这时windows 会调整桌面大小,而让屏幕dpi保持不变)

改变了屏幕dpi,这时意味着屏幕上每个像素变大了,所以显示出来方块会比实际的1×1 英寸大。

B、将逻辑dpi由96→120。

(在相同屏幕dpi下,如1024×768下,只调整逻辑dpi的值)

1、注意这时屏幕像素大小并没变,因为屏幕dpi没变化。

2、改变了逻辑dpi,意味着要将1英寸长度转为120个像素输出。注意到了吗,Windows偷偷地 将方块像素比增加了,从96px×96px方块调大成120px×120px方块输出。所以,显示 出来的图形也就比原来的更大了。

5.2.4 色彩ABC

  #define RGB(r,g,b) ((COLORREF)(((BYTE)(r))| \

           ((WORD)((BYTE)(g))<<8))| \

(((DWORD)(BYTE)(b))<<16)))

5.2.5 保存设备环境属性

(1)GetDC或BeginPaint返回的设备环境属性值是默认的。ReleaseDC或EndPaint后,所做的 任何改变都会丢失。

(2)如何解决?

  ①注册窗口类时wndClass.style=CS_HREDRAW | CS_VREDRAW | CS_OWNDC,这样这样基于 这个窗口类创建的窗口都有它私有的设备环境。CS_OWNDC只影响通过GETDC和BeginPaint获得的DC ,通过其他函数(如GetWindowDC)获得的设备环境并不受影响。

  ②在WM_CREATE消息初始化设备环境的属性

  case WM_CREATE:

    hdc = GetDC(hWnd);

    //初始化各个属性

    ReleaseDC(hWnd,hdc);

(3)保存与恢复

  idSaved= SaveDC(hdc); //可多次save,并保存在不同的id中。

  RestoreDC(hdc,idSaved);//RestoreDC(hdc,-1)为恢复到最近一次保存的状态。

5.3 点和线的绘制

5.3.1 设定像素

  SetPixel(hdc,x,y,crColor);

  COLORREF crColor = GetPixel(hdc,x,y);

5.3.2 直线

(1)MoveTo和MoveToEx

  MoveTo返回值是DWORD型,用于表示运行函数前的当前位置(x,y)——早期 windows。而现在的坐标(x,y)都为32位的,所以该函数不能现在的windows中返回正确的坐标。

  MoveToEx返回值BOOL。MoveToEX(hdc,x,y,&pt),第三个参数为运行该函数前的当前 位置。不需要为NULL。即MoveToEX(hdc,x,y,NULL)——现在Windows中。

(2)获取当前位置:GetCurrentPosition(hdc,&pt)

(3)LineTo、PolyLineTo等带To的函数会把最后一次的终点设为当前位置。

(4)PolyLine和PolyLineTo的区别

POINT apt[] ={,,,,,,,,,} //首尾点相同
int iArrayLen = sizeof(apt)/sizeof(POINT); //用 MoveToEx和LineTo绘制
MoveToEx(hdc,apt[].x,apt[].y,NULL)
for(int i=;i<iArrayLen;i++)
{
LineTo(hdc,apt[i].x,apt[i].y);
} //用 PolyLine绘制,并不改变当前位置。
PolyLine(hdc,apt,iArrayLen);//最后一个参数表示要绘制的点的个数。 //用 PolyLineTo绘制,要将当前位置作为起点,绘制完后,会改变当前位置。
MoveToEx(hdc,apt[].x,apt[].y,NULL);//从当前绘制为起点
PolyLine(hdc,apt+,iArrayLen-); //画下后面的点。

【SINWAVE程序】

/*------------------------------------------------------------
SINEWAVE.C -- Sine Wave Using Polyline
(c) Charles Petzold, 1998
------------------------------------------------------------*/
#include <windows.h>
#include <math.h> #define NUM 1000
#define TWOPI (2*3.14159) LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("SinWave");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = ;
wndclass.cbWndExtra = ;
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 ;
} hwnd = CreateWindow(szAppName, // window class name
TEXT("SinWave"), // window caption
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
hInstance, // program instance handle
NULL); // creation parameters ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd); while (GetMessage(&msg, NULL, , ))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
static int cxClient, cyClient;
POINT apt[NUM];
switch (message)
{
case WM_CREATE: return ;
case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
return ;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps); // 画x轴
MoveToEx(hdc, , cyClient / , NULL);
LineTo(hdc, cxClient, cyClient / ); // 计算坐标
for (int i = ; i < NUM; i++)
{
apt[i].x = i * cxClient / NUM;
apt[i].y = (int)(cyClient / * ( - sin(TWOPI * i / NUM)));
}
// 画正弦曲线
Polyline(hdc, apt, NUM); //调用一次且在设备驱动程序层面上,比调用1000次LineTo要快很多 EndPaint(hwnd, &ps);
return ; case WM_DESTROY:
PostQuitMessage();
return ;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}

5.3.3 边框绘制函数

(1)边界偏差(off-by-one):Windows在“边框”内绘图,即只画右坐标与底坐 标之前的点。即不包含右坐标与底坐标的点。

第5章 绘图基础_5.1-5.4 GDI绘图

(2)图解边框绘图函数

 第5章 绘图基础_5.1-5.4 GDI绘图

【LineDraw】

/*------------------------------------------------------------
LINEDRAW.C -- Line-Draw Demonstration Program
(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("LineDemo");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = ;
wndclass.cbWndExtra = ;
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 ;
} hwnd = CreateWindow(szAppName, // window class name
TEXT("LineDraw Demo"), // window caption
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
hInstance, // program instance handle
NULL); // creation parameters ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd); while (GetMessage(&msg, NULL, , ))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
static int cxClient, cyClient;
switch (message)
{
case WM_CREATE: return ;
case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
return ;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps); //
Rectangle(hdc, cxClient / , cyClient / , * cxClient / , * cyClient / ); // 画对角线
MoveToEx(hdc, , , NULL);
LineTo(hdc, cxClient, cyClient);
MoveToEx(hdc, cxClient, , NULL);
LineTo(hdc, , cyClient);
Ellipse(hdc, cxClient / , cyClient / , * cxClient / , * cyClient / );
RoundRect(hdc, cxClient / , cyClient / , * cxClient / , * cyClient / , cxClient / , cyClient / ); EndPaint(hwnd, &ps);
return ; case WM_DESTROY:
PostQuitMessage();
return ;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}

5.3.4 贝塞尔样条曲线

(1)PolyBezier(hdc,apt,iCount)

①apt是个POINT结构的数组,前四点分别表示曲线起点、第一个控制点、 第二个控点、第二个点。随后的每一条贝塞尔样条曲线则只需要给出三个点,因为前一条贝塞尔 样条曲线的终点就是后一条的起点

②iCount=3*曲线的条数+1;

(2)PolyBezierTo函数把当前位置作为第一个起点,所以后面每画一条曲线只需再给3个点。 当函数返回时,把曲线终点设置为当前位置。

【Bezier】

/*------------------------------------------------------------
LINEDRAW.C -- Bezier Splines Demo
(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("Bezier");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = ;
wndclass.cbWndExtra = ;
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 ;
} hwnd = CreateWindow(szAppName, // window class name
TEXT("Bezier"), // window caption
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
hInstance, // program instance handle
NULL); // creation parameters ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd); while (GetMessage(&msg, NULL, , ))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
void DrawBezier(HDC hdc, POINT apt[])
{
//画贝 塞尔曲线
PolyBezier(hdc, apt, );
//画控 制线
MoveToEx(hdc, apt[].x, apt[].y, NULL);
LineTo(hdc, apt[].x, apt[].y);
MoveToEx(hdc, apt[].x, apt[].y, NULL);
LineTo(hdc, apt[].x, apt[].y);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
static int cxClient, cyClient;
static POINT apt[];
switch (message)
{
case WM_CREATE: return ;
case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
apt[].x = cxClient / ;
apt[].y = cyClient / ;
apt[].x = cxClient / ;
apt[].y = cyClient / ;
apt[].x = cxClient / ;
apt[].y = * cyClient / ;
apt[].x = * cxClient / ;
apt[].y = cyClient / ;
return ;
case WM_MOUSEMOVE: //鼠标移动时
case WM_LBUTTONDOWN: //左键按下时
case WM_RBUTTONDOWN: //右键按下时
if (wParam & MK_LBUTTON || wParam &MK_RBUTTON)
{
hdc = GetDC(hwnd);
SelectObject(hdc, GetStockObject(WHITE_PEN));
DrawBezier(hdc, apt); //用白色画刷重绘一遍,即擦除旧的曲线
if (wParam & MK_LBUTTON) //左键改变第1个控制点 {
apt[].x = LOWORD(lParam);
apt[].y = HIWORD(lParam);
}
if (wParam & MK_RBUTTON) //右键改变第2个控 制点
{
apt[].x = LOWORD(lParam);
apt[].y = HIWORD(lParam);
}
SelectObject(hdc, GetStockObject(BLACK_PEN));
DrawBezier(hdc, apt); ReleaseDC(hwnd, hdc);
}
return ;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps); DrawBezier(hdc, apt);
EndPaint(hwnd, &ps);
return ; case WM_DESTROY:
PostQuitMessage();
return ;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}

5.3.5 使用现有画笔(备用画笔——画笔宽度总 是为1)

(1)获取画笔句柄——3种默认画笔(WHITE_PEN、BLACK_PEN、NULL_PEN),其中 NULL_PEN表示不绘任何图形。比如,画矩形时只要填充区域,不要边框时,可用NULL_PEN。

  HPEN hPen= GetStockObject(WHITE_PEN);

(2)选入设备环境:

  HPEN hOldPen = SelectObject(hdc, (HGDIOBJ)hPen);

  HPEN hOldPen = SelectObject(hdc, GetStockObject(WHITE_PEN));

5.3.6 创建、选择和删除画笔

(1)使用GDI对象的规则

①当GDI对象被选入一个有效的设备环境时,不要删除它

②最终应删除你所创建的所有GDI对象

③不要删除备用库里的对象

HPEN hPen = CreatePen(PS_DASH, 1, RGB(255, 0, 0));//创建画笔

HPEN hOldPen = SelectObject(hdc, (HGDIOBJ)hPen); //选入画笔

//画图代码

SelectObject(hdc, (HGDIOBJ)hOldPen);   //恢复旧画笔

DeleteObject((HGDIOBJ)hPen); //删除画笔,上一行不能省略。

(2)hPen = CreatePen(iPenStyle,iWidth,crColor);

iPenStyle参数

iWidth

crColor

 第5章 绘图基础_5.1-5.4 GDI绘图

①当iWidth =0时,Windows会重设为1个像素

②对于PS_SOLID、PS_NULL、PS_INSIDEFRAME,iWidth表示画笔的宽度

③如果是虚线或点线,当iWidth>1时,会用实心画笔代替。

①设备画笔颜色,是COLORREF值。

②PS_INSIDEFRAME唯一能使用混合色的画笔样式,但要iWidth>1。

(3)hPen = CreatePenIndirect(&logpen);

LOGPEN结构(LOGPEN logpen)

lopnStyle

画笔样式

lopnWidth

画笔宽度,是一个POINT结构,x字段画笔的宽度,y字段被忽略

lopnColor

画笔颜色,COLORREF值。

(4)删除未被保存句柄的设备环境中的画笔——不能删除被选入设备环境句柄的

画笔!

  ① 方法1://选入默认画笔,返回的是设备中的画笔,再删除。

    DeleteObject(SelectObject(hdc,GetStockObject(BLACK_PEN)));

  ②方法2:

    hPen = SelectObject(hdc,CreatePen(PS_DASH),0,RGB(255,0,0));//自定义画笔

    DeleteObject(SelectObject(hdc,hPen));//返回自定义画笔句柄,并删除。

(5)获取画笔

  ①hPen =GetCurrentObject(hdc,OBJ_PEN); //获得当前设备环境中的画笔句柄

  ②GetObject(hPen,sizeof(LOGPEN),(LPVOID)&logpen);//画笔属性保存logpen中。

5.3.7 填充空隙——点式画笔或虚线画笔之间空

隙的颜色

空隙颜色由设备环境的背景模式和背景颜色决定(注意不是窗口的背景颜色

背景模式

OPAQUE(不透明)

TRANSPARENT(透明)

SetBkMode(hdc,TRANSPARENT);

int iMode = GetBkMode (hdc);

背景颜色

COLORREF值

SetBkColor(hdc,crColor);

COLORREF crColor = GetBkColor(hdc);

5.3.8 绘图模式(默认R2_COPYPEN)

(1)线条最终显示的颜色由画笔颜色及目标区域表面的颜色共同决定。

(2)光栅操作(rasteroperation,ROP):画笔的像素颜色与目标表面像素按位布尔运算。因

画线只涉及两种像素颜色(画笔与目标),也被称为二元光栅操作(ROP2)。

(3)16种不同的ROP2运算:如R2_COPYPEN、R2_MASKNOTPEN、R2_BLACK等。

(4)获取当前绘制模式和设置绘图模式:

  ①iDrawMode =GetROP2(hdc);

  ②SetROP2(hdc,iDrawMode);

5.4 绘制填充

5.4.1 画刷

(1)备用库画刷:6种(WHITE_BRUSH、LTGRAY_BRUSH、GRAY_BRUSH、DKGRAY_BRUSH、

BLACK_BRUSH和NULL_BRUSH)。

(2)使用画刷

    HBRUSHhBrush = GetStockObject(GRAY_BRUSH); //获取画刷

    SelectObject(hdc,hBrush);  //选入设备环境中

(3)应用举例——画矩形

不要边框线,只要填充

SelectObject(hdc,GetStockObject(NULL_PEN));

只绘边框,不要填充内部

SelectObject(hdc,GetStockObject(NULL_BRUSH));

5.4.2 Polygon函数和多边形填充模式

(1)Polygon和PolyPolygon函数

  ① Polygon(hdc,apt,iCount);

apt

POINT结构的数组,表示各个顶点

若最后一个点与第一个点不同,Windows会自动加1条连接这两个点的线,以形成闭合区域

iCount

顶点的个数

  ②PolyPolygon(hdc, ,aiCounts,iPolyCount);

apt

POINT结构的数组,表示所有的顶点

aiCounts

是个数组,每个元素表示1个多边形的顶点数。

iPolyCount

多边形的个数

  ③PolyPolygon的功能等价代码

for(int i=0,iAccum =0;i<iPolycount;i++)

{

Polygon(hdc,apt+iAccum,aiCounts[i]); //第i个多边形;

iAccum +=aiCounts[i];  //绘完第i个多边形后,顶点索引

后移;

}

(2)图解多边形的填充模式:ALTERNATE(交替)和WINDING(螺旋)

  SetPolyFillMode(hdc,iMode);

第5章 绘图基础_5.1-5.4 GDI绘图

第5章 绘图基础_5.1-5.4 GDI绘图

 【ALTWIND程序】
1、特征图形与填充效果图
第5章 绘图基础_5.1-5.4 GDI绘图

效果图

第5章 绘图基础_5.1-5.4 GDI绘图2、代码实现
/*------------------------------------------------------------
ALTWIND.C -- Alternate and Winding Fill Modes
(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("AltWind");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = ;
wndclass.cbWndExtra = ;
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 ;
} hwnd = CreateWindow(szAppName, // window class name
TEXT("Alternate And Winding Fill Modes"), // window caption
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
hInstance, // program instance handle
NULL); // creation parameters ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd); while (GetMessage(&msg, NULL, , ))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
static int cxClient, cyClient;
static POINT aptFigure[] = { , , , , , , , , , ,
, , , , , , , , , }; //特征图形
POINT apt[];
switch (message)
{
case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
return ;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
SelectObject(hdc, GetStockObject(GRAY_BRUSH));
// 左幅图ALTERNATE模式填充
for (int i = ; i < ; i++)
{
apt[i].x = cxClient * aptFigure[i].x / ;
apt[i].y = cyClient * aptFigure[i].y / ; //按比例放大,因为apt[i].y /cylient = aptFigure[i].y / 100;
}
SetPolyFillMode(hdc, ALTERNATE);
Polygon(hdc, apt, );
// 右幅图ALTERNATE模式填充
for (int i = ; i < ; i++)
{
apt[i].x += cxClient / ;
}
SetPolyFillMode(hdc, WINDING);
Polygon(hdc, apt, );
EndPaint(hwnd, &ps);
return ; case WM_DESTROY:
PostQuitMessage();
return ;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}

5.4.3 用画刷(8×8的小位图)填充内部

(1)创建逻辑画刷hBrush =CreateSolidBrush(crColor); //也可能是一个抖动位图

(2)阴影线标记的画刷hBrush = CreateHatchBrush(iHatchStyle,crColor);

iHatchStyle表示阴影线标记的外观

第5章 绘图基础_5.1-5.4 GDI绘图

说明:1、阴影线颜色由crColor指定。

2、阴影线之间的空隙由背景模式和背影颜色决定,由画笔 类似。

(3)用自己的位图画刷:CreatePatternBrush和CreateDIBPatternBrushPt。

(4)包含其他4个函数功能的(更强大):hBrush = CreateBrushIndirect (&logbrush);

LOGBRUSH结构体(三个字段,lbStyle 字段的值决定Windows如何解释其他两个字段)

lbStyle(UINT)

lbColor(COLORREF)

lbHatch(LONG)

BS_SOLID

画刷的颜色

被忽略

BS_HOLLOW

被忽略

被忽略

BS_HATCHED

阴影线的颜色

阴影线画刷的样式

BS_PATTERN

被忽略

位图的句柄

BS_DIBPATTERNPT

被忽略

指向DIB的指针

(5)获取画刷信息GetObject(hBrush,sizeof(LOGBRUSH), (LPVOID)&logbrush);