图形设备接口(GDI:Graphics Device Interface)是Windows的子系统,它负责在视频显示器和打印机上显示图形。正如您所认为的那样,GDI是Windows非常重要的部分。不但您为Windows编写的应用系统在显示视觉信息时只使用GDI,就连Windows本身也只使用GDI来显示用户界面对象,诸如菜单、滚动条、图标和鼠标光标。
不幸的是,如果要对GDI进行全面的讲述,将需要一整本书。在本章中,我只是想向您提供画线和填充区域的基本知识,这对于理解GDI 已经足够了。
GDI的结构
从程序员的观点来看,GDI由几百个函数调用和一些相关的数据类型、宏和结构组成。 但是在开始讲述这些函数的细节之前,让我们先从宏观上了解一下GDI的整体结构。
一、GDI原理
Windows 98 / NT / 2000 中的图形主要由GDI32.DLL动态链接库输出的函数来处理。在 Windows98中,这个 GDI32.DLL实际是利用 16位 GDI.EXE动态链接库来执行许多函数。在 Windows NT中,GDI.EXE只用于16位的程序。
这些动态链接库为您安装的视频显示器和任何打印机调用设备驱动程序中的例程。视频驱动程序访问视频显示器的硬件,打印机驱动程序将GDI命令转换为各种打印机能够理解的代码或命令。显然,不同的视频显示适配器和打印机要求不同的设备驱动程序。
因为PC兼容机上可以连接许多种不同的视频设备,所以,GDI的主要目的之一是支持与设备无关的图形。Windows程序应该能够毫无困难地在Windows支持的任意一种图形输出设备上运行,GDI通过将您的程序和不同输出设备的特性隔离开来的方法来达到这一目的。
图形输出设备分为两大类:光栅设备和矢量设备。大多数PC的输出设备是光栅设备,这意味着它们以点模式来表示图像,这类设备包括视频显示适配器、点阵打印机和激光打印机。矢量设备使用线来绘制图像,通常局限于绘图仪。
许多传统的计算机图形编程都是完全基于矢量的,这意味着使用矢量图形系统的程序是在硬件之上一定层次的抽象。输出设备用像素表示图形,但是程序与编程接口之间并不是用像素进行对话的。您当然可以使用 Windows GDI作为一个高层次的矢量绘制系统,同时也可以将它用于比较低层次的像素操作。
从这方面来看,Windows GDI和传统的图形接口语言之间的关系,就如同C和其他编程语言之间的关系一样。C以它在不同操作系统和环境之间的高度可移植性而闻名,然而C也以它允许程序员进行低层次系统调用而闻名,这些调用在其他高级语言中通常是不可能的。正如 C有时被认为是一种“高级汇编语言”一样,您可以认为 GDI是图形设备硬件之间的一种高层接口。
已经看到,默认时Windows使用基于像素的坐标系。大多数传统的图形语言使用“虚拟”坐标系,其水平和垂直轴的范围从 0~32767。虽然有些图形语言不让您使用像素坐标,但是 Windows GDI允许您使用两者之一(甚至基于物理度量的坐标系)。您可以使用虚拟坐标系以便程序远离硬件,或者也可以使用设备坐标系而完全接近硬件。
早期,许多用户在一种单色显示器上运行Windows。即使近几年,笔记本计算机还只有灰度级别。为此,GDI的设计保证了您可以在编写一个程序时不必过多地担心色彩问题——也就是说,Windows可以将色彩转换为灰度级别。甚至在今天,Windows 98使用的视频显示已经具有了不同的色彩能力(16色、256色、“高彩色”,以及“真彩色”)。虽然,彩色喷墨打印机的成本已经很低了,但是大多数用户仍然坚持使用黑白打印机。不加区别地使用这些设备是可以的,但是您的程序也应该能决定在某种显示设备上有多少色彩可以使用,从而最好地利用硬件。
当然,就如同您编写C程序时,为了使它在其他计算机上运行而遇到一些微妙的移植性问题一样,您也可能不小心让设备依赖性溜进您的Windows程序,这就是不与硬件完全隔离的代价。您还应该知道 Windows GDI的局限。虽然可以在显示器上到处移动图形对象,但GDI通常是一个静态的显示系统,只有有限的动画支持。如果需要为游戏编写复杂的动画,就应该研究一下 Microsoft DirectX,它提供了您需要的支持。
二、GDI函数调用
组成GDI的几百个函数调用可以分为几大类:
获取《或创建》和释放(或清除)设备描述表的函数 在绘图时需要设备描述表句柄。GetDC和RealseDC函数让您在非WM_PAINT 的消息期间来做到这一点,而BeginPaint和 EndPaint函数(虽然在技术上它们是 USER模 块而不是GDI模块的一部分)在进行绘图的WM_PAINT消息期间使用。获取有关设备描述表信息的函数GetTextMetrics函数来获取有关设备描述表中当前所选字体的尺寸信息。绘图函数 显然,在所有前提条件都得以满足之后,这些函数是真正重要的部分。我们使用TextOut函数在窗口的客户区显示一些文本。我们将看到,其他GDI函数还可以让您画线、填充区域。
设置和获取设备描述表参数的函数 设备描述表的“属性”决定有关绘图函数如何工作的细节。例如,用SetTextColor来指定TextOut(或者其他文本输出函数)所绘制的文本色彩。我们使用SetTextAlign来告诉GDI:TextOut函数中的文本串的开始位置应该在串的右边而不是默认的左边。设备描述表的所有属性都有默认值,当获取设备描述表时设置这些默认值。对于所有的Set函数,都有相应的Set函数,以允许您获取当前设备描述表属性。
使用GDI对象的函数 GDI在这里变得有点混乱。首先举一个例子:默认时使用GDI绘制的所有直线都是实线,并具有一个标准的宽度。您可能希望绘制更细的直线,或者是由一系列的点或短划线组成的直线。这种线的宽度和这种线的线型不是设备描述表的属性,而是一个“逻辑画笔”的特征。
您可以通过在CreatePen、CreatePenIndirect或ExtCreatePen函数中指定这些特征来创建一个逻辑画笔,这些函数返回一个逻辑画笔的句柄(虽然这些函数被认为是GDI的一部分,但是和大多数GDI函数调用不一样,它们不要求设备描述表的句柄)。要使用这个画笔,就要将画笔句柄选进设备描述表。我们认为,设备描述表中当前选中的画笔就是设备描述表的一个属性。这样,您画任何线都使用这个画笔,然后,您可以取消设备描述表中的画笔选择,并清除画笔对象。清除画笔对象是必要的,因为画笔定义占用了分配的内存空间。除了画笔以外,GDI对象还用于创建填充封闭区域的刷子、字体、位图,以及GDI的其他一些方面。
三、GDI图元
在屏幕或打印机上显示的图形类型本身可以被分为几类,通常被称为“图元”,它们是:
直线和曲线 线条是所有矢量图形绘制系统的基础。GDI支持直线、矩形、椭(包括椭圆的子集,也就是我们所说的“圆”)、椭圆圆周上的部分曲线即所谓“弧”,以及贝塞尔曲线,我们将在本章中分别对它们进行介绍。所有更复杂的曲线可由折线( polyline)代替,折线通过一组非常短的直线来定义一条曲线。线条用设备描述表中选中的当前画笔绘制。
填充区域 当一系列直线或者曲线封闭了一个区域时,该区域可以使用当前GDI画剧对象进行填充。这个画刷可以是实心色彩、图案(可以是一系列的水平、垂直或者对角标记),也可以是在区域内垂直或者水平重复的位图图像。
位图 位图是位的矩形数组,这些位对应于显示设备上的像素,它们是光栅图形的基础工具。位图通常用于在视频显示器或者打印机上显示复杂(一般都是真实的图像。位图还可以用于显示必须很快绘制的小图像,诸如图标、鼠标光标,以及在应用工具栏中出现的按钮等。GDI支持两种类型的位图——老的(虽然还非常有用)“设备有关”位图,是 GDI对象;新的(如 Windows 3 .0的)“设备无关”位图(或者DIB),可以存储在磁盘文件中。
文本 文本的数学味道不像计算机图形的其他方面那样浓。文本和几百年的传统印刷术有关,它被许多印刷工人作为一门艺术看待。因此,文本通常不仅是所有的计算机图形系统中最复杂的部分,而且也是最重要的部分(如果识字还是社会基本要求的话)。
用于定义GDI字体对象和获取字体信息的数据结构是Windows中最庞大的部分之一。从Windows 3.1开始,GDI开始支持 TrueType字体,该字体是在填充轮廓线基础上创建的,这样的填充轮廓线可由其他GDI函数处理。基于兼容性和存储大小的考虑,Windows 98继续支持旧式的基于位图的字体。
四、其他方面
GDI的其他方面无法这么容易地分类,它们是:
映射模式和交换虽然默认时以像素为单位进行绘图,但是并非局限于此。GDI映射模式允许您以英寸(或者甚至以几分之一英寸)、mm或者任何您想使用的单位来绘图(Windows NT还支持传统的以 3x3矩阵表示的“坐标变换”,这允许倾斜和旋转图形对象。不幸的是,在 Windows 98中不支持坐标变换)元文件元文件是以二进制形式存储的GDI命令的集合。元文件主要用于通过剪贴板传输矢量图形表示。
区域 区域是形状任意的复杂区,通常定义为较简单区域的布尔组合。在GDI内部,区域除了存储为最初用来定义区域的线条组合以外,还以一系列扫描线的形式存储。您可以将区域用于绘制轮廓、填充和剪裁。
路径 路径是GDI内部存储的直线和曲线的集合。路径可以用于绘图、填充和剪裁,还可以转换为区域。剪裁 绘图可以限制在客户区的某一部分中,这就是所谓的剪裁。剪裁区域可以是矩形或非短形,剪裁通常是通过区域或者路径来定义的。
调色板 定制调色板通常限于显示256色的显示器。Windows仅保留这些色彩之中的20种供系统使用,您可以改变其他236种色彩,以准确显示按位图形式存储的真实图像。打印虽然本章限于视频显示,但是您在本章中所学到的全部知识都适用于打印。
设备描述表
在开始绘图之前,让我们更精确地讨论一下设备描述表当您想在一个图形输出设备(诸如屏幕或者打印机)上绘图时,您首先必须获得个设备描述表(或者DC)的句柄。将句柄返回给程序时,Windows就给了您使用设备的权限。然后您在GDI函数中将这个句柄作为一个参数,向Windows标识您想在其上进行绘图的设备。
设备描述表中包含许多确定GDI函数如何在设备上工作的当前“属性”,这些属性允许传递给GDI函数的参数只包含起始坐标或者尺寸信息,而不必包含Windows在设备上显示对象时需要的所有其他信息。例如,调用TextOut时,您只需要在函数中给出设备描述表句柄、起始坐标、文本和文本的长度。您不必指定字体、文本色、文本后面的背景色彩,以及字符间距,因为这些属性都是设备描述表的一部分。当您想改变这些属性之一时,您调用一个可以改变设备描述表中属性的函数,以后针对该设备描述表的TextOut调用来使用改变后的属性。
一、获取设备描述表句柄
Windows提供了几种获取设备描述表句柄的方法。如果在处理一条消息时获取了设备描述表句柄,应该在退出窗口函数之前释放它(或者删除它)。一旦释放了句柄,它就不再有效了。最常用的获取并释放设备描述表句柄的方法是,在处理WM_PAINT消息时,使用BeginPaint和 EndPaint调用:
hdc =BeginPaint(hwnd,&ps );
…….
EndPaint ( hwnd,&ps );
变量 ps是类型为 PAINTSTRUCT的结构,该结构的 hdc字段是 BeginPaint返回的设备投述表句柄。 PAINTSTRUCT结构又包含一个名为rcPaint的RECT(短形)结构,cPaint定义一个包围窗口客户区无效范围的矩形。使用从BeginPaint获得的设备描述表句柄,只能在这个区域内绘图。BeginPaint调用使该区域有效。Windows程序还可以在处理非WM_PAINT消息时获取设备描述表句柄:
hdc=GetWindowDC(hwnd) ;
………
ReleaseDC(hwnd,hdc);
这个设备描述表适用于窗口句柄为 hwnd的客户区。这些调用与 BeginPaint和EdPaint的组合之间的基本区别是,利用从GetDC返回的句柄可以在整个客户区上绘图。当然,GetDC和ReleaseDC不使客户区中任何可能的无效区域变成有效。
Windows程序还可以获取适用于整个窗口(而
不仅限于窗口的客户区)的设备描述表句柄:
hdc=GetWindowDC(hwnd) ;
………
ReleaseDC(hwnd,hdc);
这个设备描述表除了客户区之外,还包括窗口的标题栏、菜单、滚动条和框架(frame)。
GetWindowsDC函数很少使用,如果想尝试用一用它,则必须捕获WM_NCPAINT(“非客户绘制”)消息,Windows使用该消息在窗口的非客户区上绘图。
BeginPaint、GetDC和GetWindowDC获得的设备描述表都与视频显示器上的某个特定窗口相关。获取设备描述表句柄的另一个更通用的函数是CreateDC:
hdc=CreateDC (pszDriver,pszDevice,pszOutput,pData) ;
………
DeleteDC(hdc);
例如,您可以通过下面的调用来获取整个屏幕的设备描述表句柄:
hdc=CreateDC (TEXT(“DISPLAY”) , NULL , NULL , NULL) ;
在窗口之外写东西一般是不合适的,但对于一些不同寻常的应用程序来说,这样做很方便(您还可以通过在调用GetDC时使用一个NULL参数,从而获取整个屏幕的设备描述表句柄,不过这一事实并没有载人文档)。
二、获取设备描述表信息
一个设备描述表通常是指一个物理显示设备,如视频显示器和打印机。通常,您需要获取有关该设备的信息,其中包括显示器的显示尺寸(单位为像素或者物理度量)和色彩范围。您可以通过调用GetDeviceCaps(“获取设备能力”)函数来获取这些信息:
iValue = GetDeviceCaps (hdc, ilndex);
其中,参数 ilndex取值为 WINGDI.H头文件中定义的29个标识符之一。例如,当ilndex的值为HORZRES时,将导致GetDeviceCaps返回设备的宽度(单位为像素);当GetDeviceCaps的值为 VERTRES时,将导致GetDeviceCaps返回设备的高度(单位为像素)。如果hdc屏幕设备描述表的句柄,则GetDeviceCaps返回打印机显示区域的高度和宽度,它们也是以像素为单位的。
还可以使用GetDeviceCaps来确定设备处理各种不同类型的图形的能力,这通常对于处理视频显示并不十分重要,但是对于使用打印设备却是非常重要。
例如,大多数笔式绘图仪不能画位图图像,而GetDeviceCaps就可以将这一情况告诉您。
三、 WINDOWS GDI 函数
GetDC()函数
功能: 用于获取指定窗体客户区的显示器设备描述表句柄。
函数声明: HDC GetDC(HWND hWnd);
参数:hWnd:窗体句柄。
返回值:如果成功,返回指定窗体客户区的设备描述表句柄,否则返回NULL。
窗体客户区绘图
一、步骤
1.获取设备环境句柄
2.获取绘图工具
3.选择绘图工具
4.利用选择的绘图工具绘图
5.恢复原来的绘图工具并删除选择的绘图工具
6.释放设备环境句柄
ReleaseDC () 函数
功能:用于释放设备描述表句柄。
函数声明:int ReleaseDC(HWND hWnd, HDC hDC);
参数:hWnd:窗体句柄;
hDC: 设备描述表句柄。
返回值:如果成功,返回1,否则返回0。
注释:GetDC()函数与ReleaseDC()函数必须匹配使用,而且不能在WM_PAINT中使用。
BeginPaint()函数
功能:用于获取窗体客户区绘图的相关信息,并为绘图做好准备。
函数声明:HDC BeginPaint(HWND hwnd, LPPAINTSTRUCT lpPaint);
参数:hWnd:窗体句柄;
lpPaint:指向绘图结构类型的指针变量。
返回值:如果成功,返回指定窗体客户区的设备描述表句柄,否则返回NULL。
PAINTSTRUC 结构类型
PAINTSTRUCT 结构类型包含一些窗体绘图的基本信息,定义如下:
typedef struct tagPAINTSTRUCT
{
HDC hdc; // 窗体句柄;
BOOL fErase; // 窗体客户区背景是否删除标记;
RECT rcPaint; // 需要绘制的窗体客户区区域标记;
BOOL fRestore; // 系统保留;
BOOL fIncUpdate; // 系统保留;
BYTE rgbReserved[32]; // 系统保留;
} PAINTSTRUCT;
RECT 结构类型
RECT 结构类型用于描述一个矩形,定义如下:
typedef struct _RECT
{
LONG left; // 矩形左边界坐标;
LONG top; // 矩形上边界坐标;
LONG right; // 矩形右边界坐标;
LONG bottom; // 矩形下边界坐标;
} RECT;
EndPaint()函数
功能:用于结束窗体客户区绘图。
函数声明:BOOL EndPaint(HWND hWnd, CONST PAINTSTRUCT *lpPaint);
参数:hWnd:窗体句柄;
lpPaint:指向绘图结构类型的指针变量。
返回值:如果成功,返回TRUE,否则返回FALSE。
注释:BeginPaint()函数与EndPaint()函数必须匹配使用,而且只能在WM_PAINT中使用。
SetPixel()函数
功能:用于在窗体客户区绘制点(像素)。
函数声明:COLORREF SetPixel(HDC hdc, int X, int Y, COLORREF crColor);
参数:
hdc: 设备描述表句柄;
X: 点(像素)的x坐标;
Y: 点(像素)的y坐标;
crColor:点(像素)的颜色;
返回值:如果成功,返回点(像素)的颜色值,否则返回-1。
COLORREF 类型
COLORREF 类型用于描述32位
RGB颜色值,
其十六进制颜色值定义如下:0x00bbggrr
RGB()宏
功能: 用于设置颜色。
宏定义: COLORREF RGB(BYTE bRed, BYTE bGreen, BYTE bBlue);
参数:
bRed: 红色辉度级别;
bGreen:绿色辉度级别;
bBlue: 兰色辉度级别;
返回值:返回颜色值。
CreatePen()函数
功能: 用于建立逻辑笔。
函数声明:HPEN CreatePen(int fnPenStyle, int nWidth, COLORREF crColor);
参数:
fnPenStyle:逻辑笔类型;
nWidth: 逻辑笔宽度;
crColor: 逻辑笔颜色。
逻辑笔类型:
PS_SOLID
PS_DASH
PS_DOT
PS_DASHDOTDOT
PS_INSIDEFRAME
PS_NULL
返回值:如果成功,返回逻辑笔句柄,否则返回NULL。
MoveToEx()函数
功能:用于窗体客户区移动逻辑笔。
函数声明: BOOL MoveToEx(HDC hdc, int X, int Y, LPPOINT lpPoint);
参数:
hdc: 设备描述表句柄;
X: 移动位置的x坐标;
Y: 移动位置的y坐标;
lpPoint:原位置坐标;
返回值:如果成功,返回TRUE,否则返回FALSE。
LineTo()函数
功能:用于在窗体客户区当前位置画线。
函数声明: BOOL LineTo(HDC hdc, int nXEnd, int nYEnd);
参数:
hdc: 设备描述表句柄;
nXEnd:目标点的x坐标;
nYEnd:目标点的y坐标。
返回值:
如果成功,返回TRUE,否则返回FALSE。
Ellipse()函数
功能:用于窗体客户区绘制椭圆。
函数声明:BOOL Ellipse(HDC hdc,int nLeftRect,int nTopRect,int nRightRect,int nBottomRect);
参数:
hdc: 设备描述表句柄;
nLeftRect: 椭圆外边界左边界坐标;
nTopRect: 椭圆外边界上边界坐标;
nRightRect: 椭圆外边界右边界坐标;
nBottomRect:椭圆外边界下边界坐标;
返回值:如果成功,返回TRUE,否则返回FALSE。
SelectObject()函数
功能:用于选择一种绘图工具柄将其装入设备描述表。
函数声明:HGDIOBJ SelectObject(HDC hdc,HGDIOBJ hgdiobj);
参数:
hdc:设备描述表句柄;
hgdiobj:绘图工具句柄。
返回值:如果成功,返回原设备描述表中绘图工具句柄,否则返回NULL。
?
DeleteObject()函数
功能:用于删除一个绘图工具。
函数声明:BOOL DeleteObject(HGDIOBJ hObject);
参数:
hObject:绘图工具句柄。
返回值:如果成功,返回TRUE,否则返回FALSE。
TextOut()函数
功能:用于在窗体客户区输出文本。
函数声明:BOOL TextOut(HDC hdc,int nXStart,int nYStart,LPCTSTR lpString,int cbString);
参数:
hdc: 设备描述表句柄;
nXStart: 文本左上角坐标;
nYStart: 文本左上角坐标;
lpString:指向文本字符串的指针变量;
cbString:文本字符串长度;
返回值:如果成功,返回TRUE,否则返回FALSE。
SetBkMode()函数
功能:用于设置窗体客户区背景填充方式。
函数声明:int SetBkMode(HDC hdc, int iBkMode);
参数:
hdc: 设备描述表句柄;
iBkMode:填充方式。
OPAQUE: 不透明方式;
TRANSPARENT:透明方式;
返回值:如果成功,返回填充方式,否则返回0。
SetTextColor()函数
功能:用于文本颜色。
函数声明: COLORREF SetTextColor(HDC hdc, COLORREF crColor);
参数:
hdc: 设备描述表句柄;
crColor:文本笔颜色。
返回值:如果成功,返回原文本笔颜色,否则返回 CLR_INVALID。
CreateFont()函数
功能:用于建立逻辑字模。
函数声明:
HFONT CreateFont(int nHeight,
int nWidth,
int nEscapement,
int nOrientation,
int fnWeight,
DWORD fdwItalic,
DWORD fdwUnderline,
DWORD fdwStrikeOut,
DWORD fdwCharSet,
DWORD fdwOutputPrecision,
DWORD fdwClipPrecision,
DWORD fdwQuality,
DWORD fdwPitchAndFamily,
LPCTSTR lpszFace);
参数:
nHeight:逻辑字符高度;
nWidth:逻辑字符宽度;
nEscapement:字符旋转角度;
nOrientation:字符与基线的夹角;
fnWeight:字符黑度;
fdwItalic:是否斜体;
fdwUnderline:是否代下划线;
fdwStrikeOut:非零时为删除线字体;
fdwCharSet:字符集;
fdwOutputPrecision:输出精度;
fdwClipPrecision:剪切精度
fdwQuality:输出质量控制;
fdwPitchAndFamily:字符间距和字符集;
lpszFace:引用名。
返回值:如果成功,返回逻辑字模句柄,否则返回 NULL。
三、其它函数
SetTimer()函数
功能:用于建立产生时间消息的时间控件。
函数声明:UINT SetTimer(HWND hWnd,UINT nIDEvent,UINT uElapse,TIMERPROC lpTimerFunc);
参数:
hWnd: 窗体句柄;
nIDEvent: 时间控件的索引号;
uElapse: 产生时间消息的时间间隔;
lpTimerFunc:时间控件控制过程指针;
返回值:如果成功,返回时间控件的索引号,否则返回0。
KillTimer()函数
功能:用于取消时间控件。
函数声明:BOOL KillTimer(HWND hWnd, UINT uIDEvent);
参数:
hWnd: 窗体句柄;
nIDEvent:时间控件的索引号;
返回值:如果成功,返回TRUE,否则返回FALSE。
?
GetClientRect()函数
功能:用于获取窗体客户区范围。
函数声明:BOOL GetClientRect(HWND hWnd, LPRECT lpRect);
参数:
hWnd: 窗体句柄;
lpRect:指向RECT结构类型的指针变量。
返回值:如果成功,返回TRUE,否则返回FALSE。
应用实例
画点和线:
我们已谈论过Windows图形设备接口将图形输出设备的设备驱动程序与计算机连在一起的方式。在理论上,只要提供SetPixel和GetPixel函数,就可以使用图形设备驱动程序绘制一切东西了。其余的一切都可以使用 GDI模块中实现的更高级的例程来处理。例如画线时,只需GDI调用 SetPixel数次,并适当地调整X和Y坐标。
在实际情况中,也的确可以仅使用SetPixel和GetPixel函数进行您需要的任何绘制。您也可以在这些函数的基础上设计出简洁和构造良好的图形编程系统。唯一的问题是性能。如果一个函数通过几次调用才能到达。 SetPixel函数,那么它运行起来会非常慢。如果一个图形系统画线和进行其他复杂的图形操作是在设备驱动程序的层次上,它就会更有效得多,因为设备驱动程序对完成这些操作的代码进行了优化。此外,一些视频适配器卡包含了图形协处理器,它允许视频硬件自己绘制图形。
直线
Windows可以画直线、椭圆线(椭圆圆周上的曲线)。 Windows画线函数是:
LineTo画直线;
Poiyline和PoiylineTo画一系列相连的直线;
PolyPolyline 画多组相连的线;
Arc 画椭圆线;
设备描述表的5个属性影响着用这些函数所画线的外观:当前画笔的位置(仅用于LineTo、PoiylineTo和 Arc)、画笔、背景方式、背景色和绘图模式。
画一条直线,必须调用两个函数。第一个函数指定了线的开始点,第二个函数指定了线的终点:
MoveToEx(hdc,xBeg,yBeg,NULL);
LineTo (hdc, xEnd,yEnd);
MoveToEx实际上不会画线,它只是设置了设备描述表的“当前位置”属性。然后LineTo 函数从当前的位置到它所指定的点画一条直线。当前位置只是用于其他几个GDI函数的开始点。在默认的设备描述表中,当前位置最初设置在点(0,0)。如果在调用LineTo之前没有设置当前位置,那么它将从客户区的左上角开始画线。
一、画点程序
?
#include <windows.h>
#include <time.h>
?
#define IDC_TIMER 100
?
int WINAPI WinMain(HINSTANCE, HINSTANCE,LPSTR,int);
LRESULT CALLBACK WndProc(HWND, UINT,WPARAM,LPARAM);
int randomize();
int random(int);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
randomize();
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance,(LPCTSTR)IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL,IDC_ARROW);
wcex.hbrBackground = (HBRUSH)COLOR_ACTIVECAPTION;
wcex.lpszMenuName = NULL;
wcex.lpszClassName = "WndCls";
wcex.hIconSm = LoadIcon(hInstance,(LPCTSTR)IDI_APPLICATION);
if(!RegisterClassEx(&wcex)) return FALSE;
int SW_XFS = GetSystemMetrics(SM_CXSCREEN);
int SW_YFS = GetSystemMetrics(SM_CYSCREEN);
HWND hWnd;
hWnd = CreateWindowEx(WS_EX_CLIENTEDGE,
"WndCls",
"Drawing Spot of Windows Program",
WS_OVERLAPPEDWINDOW,
0,
0,
SW_XFS,
SW_YFS-25,
NULL,
NULL,
hInstance,
NULL);
if(!hWnd) return FALSE;
ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
SetTimer(hWnd,IDC_TIMER,10,NULL);
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static RECT rect;
static int ww,wh;
HDC hDC;
static int x,y;
COLORREF color;
switch (message)
{
case WM_CREATE:
GetClientRect(hWnd,&rect);
ww = rect.right-rect.left;
wh = rect.bottom-rect.top;
break;
case WM_SIZE:
ww = LOWORD(lParam);
wh = HIWORD(lParam);
break;
case WM_TIMER:
hDC = GetDC(hWnd);
x = rect.left+random(ww);
y = rect.top+random(wh);
color = RGB(random(128)+127,random(128)+127,random(128)+127);
SetPixelV(hDC,x,y,color);
ReleaseDC(hWnd,hDC);
break;
case WM_DESTROY:
KillTimer(hWnd,IDC_TIMER);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd,message,wParam,lParam);
}
return 0;
}
?
int randomize()
{
srand((unsigned int)time(NULL));
return 0;
}
int random(int number)
{
int rn;
rn = (int)(((long)rand()*number)/(RAND_MAX+1));
return rn;
}
二、画线程序
#include <windows.h>
int WINAPI WinMain(HINSTANCE, HINSTANCE,LPSTR,int);
LRESULT CALLBACK WndProc(HWND,UINT, WPARAM,LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance,(LPCTSTR)IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL,IDC_ARROW);
wcex.hbrBackground = (HBRUSH)COLOR_ACTIVECAPTION;
wcex.lpszMenuName = NULL;
wcex.lpszClassName = "WndCls";
wcex.hIconSm = LoadIcon(hInstance,(LPCTSTR)IDI_APPLICATION);
if(!RegisterClassEx(&wcex)) return FALSE;
int SW_XFS = GetSystemMetrics(SM_CXSCREEN);
int SW_YFS = GetSystemMetrics(SM_CYSCREEN);
HWND hWnd;
hWnd = CreateWindowEx(WS_EX_CLIENTEDGE,
"WndCls",
"Drawing Line of Windows Program",
WS_OVERLAPPEDWINDOW,
0,
0,
SW_XFS,
SW_YFS-25,
NULL,
NULL,
hInstance,
NULL);
if(!hWnd) return FALSE;
ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int y,y0;
static RECT rect;
static int ww,wh;
HPEN hOldPen,hNewPen;
HDC hDC;
PAINTSTRUCT ps;
COLORREF color;
switch (message)
{
case WM_CREATE:
GetClientRect(hWnd,&rect);
ww = rect.right-rect.left;
wh = rect.bottom-rect.top;
break;
case WM_SIZE:
ww = LOWORD(lParam);
wh = HIWORD(lParam);
break;
case WM_PAINT:
hDC = BeginPaint(hWnd,&ps);
y0 = rect.top;
for(y=0;y<wh/3;y++)
{
color = RGB(y+255-wh/3,0,0);
hNewPen = CreatePen(PS_SOLID,0,color);
hOldPen = (HPEN)SelectObject(hDC,hNewPen);
MoveToEx(hDC,rect.left,y+y0,NULL);
LineTo(hDC,rect.right,y+y0);
hNewPen = (HPEN)SelectObject(hDC,hOldPen);
DeleteObject(hNewPen);
}
y0 = rect.top+wh/3;
for(y=0;y<wh/3;y++)
{
color = RGB(0,y+255-wh/3,0);
hNewPen = CreatePen(PS_SOLID,0,color);
hOldPen = (HPEN)SelectObject(hDC,hNewPen);
MoveToEx(hDC,rect.left,y+y0,NULL);
LineTo(hDC,rect.right,y+y0);
hNewPen = (HPEN)SelectObject(hDC,hOldPen);
DeleteObject(hNewPen);
}
y0 = rect.top+2*wh/3;
for(y=0;y<wh/3;y++)
{
color = RGB(0,0,y+255-wh/3);
hNewPen = CreatePen(PS_SOLID,0,color);
hOldPen = (HPEN)SelectObject(hDC,hNewPen);
MoveToEx(hDC,rect.left,y+y0,NULL);
LineTo(hDC,rect.right,y+y0);
hNewPen = (HPEN)SelectObject(hDC,hOldPen);
DeleteObject(hNewPen);
}
EndPaint(hWnd,&ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd,message,wParam,lParam);
}
return 0;
}
三、画椭圆程序
?
#include <windows.h>
#include <time.h>
?
#define IDC_TIMER 100
?
int WINAPI
WinMain(HINSTANCE,HINSTANCE,LPSTR,int);
LRESULT CALLBACK WndProc(HWND, UINT,WPARAM,LPARAM);
int randomize();
int random(int);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
randomize();
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance,(LPCTSTR)IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL,IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = "WndCls";
wcex.hIconSm = LoadIcon(hInstance,(LPCTSTR)IDI_APPLICATION);
if(!RegisterClassEx(&wcex)) return FALSE;
int SW_XFS = GetSystemMetrics(SM_CXSCREEN);
int SW_YFS = GetSystemMetrics(SM_CYSCREEN);
HWND hWnd;
hWnd = CreateWindowEx(WS_EX_CLIENTEDGE,
"WndCls",
"Drawing Ellipse of Windows Program",
WS_OVERLAPPEDWINDOW,
0,
0,
SW_XFS,
SW_YFS-25,
NULL,
NULL,
hInstance,
NULL);
if(!hWnd) return FALSE;
ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
SetTimer(hWnd,IDC_TIMER,500,NULL);
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static RECT rect;
static int ww,wh;
HPEN hOldPen,hNewPen;
HBRUSH hOldBrush,hNewBrush;
LOGBRUSH LogBrush;
HDC hDC;
COLORREF color;
switch (message)
{
case WM_CREATE:
GetClientRect(hWnd,&rect);
ww = rect.right-rect.left;
wh = rect.bottom-rect.top;
break;
case WM_SIZE:
ww = LOWORD(lParam);
wh = HIWORD(lParam);
break;
case WM_TIMER:
hDC = GetDC(hWnd);
color = RGB(250*random(2),250*random(2),250*random(2));
hNewPen = CreatePen(PS_SOLID,0,color);
hOldPen = (HPEN)SelectObject(hDC,hNewPen);
LogBrush.lbStyle = BS_SOLID;
LogBrush.lbHatch = HS_CROSS;
LogBrush.lbColor = color;
hNewBrush = CreateBrushIndirect(&LogBrush);
hOldBrush = (HBRUSH)SelectObject(hDC,hNewBrush);
Ellipse(hDC,random(ww),random(wh),random(ww),random(wh));
hNewPen = (HPEN)SelectObject(hDC,hOldPen);
hNewBrush = (HBRUSH)SelectObject(hDC,hOldBrush);
DeleteObject(hNewPen);
DeleteObject(hNewBrush);
ReleaseDC(hWnd,hDC);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd,message,wParam,lParam);
}
return 0;
}
?
int randomize()
{
srand((unsigned int)time(NULL));
return 0;
}
?
int random(int number)
{
int rn;
rn = (int)(((long)rand()*number)/(RAND_MAX+1));
return rn;
}
四、输出文本程序
#include <windows.h>
int WINAPI
WinMain(HINSTANCE,HINSTANCE,LPSTR,int);
LRESULT CALLBACK
WndProc(HWND,UINT,WPARAM,LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance,(LPCTSTR)IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)COLOR_INACTIVECAPTION;
wcex.lpszMenuName = NULL;
wcex.lpszClassName = "WndCls";
wcex.hIconSm = LoadIcon(hInstance,(LPCTSTR)IDI_APPLICATION);
if(!RegisterClassEx(&wcex)) return FALSE;
int SW_XFS = GetSystemMetrics(SM_CXSCREEN);
int SW_YFS = GetSystemMetrics(SM_CYSCREEN);
HWND hWnd;
hWnd = CreateWindowEx(WS_EX_CLIENTEDGE,
"WndCls",
"Drawing text of Windows Program",
WS_OVERLAPPEDWINDOW,
0,
0,
SW_XFS,
SW_YFS-25,
NULL,
NULL,
hInstance,
NULL);
if(!hWnd) return FALSE;
ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int ch,cw;
char *s;
int sl;
int xs,ys;
static RECT rect;
static int ww,wh;
HFONT hOldFont,hNewFont;
COLORREF color;
HDC hDC;
PAINTSTRUCT ps;
switch (message)
{
case WM_CREATE:
GetClientRect(hWnd,&rect);
ww = rect.right-rect.left;
wh = rect.bottom-rect.top;
break;
case WM_SIZE:
ww = LOWORD(lParam);
wh = HIWORD(lParam);
break;
case WM_PAINT:
hDC = BeginPaint(hWnd,&ps);
SetBkMode(hDC,TRANSPARENT);
ch = 48;
cw = 24;
hNewFont = CreateFont(-ch,
cw,
0,
0,
FW_BOLD,
FALSE,
FALSE,
FALSE,
GB2312_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
PROOF_QUALITY,
DEFAULT_PITCH,
"隶书");
hOldFont = (HFONT)SelectObject(hDC, hNewFont);
s = "在窗体客户区输出文本";
sl = strlen(s);
xs = (ww-cw*sl)/2;
ys = (wh)/2-ch;
color = RGB(255,0,0);
SetTextColor(hDC,color);
TextOut(hDC,xs+1,ys+1,s,sl);
color = RGB(255,255,0);
SetTextColor(hDC,color);
TextOut(hDC,xs,ys,s,sl);
hNewFont = (HFONT)SelectObject(hDC,hOldFont);
DeleteObject(hNewFont);
EndPaint(hWnd,&ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd,message,wParam,lParam);
}
return 0;
}
直线
Windows可以画直线、椭圆线(椭圆圆周上的曲线)。
Windows画线函数是:
LineTo画直线;Poiyline和PoiylineTo画一系列相连的直线;PolyPolyline画多组相连的线;Arc 画椭圆线;
设备描述表的5个属性影响着用这些函数所画线的外观:当前画笔的位置(仅用于LineTo、PoiylineTo和 Arc)、画笔、背景方式、背景色和绘图模式。
画一条直线,必须调用两个函数。第一个函数指定了线的开始点,第二个函数指定了线的终点: