本文出自《Visual C++数字图像处理技术详解》——刘海波 沈晶 郭耸
GDI 接口是Microsoft Whistler操作系统中的一部分,它是GDI的一个新版本,不仅在GDI基础上添加许多新特性而且对原有的GDI功能进行优化。在为开发人员提供的二维矢量图形、文本、图像处理、区域、路径以及图形数据矩阵等方面构造了一系列相关的类,如Bitmap(位图类)、Brush(画刷类)、Color(颜色类)、Font(字体类)、Graphics(图形类)、Image(图像类)、Pen(画笔类)和Region(区域类)等。其中,图形类Graphics是GDI接口中的一个核心类,许多绘图操作都可用它来完成。
我们首先介绍一下GDI 的新特性以及其编程方式的改变,然后介绍用Visual C.NET在基于对话框和单文档/多文档等应用程序中使用GDI 的一般方法。
GDI 新特性
GDI 与GDI相比,增加了下列新的特性:
1、渐变画刷
以往GDI实现颜色渐变区域的方法是通过使用不同颜色的线条来填充一个裁剪区域而达到的。现在GDI拓展了GDI功能,提供线型渐变和路径渐变画刷来填充一个图形、路径和区域,甚至也可用来绘制直线、曲线等。这里的路径可以视为由各种绘图函数产生的轨迹。
2、样条曲线
对于曲线而言,最具实际意义的莫过于样条曲线。样条曲线是在生产实践的基础上产生和发展起来的。模线间的设计人员在绘制模线时,先按给定的数据将型值点准确地"点"到图板上。然后,采用一种称为"样条"的工具(一根富有弹性的有机玻璃条或木条),用压铁强迫它通过这些型值点,再适当调整这些压铁,让样条的形态发生变化,直至取得合适的形状,才沿着样条画出所需的曲线。如果我们把样条看成弹性细梁,那么压铁就可看成作用在这梁上的某些点上的集中力。GDI的Graphics:: DrawCurve函数中就有一个这样的参数用来调整集中力的大小。除了样条曲线外,GDI还支持原来GDI中的Bezier曲线。
3、持久的路径对象
我们知道,在GDI中,路径是隶属于一个设备环境(上下文),也就是说一旦设备环境指针超过它的有效期,路径也会被删除。而GDI是使用Graphics对象来进行绘图操作,并将路径操作从Graphics对象分离出来,提供一个GraphicsPath类供用户使用。这就是说,我们不必担心路径对象会受到Graphics对象操作的影响,从而可以使用同一个路径对象进行多次的路径绘制操作。
4、矩阵和矩阵变换
在图形处理过程中常需要对其几何信息进行变换以便产生复杂的新图形,矩阵是这种图形几何变换最常用的方法。为了满足人们对图形变换的需求,GDI提供了功能强大的Matrix类来实现矩阵的旋转、错切、平移、比例等变换操作,并且GDI还支持Graphics图形和区域(Region)的矩阵变换。
5、Alpha混色
在图像处理中,Alpha用来衡量一个像素或图像的透明度。在非压缩的32位RGB图像中,每个像素是由四个部分组成:一个Alpha通道和三个颜色分量(R、G和B)。当Alpha值为0时,该像素是完全透明的,而当Alpha值为255时,则该像素是完全不透明。
Alpha混色是将源像素和背景像素的颜色进行混合,最终显示的颜色取决于其RGB颜色分量和Alpha值。它们之间的关系可用下列公式来表示:
显示颜色 = 源像素颜色 X alpha / 255 背景颜色 X (255 - alpha) / 255
GDI 的Color类定义了ARGB颜色数据类型,从而可以通过调整Alpha值来改变线条、图像等与背景色混合后的实际效果。
除了上述新特性外,GDI 还将支持重新着色、色彩修正、消除走样、元数据以及Graphics容器等特性。
GDI 编程模块的变化
为了简化GDI 的编程开发过程,Microsoft对GDI 的编程模块作了一些调整,这主要体现在以下几个方面:
1、不再使用设备环境或句柄
我们知道,在使用GDI绘图时,必须要指定一个设备环境(DC)。MFC为设备环境提供了许多由基类CDC派生的设备环境类,如CPaintDC、CClientDC和CWindowDC等,用来将某个窗口或设备与设备环境类的句柄指针关联起来,所有的绘图操作都与该句柄有关。而GDI不再使用这个设备环境或句柄,取而代之是使用Graphics对象。
与设备环境相类似,Graphics对象也是将屏幕的某一个窗口与之相关联,并包含绘图操作所需要的相关属性。但是,只有这个Graphics对象与设备环境句柄还存在着联系,其余的如Pen、Brush、Image和Font等对象均不再使用设备环境。
2、绘图方式的变化
先来看看同样绘制一条从点(20, 10)到点(200, 100)直线的GDI和GDI代码,假设这些代码都是添加在OnDraw函数中。
GDI绘制该直线的代码如下:
程序代码: void CMyView::OnDraw(CDC* pDC) { CMyDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); CPen newPen( PS_SOLID, 3, RGB(255, 0, 0) ); CPen* pOldPen = pDC->SelectObject( &newPen ); pDC->MoveTo( 20, 10 ); pDC->LineTo( 200, 100); pDC->SelectObject( pOldPen ); } |
程序代码: void CMyView::OnDraw(CDC* pDC) { CMyDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); using namespace Gdiplus; // 使用名称空间 Graphics graphics( pDC->m_hDC ); Pen newPen( Color( 255, 0, 0 ), 3 ); graphics.DrawLine(&newPen, 20, 10, 200, 100); } |
6、用Visual C .NET使用GDI 的一般方法
在Visual C .NET使用GDI 一般遵循下列步骤:
(1) 在应用程序中添加GDI的包含文件gdiplus.h以及附加的类库gdiplus.lib。通常gdiplus.h包含文件添加在应用程序的stdafx.h文件中,而gdiplus.lib可用两种进行添加:第一种是直接在stdafx.h文件中添加下列语句:
#pragma comment( lib, "gdiplus.lib" ) |
图1
(2) 在应用程序项目的应用类中,添加一个成员变量,如下列代码:
程序代码: ULONG_PTR m_gdiplusToken; |
程序代码: int CEx_GDIPlusApp::ExitInstance() { Gdiplus::GdiplusShutdown(m_gdiplusToken); return CWinApp::ExitInstance(); } |
程序代码: BOOL CEx_GDIPlusApp::InitInstance() { CWinApp::InitInstance(); Gdiplus::GdiplusStartupInput gdiplusStartupInput; Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL); ... } |
程序代码: void CEx_GDIPlusView::OnDraw(CDC* pDC) { CEx_GDIPlusDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); using namespace Gdiplus; Graphics graphics( pDC->m_hDC ); Pen newPen( Color( 255, 0, 0 ), 3 ); HatchBrush newBrush( HatchStyleCross, Color(255, 0, 255, 0), Color(255, 0, 0, 255)); // 创建一个填充画刷,前景色为绿色,背景色为蓝色 graphics.DrawRectangle( &newPen, 50, 50, 100, 60); // 在(50,50)处绘制一个长为100,高为60的矩形 graphics.FillRectangle( &newBrush, 50, 50, 100, 60); // 在(50,50)处填充一个长为100,高为60的矩形区域 } |
图2
2. 在基于对话框应用程序中使用GDI
步骤如下:
(1) 创建一个默认的基于对话框的应用程序Ex_GDIPlusDlg。
(2) 打开stdafx.h文件添加下列代码:
程序代码: #include #pragma comment( lib, "gdiplus.lib" ) |
(4) 在 CEx_GDIPlusDlgApp类的属性窗口中,单击"重写"工具按钮,为该添加ExitInstance的重载:
(5) 定位到CEx_GDIPlusDlgApp::InitInstance函数处,添加下列GDI 初始化代码:
(6) 定位到CEx_GDIPlusDlgDlg::OnPaint函数处,添加下列GDI 代码:
(7) 编译并运行,结果如图3所示。
图3
从上述例子可以看出,只要能获得一个窗口的设备环境指针,就可构造一个Graphics对象,从而可以在其窗口中进行绘图,我们不必在像以往那样使用Invalidate/UpdateWindow来防止Windows对对话框窗口进行重绘。