天天看点

Display-GDI基础

第一节、设备描述表

一、设备描述表登记当前选择的绘图对象,如逻辑画笔。绘图对象的创建无需依赖于设备句柄

二、GDI 元素:图元及其他

1)线条、封闭填充区域、位图、文本

2)度量映射

3)矢量元文件

4)剪裁

5)调色板

三、向 GDI 函数提供设备描述表句柄

GDI 函数使用登记于设备描述表的有关绘图元素的信息,并按指定格式执行,如输出文本的 GDI 函数使用设备描述表选用的字体等。

四、获取设备句柄

BeginPaint(hWnd, &ps) 处理 WM_PAINT 消息时,完成无效区域的有效化,填充 PAINTSTRUCT 结构并返回客户区设备句柄

GetDC(hWnd) 获取指定窗口客户区的绘图设备句柄

GetWindowDC(hWnd) 获取指定窗口的整个窗口区域的设备句柄

五、创建设备描述表

CreateDC(LPCTSTR driver, LPCTSTR device, LPCTSTR output, const DEVMODE* lpInitData);

1)driver 驱动类型

DISPLAY, WINSPL16 分别表示显示设备和打印机,设置为 NULL 并由第二个参数决定特定设备驱动

2)device 设备名称

相应 driver 类别的主设备,使用 NULL 或者其名称。

使用 EnumDisplay(device, num, &display_device, 0) 枚举设备。

六、设备表状态

GetDC 可能获取通用的、类、私有的设备上下文,类、私有设备上下文在取回时总是保持上回的选取状态,而通用的设备上下文则不保存状态。通用设备上下文必须得到释放,而私有设备上下文不需要。设备上下文句柄在同一时刻仅能被单一线程访问。

七、获取设备描述表信息

GetDeviceCaps(HDC hdc, int nIndex);

1)尺寸

HORZSIZE, VERTSIZE              毫米表示的设备尺寸

HORZRES, VERTRES                像素表示的设备尺寸

BITSPIXEL                       色深

LOGPIXELSX, LOGPIXELSY          每“逻辑英寸”像素数

2)数值

NUM_ + BRUSHES, PENS, FONTS, COLORS 画刷、画笔、字体、颜色表颜色数

ASPECTX, ASPECTY, ASPECTXY      线条绘制的像素的相对宽度、高度、对角线长度

PLANES                          调色板数

SIZEPALETTE                     系统调色板数

COLORRES                        活动的调色板颜色数

DPI                             像素分辨率 Dots per inch

八、计量单位

1) 英寸的单位转换

一英寸 inch     =                2.539999918 厘米

一英尺 foot     =               12 英寸                 = 30.48 厘米

2)像素尺寸转换到毫米尺寸

HORZSIZE        =               25.4 * HORZRES / LOGPIXELSX

VERTSIZE        =               25.4 * VERTRES / LOGPIXELSY

2)字体大小(磅值)

一磅 pound      =               1/72 inch

                =               0.35278 mm

字体的磅值未包含内部间距,POUNDpix = tmHeight - tmInternalLeading。

不同的 DPI(Dots per inch)设置,磅值包含的像素值是不定的。

在 DPI 为 96 时:

10 POUNDpix = 10 * 1/72 inch = 10 * 1/72 * 96 pix = 13 pix

在 DPI 为 120 时:

10 POUNDpix = 10 * 1/72 inch = 10 * 1/72 * 120 pix = 16.5 pix

九、色彩

1)对于高 / 真彩色深,调色板数总是为 1

2)32 位色深的颜色内存布局

Display-GDI基础

十、设备描述表属性设置

1)窗口、视口定义

SetViewportOrgEx, SetWindowOrgEx 设置视口到窗口 (0, 0) 的映射坐标以及窗口到视口 (0, 0) 的映射坐标,且不应同时设置。

2)默认的 GDI 对象

HPEN                    BLACK_PEN

HBRUSH                  WHITE_BRUSH

HFONT                   SYSTEM_FONT

HBITMAP                 <None>

3)绘制属性

SetBkMode / GetBkMode   设置和取得字体背景模式,选用 OPAQUE, TRANSPARENT 之一。

4)保存设备描述表

私有、类设备描述表自动保存状态,系统设备描述表总是重置为默认状态。使用 n = SaveDC(hdc), RestoreDC(hdc, n) 手动存取设备描述表状态,且对系统设备描述表无效。n = -1 表示取出最近保存的设备描述表状态。

第二节、绘制线条和填充

一、画线

1)设置当前点位置

MoveToEx(hdc, x, y, &point);

point 将保存原来的点位置。

2)画线到 (x, y)

LineTo(hdc, x, y);

3)折线

Polyline(hdc, &points, n);

画一组 points 存储的 n 个点的折线,不使用 / 改变当前绘图点和连接首尾。

PolylineTo(hdc, &points, n);

使用当前绘图点,画一组 points 存储的 n 个点的折线,不连接首尾。

4)获取当前点

GetCurrentPositionEx(hdc, &point);

二、边界框函数

Rectangle(hdc, x0, y0, x1, y1);

绘制 [(x0, y0), (x1, y1)) 矩形区域。

Ellipse(hdc, x0, y0, x1, y1);

Display-GDI基础

绘制由 [(x0, y0), (x1, y1)) 决定的边界框包含的椭圆。

Display-GDI基础

RoundRect(hdc, x0, y0, x1, y1, xc, yc);

绘制椭方形,其转角的所在的椭圆的横向半径和纵向半径分别为 xc 和 yc。

Display-GDI基础

Arc / Chord / Pie (x0, y0, x1, y1, xs, ys, xe, ye);

绘制弧、弓形和饼形,以 [(x0, y0), (x1, y1)) 为边界,(xs, ys) 与圆心 O 的线段所在射线的弧度沿逆时针到 (xe, ye) 与圆心 O 的线段所在射线的弧度为绘制范围。

三、贝塞尔曲线

由两个端点和两个控制点,并由贝塞尔曲线公式决定的曲线轨迹。

Display-GDI基础

PolyBezier(hdc, &pt, n)

绘制 N 组贝塞尔曲线,在 pt 指向的存储中包含以下点:

        A1, B1, C1, D1

            B2, C2, D2

            ...

            Bn, Cn, Dn

由以上贝塞尔曲线序列的点可以获知,除了第一组具有四个端点外,后续的贝塞尔曲线点总是使用前一组贝塞尔曲线点的结束端点作为起始端点,所以 PolyBezier 绘制 N 组贝塞尔曲线,包含 3N + 1 个点。

PolyBezierTo(hdc, &pt, n)

以当前绘制点为起始端点,绘制 N 组贝塞尔曲线,在 pt 指向的存储中包含以下点:

        B1, C1, D1

        B2, C2, D2

        ...

        Bn, Cn, Dn

PolyBezierTo 绘制 N 组贝塞尔曲线,包含 3N 个点,每组贝塞尔曲线均使用 3 个点,起始端点由前一次的绘制点决定。

四、画笔

画笔是一类 GDI 对象,包含系统预设的画笔,通过 GetStockObject 并传递索引取得,有效的索引如 WHITE_PEN, BLACK_PEN, NULL_PEN 等。选入系统预设的 GDI 对象到设备表无需清除。

由用户创建的画笔等 GDI 对象需要手动清除,并注意在选入用户对象句柄到设备表之前和之后保存和还原设备表的初始值。

1)创建画笔

CreatePen(style, width, color);

可用的画笔样式有:

Display-GDI基础

PS_INSIDEFRAME 在宽度大于 1 的时候可实现抖动色,并且绘线的外边缘不超过边界框。

2)选入和销毁画笔

SelectObject(hdc, (HGDIOBJ)hpen);

DeleteObject((HGDIOBJ)hpen);

五、绘图模式

SetROP2(hdc, mode);

ROP2 即 Binary raster operation,相应的 ROP2 模式可用于控制进行光栅绘图时,画刷 / 画笔色和目标设备的像素色将如何作用。使用得当的光栅绘图模式,可以获得实用的绘制效果。例如,实现响应鼠标拖曳事件的矩形区域,当使用鼠标拖动定义一个矩形区域,使用“反相”的绘图模式绘制出该矩形区域,并在鼠标拖动状态更改时,再使用相同的模式绘制该区域,其效果是已绘制矩形被“擦除”了。使用相似的过程来跟踪鼠标拖曳事件。“异或”的绘图模式则使用绘制色为参考,当绘制色的指定位为 1,则翻转目标色的指定位,为 0 则不改变,重复绘制亦能还原目标色。

可用的绘图模式:

绘图模式                操作

-------------------------------------

R2_BLACK                0

R2_COPYPEN              PEN (default)

R2_MASKNOTPEN           SCREEN & ~PEN

R2_MASKPEN              SCREEN & PEN

R2_MASKPENNOT           ~SCREEN & PEN

R2_MERGENOTPEN          SCREEN | ~PEN

R2_MERGEPEN             SCREEN | PEN

R2_MERGEPENNOT          ~SCREEN | PEN

R2_NOP                  SCREEN

R2_NOT                  ~SCREEN

R2_NOTCOPYPEN           ~PEN

R2_NOTMASKPEN           ~(SCREEN & PEN)

R2_NOTMERGEPEN          ~(SCREEN | PEN)

R2_NOTXORPEN            ~(SCREEN ^ PEN)

R2_WHITE                1

R2_XORPEN               SCREEN ^ PEN

六、填充区域

1)具有填充能力的绘图函数

Rectangle(hdc, x0, y0, x1, y1) 绘制矩形

Ellipse(hdc, x0, y0, x1, y1) 画矩形区域内的椭圆形

RoundRect(hdc, x0, y0, x1, y1, xc, yc) 画矩形区域内的圆边矩形,圆边的弧线宽高分别为 xc, yc

Chord(hdc, x0, y0, x1, y1, xs, ys, xe, ye) 画矩形区域内的弓形,起点所在过圆心射线上的点 (xs, ys),终点 (xe, ye),按逆时针方向构造

Pie(hdc, x0, y0, x1, y1, xs, ys, xe, ye) 画扇形

Polygon(hdc, &pt, n) 形似 Polyline,当 pt[0] 和 pt[n - 1] 并不重合时,将自动增加终点到起点的线,并填充包围区域

PolyPolygon(hdc, &pt, &n, c) 绘制 c 个多边形,pt 包含所有的端点信息,pn 指出所有多边形的端点数,总是大于等于 2,c 指示所有多边形数,对于包含的多余的点则忽略

2)填充模式

Display-GDI基础

SetPolyFillMode(hdc, mode)

可选的填充模式为 ALTERNATE 和 WINDING。

1)ALTERNATE 交叠

当光栅扫描线经过奇数边和偶数边之间时,填充该区域,经过偶数边和奇数边之间,不填充该区域,形成交叠的填充模式。

2)WINDING 环绕

当光栅扫描线每经过一条边,该边的方向为正,则增一,为负则减一。例如,矩形左侧边向下设为正,则当矩形边绕到右侧时,方向为 y 值向上,方向为负,累加为零,称为一次关闭,已经完成关闭的区域的后续扫描线区域将不填充。

在示意图中扫描线穿过边 1,方向为正而增一,并再次穿过边 5,方向为正而增一,最后穿过边 3,方向为负减一,扫描线穿过的所有区域均未能具有相等的不同方向的边而关闭,所以全部被填充。

综上所述,仅当扫描线穿过的区域已关闭(不同方向的边数一致)时,后续的区域不填充。

以上两种算法均作为查找可填充区域的策略,大多数情况下,WINDING 将尽可能查找复合多边形中更多可填充的区域。

七、画刷

画刷包含位图画刷和填充画刷。

1)抖动色使用位图表示

2)创建画刷的多种形式

CreateSolidBrush(color);

使用指定颜色创建实心的画刷。Windows 可能选用抖动色位图以模拟指定色彩。

CreateHatchBrush(style, color);

Display-GDI基础

使用指定颜色和阴影样式创建画刷。

CreatePatternBrush(hbmp);

使用位图句柄创建图案刷子。对于大于 8 * 8 的位图,超出的部分被忽略。选入单色位图,允许以 1 位使用背景色填充,以 0 位使用文本色填充。

CreateBrushIndirect(&logbrush);

使用 LOGBRUSH 逻辑画刷结构创建画刷。

3)LOGBRUSH 逻辑画刷结构

typedef struct tagLOGBRUSH {

        UINT     lbStyle;

        COLORREF lbColor;

        LONG     lbHatch;

} LOGBRUSH, *PLOGBRUSH;

lbStyle 逻辑画刷结构包含的画刷类型,如 BS_PATTERN, BS_SOLID, BS_HATCHED, BS_HOLLOW 等,相应的颜色和阴影信息亦可能具有不同的意义。

当逻辑画刷的样式为 BS_PATTERN,lbColor 被忽略。当样式为 BS_DIBPATTERN, BS_HOLLOW,lbColor 指出位图画刷的颜色制式为 DIB_PAL_COLORS, DIB_RGB_COLORS 之一。当样式为 BS_SOLID, BS_HATCHED,lbColor 包含画刷色。

lbHatch 当逻辑画刷的样式为 BS_PATTERN, BS_DIBPATTERN 时,包含位图句柄。当样式为 BS_HATCHED 时,包含阴影线样式,当样式为 BS_SOLID, BS_HOLLOW 时,lbHatch 被忽略。

第三节、GDI 映射模式

一、度量单位

metric          米制的

english         英制的

pound 磅        = 1/72 英寸

twip 缇         = 1/20 磅 = 1/1440 英寸

在不同的系统 DPI(Dots per inch) 设置中,一英寸分别具有特定的像素数。

二、映射模式

SetMapMode(hdc, mode);

映射模式        逻辑单位        y 的方向

----------------------------------------

MM_TEXT         pixel           UP

MM_LOMETRIC     0.1mm           DOWN

MM_HIMETRIC     0.01mm          DOWN

MM_LOENGLISH    0.01in          DOWN

MM_HIENGLISH    0.001in         DOWN

MM_TWIPS        1/1440in        DOWN

MM_ISOTROPIC    用户定义        用户定义

MM_ANISOTROPIC 用户定义        用户定义

1)映射模式影响设备描述表,而不影响消息传送使用的坐标单位

GetSystemMetrics 总是使用设备单位。

GetDeviceCaps 中,HORZRES / VERTRES 使用像素表示的设备尺寸,HORZSIZE / VERTSIZE 使用毫米表示的设备尺寸,使用 LOGPIXELSX / LOGPIXELSY 执行换算。

TEXTMETRICS 字体信息结构中度量依据当前映射模式。

三、坐标系

1)设备坐标与逻辑坐标

设备坐标通常以像素表示。

逻辑坐标使用特定度量形式,并以设备描述表的映射模式来转换到设备坐标。

使用设备描述表的绘图函数使用逻辑坐标。

2)设备坐标中的特定坐标系

        1、主要工作于客户区坐标系

        2、所有设备坐标系以像素为单位

        3、客户区坐标系从属于窗口坐标系,窗口坐标系从属于屏幕坐标系

        4、ClientToScreen 转换特定窗口的客户区坐标系到屏幕坐标系

        5、ScreenToClient 转换屏幕坐标系到特定窗口的客户区坐标系

        6、GetClientRect 获取指定窗口客户区在其客户区坐标系的值

        7、GetWindowRect 获取窗口在屏幕坐标系的坐标

四、视口(Viewport)和窗口(Window)

使用设备描述表执行 GDI 绘图时,GDI 将在一个逻辑尺寸映射的区域作图,称为“窗口 Window”,GDI 超出窗口映射到视口的区域将不被显示。

Display-GDI基础

1)SetWindowOrgEx(hdc, x, y, &pt);

设置表示视口原点 (0, 0) 的逻辑窗口坐标 (xWinOrg, yWinOrg)。

2)SetViewportOrgEx(hdc, x, y, &pt);

设置表示逻辑窗口原点 (0, 0) 的视口坐标 (xViewOrg, yViewOrg)。

3)ViewportExt 和 WindowExt

SetViewportExtEx 设置视口的宽度,SetWindowExtEx 设置窗口的宽度。

通过设置视口宽度,可以得到视口的像素单位到中间量的换算因子,视口的宽度总是对应一个单位的中间量。

视口宽度表示为视口坐标轴的表示范围,设置视口宽度为 1,则一个单位中间量对应视口宽度,从视口到中间量不改变。设置视口宽度为设备像素数,如 1024,则一个单位中间量对应 1024 个视口单位,从视口到中间量,除于 1024。

窗口宽度表示为逻辑窗口坐标轴的表示范围,设置窗口宽度为 1,则一个单位中间量对应窗口宽度,从中间量到窗口不改变。设置窗口宽度为逻辑单位数,如 1024,则 1024 个逻辑单位对应一个单位中间量,从中间量到窗口,乘于 1024。

        逻辑窗口值 = 视口值 / 视口宽度 * 逻辑窗口宽度

通常,单独的视口或者逻辑窗口的宽度的概念是没有意义的。

4)设备坐标点到逻辑坐标点

DPtoLP(hdc, &pt, n);

将位于 pt 所在内存区域的 n 个表示设备单位值的点转换成对应设备描述表的逻辑单位值。

LPtoDP(hdc, &pt, n);

从逻辑坐标点到设备坐标点的转换。

五、MM_TEXT 文本映射

MM_TEXT 映射模式不论设备坐标抑或逻辑坐标,均使用像素作为单位,SetViewportExtEx, SetWindowExtEx 不影响该映射模式。

SetViewportOrgEx(hdc, widView / 2, hgtView / 2, 0);

通过上式将位于视口居中坐标点映射到逻辑窗口的 (0, 0) 点位置,使得从逻辑窗口原点绘制的图形位于视口的居中位置。

SetViewportOrgEx, SetWindowOrgEx 不应同时设置,避免引起混淆。

六、按预设逻辑单位的映射

映射模式        逻辑单位        DPI = 96, 1 inch

--------------------------------------------------------

MM_LOMETRIC     0.1mm           (254, -254)

MM_HIMETRIC     0.01mm          (2540, -2540)

MM_LOENGLISH    0.01in          (100, -100)

MM_HIENGLISH    0.001in         (1000, -1000)

MM_TWIPS        1/1440in        (1440, -1440)

预设逻辑单位的映射模式,y 的正方向为上,和迪卡尔坐标系保持一致。

和 MM_TEXT 类似,使用 SetViewportOrgEx 将设备坐标系的原点移动到绘图设备居中位置,且无法更改映射的换算因子(即视口和绘图窗口的宽度比)。

七、各向同性 / 各向异性

MM_ISOTROPIC 各向同性的映射模式对于要求的映射宽度总满足不同轴向上的逻辑单位一致,以最短边为最大可能扩展空间。

Display-GDI基础

在设置映射模式为各向同性的时候,调整可显示设备范围对应的逻辑窗口范围,默认具有 MM_LOMETRIC 的逻辑单位映射。用户使用感兴趣的值设置设备宽度和逻辑窗口宽度,而后系统自动调整范围,使调整后的范围满足各向同性。

MM_ANISOTROPIC 使用户允许自由设置所需的逻辑窗口以及设备视口的宽度,使逻辑窗口映射到视口的纵向和横向的宽度不必具有相同的比例。

第四节、矩形、区域、裁剪

一、矩形填充

FillRect(hdc, &rc, brush);

使用指定画刷填充举行矩形,不受绘图模式影响。

FrameRect(hdc, &rc, brush);

使用指定画刷绘制矩形,不受绘图模式影响。

InvertRect(hdc, &rc);

负片效果。使用 NOT 绘图模式。

二、区域 Region

1)区域、矩形并不依赖特定的设备描述表,矩形向绘图函数传递数据指针,区域向绘图函数传入 HRGN 句柄

2)创建区域的函数:

CreateRectRgn(x0, y0, x1, y1);

CreateRectRgnIndirect(&rc);

CreateEllipticRgn(x0, y0, x1, y1); 创建椭圆形的区域

CreateEllipticRgnIndirect(&rc);

CreateRoundRectRgn(x0, y0, x1, y1, xc, yc); 创建圆边矩形

CreatePolygonRgn(&pt, n, fillmode); 创建多边形区域,fillmode 可选 ALTERNATE 和 WINDING 模式

CreatePolyPolygonRgn(&pt, &n, c, fillmode); 创建多个多边形区域,每个多边形的边在 n 数组中指定,共计 c 个多边形

三、联合多个区域

CombineRgn(hrgn_dest, hrgn_src1, hrgn_src2, combine);

Display-GDI基础

联合区域可能的返回结果:

        1、NULLREGION 空区域

        2、SIMPLEREGION 单一区域

        3、COMPLEXREGION 复合区域

        4、ERROR 失败

四、使用区域的绘图函数

FillRgn(hdc, hrgn, brush);

使用指定画刷填充区域,不受绘图模式影响。

FrameRgn(hdc, hrgn, brush);

使用指定画刷绘制区域边框,不受绘图模式影响

InvertRgn(hdc, hrgn);

使用 NOT 绘图模式反相绘制区域。

PaintRgn(hdc, hrgn);

使用当前画刷填充区域,不受绘图模式影响。

五、使用区域

1)无效矩形和无效区域

使用 GetUpdateRect(hwnd, &rc, bErase) 取得包含无效区域的最小无效矩形,bErase 指示操作该函数,向系统发送 WM_ERASEBKGND 消息。

使用 GetUpdateRgn(hwnd, hrgn, bErase) 取得指定窗口的无效区域。

2)设置剪裁区域

SelectClipRgn(hdc, hrgn);

选进设备描述表的剪裁区域将会创建一个新的副本,故可删除该剪裁区域。