5、绘图基础
设备环境 1、
《4、文本输出》中介绍了设备环境句柄的获取方法。但有时并不需要绘图,只是获取一些设备环境句柄相关的信息,可以使用。也是返回一个HDC,但是不能对其进行绘制。
2、
用于获取设备环境的一些显示能力,比如:
- HORZSIZE:以毫米为单位的物理屏幕宽度- VERTSIZE:以毫米为单位的物理屏幕高度- HORZRES:水平方向像素数- VERTRES:垂直方向像素数- LOGPIXELSX:水平方向每英寸像素数- LOGPIXELSY:垂直方向每英寸像素数
和的值并不可靠。它们并不等于实际的物理屏幕尺寸,在win10系统中测试也不满足书本中提供的公式:
书中还提到,在 NT中,和是固定的值,用来表示标准的显示器尺寸。
3、字号和字体在屏幕显示大小
字号(字体大小)是用“点值”(也称“磅”)来表示的,1磅大约是1/72英寸。通过获取到的表示垂直方向每英寸像素数,那么 / 72就是1磅的字体高度,这个值和中的 - 相等。
我们可以在系统设置中改变屏幕分辨率。如果减小屏幕分辨率, 并不会改变,只是总像素数减少了,单个像素的面积变大,所以同样的字号显示得更大。
4、色彩
在GDI函数中,使用来表示一个颜色值,是一个32位无符号整型。最高8位是0,其后每8位分别用于指定红、绿、蓝。所以实际只有24位被实际用于表示颜色。提供了几个宏用于:
#define RGB(r,g,b) ((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))
#define GetRValue(rgb) (LOBYTE(rgb))
#define GetGValue(rgb) (LOBYTE(((WORD)(rgb)) >> 8))
#define GetBValue(rgb) (LOBYTE((rgb)>>16))
5、保存设备环境
默认情况下,设备环境句柄被释放后,对设备环境属性所做的所有改变都会丢失。如果想保存这些修改,可以:
绘制点和线 1、点
虽然一般不会直接操作像素,但GDI提供了相关函数
2、线
3、GDI对象–画笔
有6种GDI对象:画笔、画刷、位图、区域、字体和调色板。
使用GDI对象时,需要调用把GDI对象选入设备环境中,调用可以获取设备环境中当前使用的GDI对象。不再使用的GDI对象需要调用删除GDI。提供了一些预定义好的GDI对象,可以调用来获取预定义的GDI对象。
画笔决定了线条的颜色、宽度和样式。提供了3种预定义画笔:
是设备环境中的默认画笔。需要使用其他画笔时,可以调用把画笔选入设备环境中。
除了预定义画笔,还可以自定义画笔:
使用完毕后,应该删除所有创建的画笔对象。但不能在被选入设备环境时删除,也不能删除预定义的画笔对象。
调用可以从画笔对象句柄中读取画笔属性到逻辑画笔中:
GetObject(hPen, sizeof(LOGPEN), (LPVOID)&logpen);
的返回值是设备环境中上一个GDI对象,这一特性可以用于中:
SelectObject(hdc, CreatePen(PS_DASH, 0 RGB(255, 0, 0)));
DeleteObject(SelectObject(hdc, GetStockObject(BLACK_PEN)));
或者
hPen = SelectObject(hdc, CreatePen(PS_DASH, 0 RGB(255, 0, 0)));
DelectObject(SelectObject(hdc, hPen));
4、填充间隙
绘制中,很多地方存在间隙,比如虚线间或者文本输出间的空白部分。
这些间隙是由设备环境的背景模式和背景颜色共同控制的
只有两种背景模式
背景模式默认是不透明,背景颜色默认是白色。
5、绘图模式
对像素颜色执行一个按位布尔运算称为“光栅操作”(ROP),如果只涉及两种像素颜色则称为“二元光栅操作”(ROP2)。
使用画笔进行绘制时,像素最终颜色是原颜色和画笔颜色进行一个光栅操作的结果。定义了16中ROP2运算,被称为绘图模式。
填充区域 1、边框绘制
下列函数先用画笔绘制边框线,再用画刷填充区域。
BOOL WINAPI Rectangle(HDC hdc, int left, int top, int right, int bottom);
根据左上角和右下角坐标绘制矩形,实际绘制时不包含右下角像素,而是往上、左缩了一个像素。(hdc, 1, 1, 5, 4)绘制结果:
BOOL WINAPI Ellipse(HDC hdc, int left, int top, int right, int bottom);
根据左上角和右下角确定一个虚拟的矩形边框,然后在矩形边框内绘制矩形。
BOOL WINAPI RoundRect(HDC hdc, int left, int top, int right, int bottom, int width, int height);
相比多了width和参数。圆角矩形的圆角是按照椭圆绘制的,每个圆角对应椭圆的一个象限。width和定义了椭圆对应虚拟矩形边框的宽和长,加上左上角和右下角左边可以确定4个椭圆。
BOOL WINAPI Chord(HDC hdc,int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4);
BOOL WINAPI Pie(HDC hdc, int left, int top, int right, int bottom, int xr1, int yr1, int xr2, int yr2);
Chord和Pie绘制的图形都是椭圆的一部分,所以前面参数和相同,先确定一个椭圆。后面4个参数在椭圆弧线上确定了起点、终点两个点。
BOOL WINAPI Arc(HDC hdc, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4);
Arc绘制的并不是一个有填充区域的封闭图形,而是椭圆上的一条弧线。
BOOL WINAPI Polygon(HDC hdc, CONST POINT *apt, int cpt);
根据提供的坐标点绘制多边形,如果最后一个点和第一个点不同,则连接两点。
BOOL WINAPI PolyPolygon(HDC hdc, CONST POINT *apt, CONST INT *asz, int csz);
根据提供的坐标点绘制多个多边形。
和绘制的多边形可能构成多个封闭区域,如何对这些区域进行填充取决于多边形的填充模,调用可以设置设备环境的多边形填充模式:
上图是模式的一个例子,以下面POINT数组为传参:
POINT apt[5] = {0, 20, 60, 20, 10, 60, 30, 0, 50, 60};
边框线的方向依赖于POINT数组提供的坐标顺序,比如第一个点是(0,20),第二个点是(60,20),所以边框线AB的方向是从A到B。
从中间的五边形区域向左画一条射线,经过了边框线EA和CD。关键是相对于射线的方向怎么理解?我认为是垂直于射线的方向,比如图中的射线是水平的,则相对于射线的方向应该取垂直方向。那么,EA边框线在垂直方向是向上,CD边框线在垂直方向也是向上。即向下的边框线数目是0,向上的边框线数目是2,故需要填充。
2、画刷
提供了6种预定义画刷:
当然也可以创建自定义画刷。提供了5个函数来创建画刷:
HBRUSH WINAPI CreateSolidBrush(COLORREF color);
HBRUSH WINAPI CreateHatchBrush(int iHatch, COLORREF color);
// 6种阴影线样式
#define HS_HORIZONTAL 0 /* ----- */
#define HS_VERTICAL 1 /* ||||| */
#define HS_FDIAGONAL 2 /* \\\\\ */
#define HS_BDIAGONAL 3 /* / */
#define HS_CROSS 4 /* +++++ */
#define HS_DIAGCROSS 5 /* xxxxx */
HBRUSH WINAPI CreatePatternBrush(HBITMAP hbm);
HBRUSH WINAPI CreateDIBPatternBrush(HGLOBAL h, UINT iUsage);
HBRUSH WINAPI CreateBrushIndirect(CONST LOGBRUSH *plbrush);/* Logical Brush (or Pattern) */
typedef struct tagLOGBRUSH
{UINT lbStyle;COLORREF lbColor;ULONG_PTR lbHatch;
} LOGBRUSH, *PLOGBRUSH, NEAR *NPLOGBRUSH, FAR *LPLOGBRUSH;
属性定义了另外两个属性的意义:
h
画刷颜色
忽略
(空画刷)
忽略
忽略
阴影线颜色
阴影线样式
忽略
位图句柄
忽略
指向DIB的指针
类似画笔,可以利用从画刷句柄中读取画刷信息到逻辑画刷中:
GetObject(hBrush, sizeof(LOGBRUSH), (LPVOID)&logbrush);
映射模式 矩形、区域和裁剪 1、处理矩形
除了,还提供了一些其它处理矩形的函数:
2、区域
区域是一种GDI对象,是对一块空间的描述,可以是任意形状。
将区域选进设备环境,是对设备环境绘图空间的一种裁剪,就会把绘图限制在区域指定的空间内,在区域外的绘图动作会被忽略。
HRGN WINAPI CreateRectRgn(int x1, int y1, int x2, int y2);
HRGN WINAPI CreateRectRgnIndirect(CONST RECT *lprect);
HRGN WINAPI CreateRoundRectRgn(int x1, int y1, int x2, int y2, int w, int h);
HRGN WINAPI CreateEllipticRgn(int x1, int y1, int x2, int y2);
HRGN WINAPI CreateEllipticRgnIndirect(CONST RECT *lprect);
HRGN WINAPI CreatePolygonRgn(CONST POINT *pptl, int cPoint, int iMode);
HRGN WINAPI CreatePolyPolygonRgn(CONST POINT *pptl, CONST INT *pc, int cPoly,int iMode);
int WINAPI CombineRgn(HRGN hrgnDst, HRGN hrgnSrc1, HRGN hrgnSrc2, int iMode);
// iMode区域合并方式
#define RGN_AND 1
#define RGN_OR 2
#define RGN_XOR 3
#define RGN_DIFF 4 // hrgnSrc1不在hrgnSrc2的部分
#define RGN_COPY 5 // hrgnSrc1全部
// 返回值
#define ERROR 0 // 错误
#define NULLREGION 1 // 空区域
#define SIMPLEREGION 2 // 矩形、椭圆或多边形简单区域
#define COMPLEXREGION 3 // 矩形、椭圆或多边形组合区域
类似矩形,也提供了一些处理区域的函数
3、裁剪
除了把区域选入设备环境外,还可以调用使一个区域无效,从而在处理消息时把绘制动作限制在特定区域内。相对的,可以使一个区域有效。