GDI+编程小结(三) 下载本文

http://www.mybdqn.com/

三、GDI+使用过程中出现的问题:

1)、在VC调用过程中,重绘问题。 GDI+程序往往在窗口被创建时,不能自动重画(没有自动调用OnDraw函数)。解决办法是,在创建图形对象后,自己调用视图类(基类CWnd)的成员函数RedrawWindow: BOOL RedrawWindow(LPCRECT lpRectUpdate = NULL, CRgn* prgnUpdate = NULL, UINT flags = RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);

其中,lpRectUpdate为窗口客户区中需要重画的矩形(NULL表示整个客户区矩形重画)、prgnUpdate表示需要重画的区域(NULL表示整个客户区矩形区域重画)、flags为特征标志(RDW_INVALIDATE指定范围无效、RDW_UPDATENOW立即更新、RDW_ERASE擦除背景)。 例如:

Graphics graph(pDC->m_hDC);

RedrawWindow(); // 一般输入参数取缺省值即可

// 相当于Invalidate(); UpdateWindow();的综合效果 // this->Invalidate();

注意:不能在OnDraw和OnPaint函数中调用RedrawWindow,那样会造成反复调用,产生死循环。

其实,只要GDI+的两个初始化语句放置的位置对(必须放在CWinApp:: InitInstance ();语句之前),就不会出现该问题。 2)、new 问题。

不能使用new来动态创建GDI+对象。解决办法是:打开(缺省)位于C:/Program Files/Microsoft Visual Studio 8/VC/PlatformSDK/Include目录中的GdiplusBase.h头文件,并注释掉里面GdiplusBase类的内容(该类其实只含new、new[]、delete和delete[]这四个运算符的重载),使其成为一个空类(不要删除整个类)。

对实验室中的写保护机器,不能修改安装目录中的GdiplusBase.h头文件,解决办法是: ?将该头文件复制到你的项目目录中;

?注释掉该头文件里面GdiplusBase类的内容(保留类定义);

?在你项目中所有的#include 语句之前,包含\头文件,形如: #include \#include

?则编译系统会优先包含项目目录中的gdiplusBase.h头文件,从而屏蔽掉原来位于平台SDK的Include目录中的同名头文件。 // 技巧

你也可以在有些使用new的地方改用&,例如你可以将代码 Pen *pPen = new Pen(Color::Red); // 在C#可运行 改为

Pen *pPen = &Pen(Color::Red); 又例如,你也可以将代码:

graphics.DrawPolygon(new Pen(Color::Green), points, n); 改为

Pen pen(Color::Green);

graphics.DrawPolygon(&pen, points, n); 或直接改为

http://www.mybdqn.com/

graphics.DrawPolygon(&Pen(Color::Green), points, n);

3)、调试问题

现在版本的VC05存在许多Bug,特别是GDI+程序在调试时的问题就更多。解决办法是: ?在编译运行时,不使用Debug配置,而改用Release配置; ?运行时不使用调试运行(F5),而改用不调试直接运行(Ctrl +F5); ?最好是用静态链接的MFC库,而不用DLL动态库。

常用的调试方法有:

?使用MessageBox信息框: ?在视图类中的常用格式为 MessageBox(L\提示信息\

?在应用程序类和文档类中的常用格式为

MessageBox(NULL, L\提示信息\标题\ // Win32 API ?设置断点,然后逐步运行(F10)或F11。 ?运行当前位置,然后逐步运行(F10)

?利用调试界面中的“局部变量”和“监视1”等窗口,来查看变量当前的值

4)、用MFC开发GDI+程序

创建一个名为GDIPlusDemo的MFC单文档应用程序项目。

首先要进行GDI+系统的初始化,这需要在应用程序类CGDIPlusDemoApp中声明一个成员变量:

ULONG_PTR m_gdiplusToken; // ULONG PTR 为int64 类型

并在该类的初始化函数CGDIPlusDemoApp::InitInstance() 中加入以下代码来对GDI+进行初始化:

GdiplusStartupInput gdiplusStartupInput;

GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL); 注意:这两个语句必须加在应用程序类的InitInstance函数中的 CWinApp:: InitInstance ();

语句之前,不然以后会造成视图窗口不能自动重画、程序中不能使用字体等等一系列问题。 还要在CGDIPlusDemoApp::ExitInstance() 函数中加入以下代码来关闭GDI +: GdiplusShutdown(m_gdiplusToken);

上面的InitInstance和ExitInstance都是应用程序类的重写型成员函数。而且,缺省时无ExitInstance,需要自己利用属性窗口来添加(不要手工添加)。 接下来就可以利用GDI+进行绘图了。 在OnDraw函数中画图:

CGDIPlusDemoView::OnDraw (CDC* pDC) {

Graphics graph(pDC->m_hDC); // 创建图形对象 Pen bluePen(Color(255, 0, 0, 255)); // 创建蓝色笔 Pen redPen(Color(255, 255, 0, 0)); // 创建红色笔 int y = 255; // y的初值

for (int x = 0; x < 256; x += 5) { // 绘制红蓝网线 graph.DrawLine(&bluePen, 0, y, x, 0);

http://www.mybdqn.com/

graph.DrawLine(&redPen, 255, x, y, 255); y -= 5; }

for (y = 0; y < 256; y++) { // 画绿色透明度水平渐变的线(填满正方形) Pen pen(Color(y, 0, 255, 0)); // A green pen with shifting alpha graph.DrawLine(&pen, 0, y, 255, y); }

for (int x = 0; x < 256; x++) { // 画品红色透明度垂直渐变的线(填满扁矩形) Pen pen(Color (x, 255, 0, 255)); // A magenta pen with shifting alpha graph.DrawLine(&pen, x, 100, x, 200); } }

运行的结果如图所示。(其中,第一个图为第一个循环所绘制的结果、第二个图为前两个循环所绘制的结果、第三个图为全部循环所绘制的结果)

图 透明度的连续变化

5)GDI+帮助文档

GDI+的英文帮助内容,位于VS2008的“目录/Win32和COM开发/Graphics and Multimedia/ GDI+”,主要的参考资料位于其子目录“GDI+ Reference”中。 GDI+的中文帮助内容位于VS2008的“目录/开发工具和语言/Visual Studio文档/基于Windows的应用程序、组件和服务/创建基于 Windows 的应用程序/Windows窗体(Windows Forms)/增强Windows窗体应用程序/Windows窗体中的图形和绘制”,其中包括“图形概述(Windows 窗体)”、“关于 GDI+ 托管代码”和“使用托管图形类”三个子目录。

6)Graphics与GraphicsPath中心点

Graphics中心点是左上点,而GraphicsPath中心点是真正的中心点。如图:

GraphicsPath类的GetBounds等函数得到的点,是转换到Graphics平面上的点,对GraphicsPath进行矩阵操作时,需要就将点坐标平移到GraphicsPath中心。 例如:(大小渐变文字)

GraphicsPath path; // 定义路径对象

path.AddString(L\大小渐变文字测试\将文本串加入路径 &FontFamily(L\隶书\ RectF boundRect;

path.GetBounds(&boundRect); // 获取路径的界限矩形 Matrix M; // 定义矩阵对象(单位阵)

M.Translate(-(boundRect.X + boundRect.Width / 2),

-(boundRect.Y + boundRect.Height / 2)); // 平移原点到文本路径的中心 path.Transform(&M); // 更改路径的中心点

INT n = path.GetPointCount(); // 获取路径中的点数 PointF *points = new PointF[n]; // 动态创建点数组

http://www.mybdqn.com/

path.GetPathPoints(points, n); // 获取路径的点数组 BYTE *types = new BYTE[n]; // 动态创建类型数组

path.GetPathTypes(types, n); // 获取路径类型数组(用于路径重构) for (int i= 0; i < n; i++) // 根据路径点到中心的距离,按比例修改点的y值 points[i].Y *= 2*(boundRect.Width-abs(points[i].X))/boundRect.Width; GraphicsPath newPath(points, types, n); // 用新的路径点构造新路径 CRect crect;

GetClientRect(&crect); // CRect

Graphics graph(pDC->m_hDC); // 将坐标原点移到窗口中心:

graph.TranslateTransform(REAL(crect.Width()/2),REAL(crect.Height()/2));

graph.FillPath(&SolidBrush(Color::Green), &newPath);//填充路径(绘制文本串) delete points; delete types; 7)在保存图象的时候会发生这样的错误:“GDI+发生一般性错误”,一般解决方法参考下面。 “GDI+发生一般性错误”,这样的错误一般可以这样重现: Image image = new Bitmap(openFileDialog1 .FileName );

image.Save(openFileDialog1 .FileName ,System .Drawing .Imaging .ImageFormat .Jpeg ); 发生这个错误的原因是:

从一个文件构造的Bitmap 对象或一个 Image 对象, 在该对象的生存期内该文件处于锁定状态。 因此, 在没有释放这个Image或Bitmap对象前,无法更改图像并将其保存回原文件。 解决方法:

构造一个新的Image对象,然后把原来的Image对象中的图象通过Graphics的DrawImage()方法,拷贝到新Image对象中,最后通过Dispose()方法释放原来的Image对象: Image image = new Bitmap ( openFileDialog1 . FileName );

//新建第二个Image类型的变量newImage,这里是根据程序需要设置自己设置。 Image newImage = new Bitmap ( 800 , 600 ); //将第一个bmp拷贝到bmp2中

Graphics draw = Graphics . FromImage ( newImage); draw . DrawImage ( image , 0 , 0 ); //释放第一个Image对象 image.Dispose(); //保存图象

newImage.Save(openFileDialog1.FileName);

8) GDI+ 缩放图片的方法

方法一 : 最简单的 , 使用 GetThumbnailImage, 这个方法的局限性对支持内嵌缩略图的图片文件无效 . 因为 MSDN 中提到 : 如果图片文件有内嵌的缩略图 , 那么就提取这个缩略图返回 , 否则就缩放原图片 , 不过我想对位图还是安全的 : Bitmap * image = new Bitmap(L\Image* pScaledImage = NULL;

UINT nWidth = image->GetWidth()/2; UINT nHeight= image->GetHeight()/2;

pScaledImage = image->GetThumbnailImage(nWidth, nHeight, NULL, NULL);