Windows程序设计 学习笔记 第五章 GDI(绘图基础)



一,GDI 简介

1. GDI 函数

GDI 包含几百个函数,可分为以下几大类:

  • 获取(或建立)和释放(或销毁)设备环境的函数
  • 获取设备环境信息的函数
  • 绘图函数
  • 设置和获取设备环境属性的函数
  • 使用GDI “对象” 的函数

2. GDI 基本图形

  • 线条和曲线
  • 可被填充的封闭区域
  • 位图
  • 文本

3.其他概念

  • 映射模式(mapping mode)和转换(transform)
  • 图元文件(metafile)
    图元文件是以二进制形式编码的GDI函数调用的集合
  • 区域(region)
  • 路径(path)
  • 剪裁(clipping)
  • 调色板(patettes)
  • 打印(printing)

二,设备环境

设备环境句柄(HDC)(32位无符号整数)

  • 是程序使用 GDI 函数的“通行证”
  • 设备环境的属性决定绘制的一些特性,如颜色
  • 当程序完成对客户区的绘制后,必须释放设备环境句柄(释放之后就无效了)
  • 必须在处理同一条消息时,获取HDC并释放HDC,不能在两条消息中传递一个HDC(唯一例外是CreateDC 创建的设备环境)。

1. 获取设备环境

1.1 :方法一:BeginPaint() 和 EndPaint()

处理 WM_PAINT 时使用。

case WM_PAINT:
	hdc=BeginPaint(hwnd,&ps);
		(using GDI functions)
	EndPaint(hwnd,&ps);
	return 0;

BeginPaint() 函数:擦去无效区域,填充ps结构,返回设备环境句柄。
EndPaint() 函数:释放设备环境句柄。

注意:
处理 WM_PAINT 消息时,使用无效矩形以避免不必要的GDI 函数调用(否则绘制时,需要GDI函数自己恢复无效区域,影响程序运行效率)

1.2 方法二: GetDC() 和 ReleaseDC()
hdc = GetDC(hwnd);
(使用 GDI 函数)
ReleaseDC(hwnd,hdc);

GetDC 返回设备环境句柄中的“裁剪”区是整个客户区,与无效矩形没有任何关系。也不会使无效区域有效化。

GetDC(NULL);获取整个屏幕设备环境

注意:
即使程序再处理非 WM_PAINT 消息时进行了绘制,他仍然必须收集足够的信息以便在收到 WM_PAINT 时能更新显示。

1.3 方法三:GetWindowDC() 和 ReleaseDC()

获取整个窗口的设备环境句柄

意味着可以在窗口的标题栏,菜单,滚动条和客户区外框输出,同样程序必须要处理 WM_NCPAINT(非客户区绘制)消息

hdc = GetWindowDC(hwnd);
	...
ReleaseDC(hwnd,hdc);
1.4 方法四:CreateDC() 和 DeleteDC()
hdc = CreateDC(pszDriver,pszDevice,pszOutput,pData);
	...
DeleteDC(hdc);

例如,获取整个屏幕的设备环境句柄:

hdc = CreateDC(TEXT("DISPLAY"),NULL,NULL,NULL);

1.5 只用于获取信息,不绘制——CreateIC()

获取一个“信息上下文”句柄,其参数与CreateDC相同

!!!不能用于绘制!!!

hdc = CreateIC(TEXT("DISPLAY"),NULL,NULL,NULL);
1.6 内存设备环境(处理位图时)
hdcMem = CreateConpatibleDC(hdc);
	...
DeleteDC(hdcMem);

将一个位图选入内存设备环境,并且调用GDI函数绘制这个位图

图元文件是以二进制形式编码的GDI函数调用的集合,可通过获取一个图元文件的设备环境来创建

hdcMeta = CreateMetaFile(pszFilename);
	...
hmf = CloseMetaFile(hdcMeta);

在图元文件设备环境有效时,使用hdcMeta 所做的任何GDI调用都不会被直接显示出来,它们都会变成图元文件的一部分,当调用 CloseMetaFile 时,图元文件设备环境句柄变为无效,,该函数返回一个图元文件的句柄(hmf),18章将详细讨论图元文件。

2. 获取设备环境的信息

iValue = GetDeviceCaps(hdc,iIndex);
2.1 设备的尺寸

本书中,“分辨率”被严格定义为 每度量单位(通常是英寸)中含有的像素数。
“像素规模”或“像素尺寸”表示设备在水平和垂直方向上显示的总的像素数。
“度量规模”或“度量尺寸”表示以英寸或毫米为单位的客户区域的大小。
像素尺寸/度量尺寸=分辨率

2.1.1 获取像素规模

可以调用 GetSystemMetrics()(使用SM_CXSCREEN和SM_CYSCREEN)参数来获取显示器的像素规模
还可以调用 GetDeviceCaps()(使用HORZRES 和 VEERTRES)获取同样的值。

2.1.2 获取逻辑像素

可以用 GetDeviceCaps()(使用 LOGPIXELSX 和 LOGPIXELSY)
获取“逻辑像素”,即“以每英寸的像素数计算的非实际分辨率”

2.1.3 获取逻辑尺寸

可以用 GetDeviceCaps()(使用 HORZSIZE 和 VERTSIZE)
获取“逻辑宽度”,“逻辑高度”,即“以毫米为单位的物理屏幕宽/高度”,其实是用HORZRES,VERTRES,LOGPIXELSX,LOGPIXELSY的值计算出来的,公式:
在这里插入图片描述
其实是标准显示器的尺寸,并不能反映当前显示器的尺寸
(像素规模是可以改的)

GetDeviceCaps 函数获得值与视频尺寸相关,
ASPECTX(每个像素点的相对宽度)
ASPECTY(每个像素点的相对高度)
ASPECTXY(每个像素点的相对对角线长度)(前两个平方和开根)

程序需要视频显示器的实际物理尺寸,最好的解决办法是让用户输入。

2.1.4 字符尺寸

字体字符大小又“点值”(也叫磅值point size),一点为1/72英寸。
10点值的字读起来舒服,行间距为13磅或12磅。

3.色彩ABC

3.1 获取视频适配器板卡上的内存组织形式

各个像素的多个色彩位可以在显卡内存中以顺序方式存储,也可以不同的色彩位被存放在内存的不同平面(plane)上

每个像素颜色需要的内存:

iBitsPixel = GetDeviceCaps(hdc,BITSPIXEL);

获取色彩平面的数目:

iPlanes = GetDeviceCaps(hdc,PLANES);

上面两个函数肯定有一个的返回值为1.
视频适配器支持的色彩数:iColor = 1<<(iPlanes*iBitPixel);

对于使用 NUMCOLORS 参数的 GetDeviceCaps 获取的颜色,只是Windows保留的色彩数,或对高彩和真彩返回-1。总之,其结果不准确,建议用上面的公式算

3.2 表示颜色

COLORREF(32位无符号长整型)来表示特定颜色,按 红,蓝,绿(各8位,最高8位为0)的顺序指定。
可以用 RGB 宏组合该值:

#define RGB(r,g,b)          ((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))

例:

RGB(255,255,0);

可利用“抖动”仿真更多颜色。
抖动就是用不同的色彩的相邻像素形成一个小的图案,可调用 GetNearestColor 确定与某种特殊颜色值最接近的非合成颜色:

crPureColor = GetNearestColor(hdc,crColor);

4,设备环境属性

设备环境属性默认值修改该值的函数取得该值的函数
Mapping ModeMM_TEXTSetMapModeGetMapMode
Window Origin(0, 0)SetWindowOrgEx OffsetWindowOrgExGetWindowOrgEx
Viewport Origin(0, 0)SetViewportOrgEx OffsetViewportOrgExGetViewportOrgEx
Window Extents(1, 1)SetWindowExtEx, SetMapMode, ScaleWindowExtExGetWindowExtEx
Viewport Extents(1, 1)SetViewportExtEx,SetMapMode,ScaleViewportExtExGetViewportExtEx
PenBLACK_PENSelectObjectSelectObject
BrushWHITE_BRUSHSelectObject\SelectObject
FontSYSTEM_FONTSelectObjectSelectObject
BitmapNoneSelectObjectSelectObject
Current Position(0, 0)MoveToEx,LineTo,PolylineTo,PolyBezierToGetCurrentPositionEx
Background ModeOPAQUESetBkModeGetBkMode
Background ColorWhiteSetBkColorGetBkColor
Text ColorBlackSetTextColorGetTextColor
Drawing ModeR2_COPYPENSetROP2GetROP2
Stretching ModeBLACKONWHITESetStretchBltModeGetStretchBltMode
Polygon Fill ModeALTERNATESetPolyFillModeGetPolyFillMode
Intercharacter Spacing0SetTextCharacterExtraGetTextCharacterExtra
Brush Origin(0, 0)SetBrushOrgExGetBrushOrgEx
Clipping RegionNoneSelectObject,SelectClipRgn,IntersectClipRgn,OffsetClipRgn,ExcludeClipRect,SelectClipPathGetClipBox

5.保存设备环境

情况一:释放设备环境时保存对属性的改变(默认释放HDC改变不被保存)
注册窗口类时 CS_OWNDC ,基于这个窗口类创建的窗口都有它的私有的设备环境,即使窗口被销毁,设备环境仍然存在。使用该样式只需初始化设备环境属性一次。(在处理WM_CREATE时),之后再再次改变这些值时,值保持不变。

CS_OWNDC 样式仅影响 GetDC 和 BeginPaint 获得的设备环境。

情况二:临时改变设备环境属性,绘制后再恢复原来的属性

idSaved = SaveDC(hdc);
...(改变属性,并绘制)
RestoreDC(hdc,idSaved);

还可以不保存返回值(类似于 栈):

SaveDC(hdc);
...(改变属性,并绘制)
RestoreDC(hdc,-1);//恢复最近一次SaveDC保存的状态

三,点和线的绘制

1. 设定像素

SetPixel :将某像素点设定为某个特定的颜色

SetPixel(hdc,x,y,crColor);

GetPixel:返回指定坐标像素点的颜色:

crColor = GetPixel(hdc,x,y);

2. 画线

Windows 可以绘制直线,椭圆弧线 和 贝塞尔样条曲线

Window98 支持的7中画线函数:

  • LineTo —— 画直线
  • Polyline 和 PolylineTo —— 画一条由多条首尾相连的直线构成的折线。
  • PolyPolyline —— 画多条折线
  • Arc —— 画椭圆弧线
  • PolyBezier 和 PolyBezierTo —— 画贝塞尔样条曲线

Windows NT 还支持:

  • ArcTo 和 AngleArc —— 画椭圆弧线
  • PolyDraw —— 画多条贝塞尔样条曲线 或 一条由多条首尾相连直线构成的折线

既画线又填充 的函数:

  • Rectangle —— 画矩形
  • Ellipse —— 画椭圆
  • RoundRect —— 画圆角矩形
  • Pie —— 画椭圆的一部分,使其看起来像一个扇形
  • Chord —— 画出由弦分割出的部分椭圆,形状呈弓形

设备环境有 5 个属性会影响这些函数绘制的线条外观:

  • ① 当前画笔位置(仅适于 LineTo,PolylineTo,PolyBezierTo,ArcTo )
  • ② 画笔
  • ③ 背景模式
  • ④ 背景颜色
  • ⑤ 绘制模式
画一条直线:
MoveToEx(hdc,xBeg,yBeg,NULL);//指定起点
LineTo(hdc,xEnd,yEnd);//指定终点

MoveToEx 实际上啥都没画,只是设置设备环境“当前位置”属性。默认(0,0)是最初的当期位置
MoveToEx 的最后一个参数是一个指向 POINT 结构的指针,表示运行该函数之前的当前位置,不需要就 NULL

获取当期位置:

GetCurrentPositionEx(hdc,&pt);//pt -- a pointer to POINT
多点连线

将数组的点连接成线:

POINT apt[5] ={100,100,100,200,200,200,200,100,100,100};
Polyline(hdc,apt,5);

Polyline 不使用和改变当前位置(从数组的第一个点开始)

PolyTo 使用当前位置作起点,最后终点为最后的终点

3. 边框绘制

(其实会填充该边框,但是默认填充白色)

绘制矩形:

Rectangle(hdc,xLeft,yTop,xRight,yBottom);

绘制椭圆:

Ellipse(hdc,xLeft,yTop,xRight,yBottom);

绘制圆角矩形:

RoundRect(hdc,xLeft,yTop,xRight,yBottom,xCornerEllipse,yCornerEllipse);

在这里插入图片描述

//椭圆弧线
Arc(hdc,xLeft,yTop,xRight,yBottom,xStart,yStart,xEnd,yEnd);

//弓形
Chord(hdc,xLeft,yTop,xRight,yBottom,xStart,yStart,xEnd,yEnd);

//扇形
Pie(hdc,xLeft,yTop,xRight,yBottom,xStart,yStart,xEnd,yEnd);

在这里插入图片描述

4. 贝塞尔样条曲线

…待学习…

5. 使用现有画笔

画笔决定:线条的颜色,宽度,样式(实线,点线,虚线)

Windows 提供的三种“备用画笔”(stock pen):

  • 画笔的默认设备环境是 BLACK_BEN (实线,宽度为1,黑色)
  • WHITE_PEN
  • NULL_PEN(不绘制任何图形)

也可创建自己的画笔

5.0. 使用句柄来操作画笔 —— HPEN
5.1. 调用 GetStockObject 获取备用画笔的句柄
hPen = GetStockObject(WHITE_PEN);
5.2. 将画笔选入设备环境:
SelectObject(hdc,hPen);

返回先前环境句柄中的画笔

直到选入另一画笔或释放该句柄

可组合使用:

SelectObject(hdc,GetStockObject(WHITE_PEN));
5.3. 创建画笔

CreatePen 或 CreatePenIndirect 创建一个 “逻辑画笔”(六个GDI对象之一)。
(GDI对象:画刷,画笔,位图,区域,字体,调色板,除调色板外其余都是由SelectObject 选入设备环境)
需用 DeleteObject 删除创建的逻辑画笔。

对画笔等GDI对象的注意事项:

  • 最终应当删除创建的所有GDI对象。
  • 当 GDI 对象被选入一个有效的设备环境时,不要删除它
  • 不要删除备用对象

(一)CreatePen

hPen = CreatePen(iPenStyle,iWidth,crColor);

iPenStyle 为画笔样式:

在这里插入图片描述对于 PS_SOLID,PS_NULL,PS_INSIDEFRAME 样式,iWidth表示画笔的宽度,iWidth = 0 时,Windows 把画笔宽度设为1像素。如果虚线或点线样式的画笔宽度大于1,Windows就会用实线来代替

PS_INSIDEFRAME 在画笔宽度大于1时,可以使用抖动色(是唯一能使用抖动色的画笔样式)

在这里插入图片描述
(二)CreatePenIndirect

LOGPEN(“逻辑画笔”)结构
字段:

  • lopnStyle
  • lopnWidth
  • lopnColor
hPen = CreatePenIndirect(&logpen);

获取画笔信息:

GetObject(hPen,sizeof(LOGPEN),(LPVOID)&logpen);

获取当前设备环境中的画笔句柄:

hPen = GetCurrentObject(hdc,OBJ_PEN);
5.4. 填充空隙

点和虚线的空隙填充 由设备环境的两个属性(背景模式和背景颜色决定)。
默认的背景模式是 OPAQUE(不透明),即使用背景颜色填充空隙,背景颜色默认为白色。

改变 Windows 填充空隙的背景颜色:

SetBkColor(hdc,crColor);

获取 Windows 填充空隙的背景颜色:

GetBkColor(hdc,crColor);

改变背景模式:
TRANSPARENT (透明),不填充空隙
OPAQUE(不透明),使用背景颜色填充空隙

SetBkMode(hdc,TRANSPARENT);

获取背景模式:

SetBkMode(hdc);
5.5. 绘图模式

当 Windows 绘制时,实际上是对像素颜色按位进行布尔运算,“光栅操作”(ROP)。只涉及两种像素颜色时称为“二元光栅操作(ROP2)”,Windows 定义了 16 种 ROP2 运算码,默认是 R2_COPYPEN,将画笔像素的颜色复制到目标像素上。

画笔§:目标(D):1 11 00 10 0布尔操作绘图模式
结果:00000R2_BLACK
0001~(P | D)R2_NOTMERGEPEN
0010~P & DR2_MASKNOTPEN
0011~PR2_NOTCOPYPEN
0100P & ~DR2_MASKPENNOT
0101~DR2_NOT
0110P ^ DR2_XORPEN
0111~(P & D)R2_NOTMASKPEN
1000P & DR2_MASKPEN
1001~(P ^ D)R2_NOTXORPEN
1010DR2_NOP
1011~P |DR2_MERGENOTPEN
1100PR2_COPYPEN(内定)
1101P | ~DR2_MERGEPENNOT
1110P | DR2_MERGEPEN
11111R2_WHITE

设置绘图模式:

SetROP2(hdc,iDrawMode);

获取绘图模式:

iDrawMode = GetROP2(hdc);

6. 绘制填充区域

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值