图形基础
见钱眼开 于2005-4-16
图形设备接口(GDI:Graphics Device Interface)是Windows的子系统,负责在显示器和打印机上显示图形。
GDI由几百个函数调用和一些相关数据类型组成,包含在GDI32.dll动态连接库中。GDI命令通过设备驱动程序访问显示器和打印机设备。GDI支持与设备无关的图形。
图形输出设备分为两大类:光栅设备和矢量设备。显示器、打印机都是光栅设备,它们以点模式表示图像。矢量设备使用线来绘制图像。
在屏幕或打印机上显示的图形类型本身可以被分为几类,通常称为“图元”。它们是:
u 直线和曲线 线条是所有矢量图形绘制系统的基础。线条用设备描述表中选中的当前画笔绘制。
u 填充区域 当一系列直线或曲线封闭了一个区域时,该区域就可以当前GDI画刷对象进行填充。画刷可以是实现色彩、图案或者位图。
u 位图 位图是位的矩形数组。这些位对应显示设备上的象素,它们是光栅图形的基础。GDI支持两种类型位图:老的“设备有关”位图,是GDI对象;新的“设备无关”位图,可以存储在磁盘文件中。
u 文本
无法简单分类的其他:
u 映射模式和变换 默认以象素为单位
u 元文件 以二进制形式存储的GDI命令的集合
u 区域 形状任意的复杂区
u 路径 GDI内部存储的直线和曲线的集合。路径可以用于绘图、填充和剪裁,还可以转换为区域。
u 剪裁 绘图可以限制在客户区的某一部分中,这就是所谓的剪裁。剪裁通常通过区域或路径来定义。
u 调色板 定制调色板通常限于显示256色的显示器。可以改变除20种系统保留外的236种色彩。
要在一个图形输出设备(显示器或打印机)上绘图,必须先获得一个设备描述表句柄。设备描述表中包含许多确定GDI函数如何在输出设备上工作的当前“属性”。
最常用的获取并释放设备描述表句柄的办法是,在处理WM_PAINT消息时,调用BeginPaint和EndPaint;在处理非WM_PAINT消息时,调用GetDC和ReleaseDC(绘图区域为窗口客户区)以及GetWindowDC和ReleaseDC(绘图区域为整个窗口)。
BeginPaint、GetDC和GetWindowDC获得的设备描述表句柄都与显示器上的窗口有关。获取设备描述表句柄有一个更通用的函数是CreateDC:
hdc = CreateDC(pszDriver,pszDevice,pszOutput,pData);
…
Delete(hdc);
下列调用可获得整个屏幕的设备描述表句柄:
Hdc = CreateDC(TEXT(“DISPLAY”,NULL,NULL,NULL,NULL));
调用CreateIC函数可以获得一个信息描述表,无法使用它绘制图形。
调用CreateCompatibleDC函数创建一个兼容指定设备的内存设备描述表,显示表面默认只有一个单色象素宽度和高度。因此绘制前必须将一个合理宽度和高度的位图选进内存设备描述表,然后使用GDI函数在位图上绘图。
调用CreateMetaFile函数创建元文件:
hdcMeta = CreateMetaFile(pFileName);
…
Hmf = CloseMetaFile(hdcMeta);
一个设备描述表通常代表一个物理显示设备。调用GetDeviceCaps函数可获得有关该设备的信息。
打印机以“每英寸的象素数”表示分辨率;显示器以水平和垂直方向总的象素数如1024×768表示分辨率。调用GetSystemMetrics和GetDeviceCaps都可以得到屏幕象素数。字体大小一般以“磅“为单位表示。一磅大约等于1/72英寸。为什么都是10磅字体,还分大字体和小字体呢?一般情况下假定显示器分辨率为每英寸96个象素,而选择大字体时,假定为每英寸120象素。因此,屏幕分辨率越高,同一号字体越小。
一个象素颜色值可以以1位、8位、16位或者24位表示。GetDeviceCaps可返回指定设备上每个象素的颜色位数。一般使用COLORREF类型来表示颜色值,它是一个32位无符号长整数,从0-24位每8位分别代表红、绿、蓝值。
下表列举了设备描述表的基本属性以及获取和设置每一种属性的方法:
设备内容属性
默认值
修改该值的函数
取得该值的函数
Mapping Mode
MM_TEXT
SetMapMode
GetMapMode
Window Origin
(0, 0)
SetWindowOrgEx
OffsetWindowOrgEx
GetWindowOrgEx
Viewport Origin
(0, 0)
SetViewportOrgEx
OffsetViewportOrgEx
GetViewportOrgEx
Window Extents
(1, 1)
SetWindowExtEx
SetMapMode
ScaleWindowExtEx
GetWindowExtEx
Viewport Extents
(1, 1)
SetViewportExtEx
SetMapMode
ScaleViewportExtEx
GetViewportExtEx
Pen
BLACK_PEN
SelectObject
SelectObject
Brush
WHITE_BRUSH
SelectObject
SelectObject
Font
SYSTEM_FONT
SelectObject
SelectObject
Bitmap
None
SelectObject
SelectObject
Current Position
(0, 0)
MoveToEx
LineTo
PolylineTo
PolyBezierTo
GetCurrentPositionEx
Background Mode
OPAQUE
SetBkMode
GetBkMode
Background Color
White
SetBkColor
GetBkColor
Text Color
Black
SetTextColor
GetTextColor
Drawing Mode
R2_COPYPEN
SetROP2
GetROP2
Stretching Mode
BLACKONWHITE
SetStretchBltMode
GetStretchBltMode
Polygon Fill Mode
ALTERNATE
SetPolyFillMode
GetPolyFillMode
Intercharacter Spacing
0
SetTextCharacterExtra
GetTextCharacterExtra
Brush Origin
(0, 0)
SetBrushOrgEx
GetBrushOrgEx
Clipping Region
None
SelectObject
SelectClipRgn
IntersectClipRgn
OffsetClipRgn
ExcludeClipRect
SelectClipPath
GetClipBox
在注册窗口类前,如果在窗口类风格中指定CS_OWNDC风格,则该类每个窗口都将独自有一个设备描述表;如果在窗口类风格中指定CS_CLASSDC风格,则该类每个窗口都将共享一个设备描述表;如果上述两种风格都没有包含,则返回一个临时设备描述表。SaveDC和RestoreDC函数分别用以保存或恢复某个状态时设备描述表的一些属性。
GetPixel和SetPixel函数分别用以读、写某个象素。
LineTo 画直线
PolyLine和PolyLineTo 画一组相连直线
PolyPolyLine 画多组相连直线
Arc和ArcTo 画椭圆线
PolyBezier和PolyBezierTo 画贝赛尔样条
Rectangle 画矩形
Ellipse 画椭圆
RoundRect 画圆角矩形
Pie 画饼形
Chord 画弓形
在设备描述表中有个“当前位置(current position)”属性,画直线时表示直线起点。调用GetCurrentPositionEx可获取这一属性。
Rectangle、Ellipse等绘制封闭区域图形的函数,在绘制线条的同时,还将使用画刷填充封闭区域。
使用GDI对象应注意的三条规则:
l 使用完毕务必删除自己创建的GDI对象
l 禁止删除在有效设备描述表中使用的GDI对象
l 禁止删除原有GDI对象
创建一个Pen对象:
hPen = CreatePen(iPenStyle,iWidth,crColor);
LOGPEN logpen;
hPen = CreatePenIndirect(&logpen);
删除一个Pen对象:
DeleteObject(hPen);
返回Pen对象的属性:
GetObject(hPen,sizeof(LOGPEN),(LPVOID)&logpen);
返回设备描述表中的Pen对象:
hPen = GetCurrentObject(hdc,OBJ_PEN);
设置Pen模式:
SetROP2(hdc,iDrawMode); //将Pen颜色和目标象素颜色进行某种按位布尔运算
设置背景模式:
SetBKMode(hdc,TRANSPARENT);
设置背景颜色:
SetBKColor(hdc,crColor);
创建一个
Brush对象:
hBrush = CreateSolidBrush(crColor); //实体画刷
hBrush = CreateHatchBrush(iHatchStyle,crColor); //背影画刷
hBrush = CreatePatternBrush(hBmp); //位图画刷
hBrush = CreateDIBPatternBrush(hBmp);//设备无关位图画刷
hBrush = CreateBrushIndirect(&logbrush);
“映射方式”是一种几乎影响所有客户区绘图的设备描述表属性。几乎所有GDI函数都使用“逻辑单位“;而所有非GDI函数都使用”设备单位(即象素单位)“。所有设备坐标
系都以象素为单位,X轴上的值从左到右递增,Y轴上的值从上到下递增。
设备坐标一般可以分为三种:
l 屏幕坐标 以屏幕左上角为原点,调用CreateDC返回。用于CreateWindow(非子窗口)、MoveWnidow(非子窗口)、GetMessagePos、GetCursorPos、WindowFromPoint等与窗口无关的函数。
l 窗口坐标 以窗口左上角为原点,调用GetWindowDC返回。一般不太使用。
l 客户区坐标 以客户区左上角为原点,调用GetDC返回。ClientToScreen和ScreenToClient函数使客户区坐标和屏幕坐标之间相互转换。
视点(ViewPort)坐标基于设备坐标系;而窗口原点(WindowOrg)坐标基于逻辑坐标系。不管窗口原点和视点如何改变,设备坐标原点(0,0)始终是屏幕、窗口、客户区的左上角。因此,不论改变视点(ViewPort)坐标值还是窗口原点(WindowOrg)坐标值,本质上都是在改变逻辑坐标系的原点值。设置视点等于设置设备坐标系中逻辑坐标的原点。设置窗口原点等于设置逻辑坐标系中逻辑坐标的原点。
SetViewportOrgEx ( hdc, 10,20, NULL)将设备点(10,20)映射为逻辑点(0,0);
SetWindowOrgEx (hdc, 10, 20, NULL)将逻辑点(10,20)映射为设备点(0,0);
区域是对显示器上一个范围的描述,这个范围是矩形、多边形和椭圆的组合。区域可以用于绘制和剪裁,通过将区域选进设备描述表,就可以用区域来进行剪裁(就是说将绘图范围限制为客户区的一部分)。
创建一个区域:
hRgn = CreateRectRgn(xLeft,yTop,xRight,yBottom);
hRgn = CreatePolygonRgn(&point,iCount,iPolyFillMode);
组合区域:
iRgnType = CombineRgn(hDestRgn,hSrcRgn,hSrcRgn1,iCombine);
将区域选进设备描述表:
SelectObject(hdc,hRgn);或SelectClipRgn(hdc,hRgn);