1.GDI+ 常用方法详解

来源:互联网 发布:昆明暴恐 中国公知 编辑:程序博客网 时间:2024/06/06 11:51

作为Windows图形系统基准的GDI存在诸多不足:Alpha通道支持较弱,不支持高级图像功能,抗锯齿能力差等等。为了解决这鞋问题,微软推出GDI+,使用非常方便,满足大多数场合需求。

需要指明的是,GDI+是构建于GDI上的基于CPU渲染的图形系统,所以尽管微软一直在优化,它的性能是比不上GDI的且实测发现XP上GDI+有一定概率崩溃,另外,GDI可以借助[GDI DDI驱动程序和显卡实现]完成硬件加速而GDI+不行。对于游戏等性能要求高的需求,微软推荐使用Direct2D和Direct3D,他们很好支持抗抗锯齿等高级特性且支持硬件加速。


下面详细说下GDI+ 常用方法的使用。


1.环境初始化

GDI+使用需要链接对应的链接库,使用完成后要反初始化环境。这里封装一个类,构造函数中完成初始化,析构函数中完成反初始化,使用时作为全局变量或类成员,即可自动完成这些过程,如下:

#pragma once//GDI+#include <GdiPlus.h>#include <GdiplusGraphics.h>#pragma comment(lib, "gdiplus.lib")using namespace Gdiplus;class CGdiplusInitHelper{public:CGdiplusInitHelper(){//gdi+初始化m_nGidToken = 0;Gdiplus::GdiplusStartupInput gdiplusStartupInput;GdiplusStartup(&m_nGidToken, &gdiplusStartupInput, NULL);}~CGdiplusInitHelper(){//gdi+卸载if ( 0 != m_nGidToken ){GdiplusShutdown( m_nGidToken );m_nGidToken = 0;}}private:ULONG_PTR m_nGidToken;};

2.常见图像绘制

和GDI 中dc对应的是GDI+中的Graphics。但是:

1.GDI是有状态的,GDI+是无状态的,比如GDI中要把当前画笔、画刷等选进dc,然后使用这些GDI图元来工作,GDI+中不一样,画笔、画刷、字体等等都是对象,我们只需要Graphics绘制时将对象作为参数传递即可。

2.GDI画矩形、圆等图形的副作用是会填充区域,要不填充区域必须使用NULL_BRUSH,GDI+中绘制边缘和填充是两个概念,另外GDI+支持的图形种类更多也更灵活


演示如下:

void DrawLines(HDC &hdc){Graphics g(hdc);Pen redP(Color(255,0,0), 2);g.DrawLine(&redP, 20, 20, 40, 60);redP.SetDashStyle(DashStyle::DashStyleDashDotDot);//线条样式g.DrawLine(&redP, 1, 10, 300, 20);Pen cyanP(Color::DarkCyan);g.DrawRectangle(&cyanP, 60,30,20,100);g.DrawEllipse(&cyanP, 60,30,20,100);Point pts[] = {Point(10,120), Point(60,200), Point(80,200), Point(90,90)};g.DrawPolygon(&cyanP, pts, 4);Pen pinkP(Color::HotPink);PointF pts2[] = {PointF(10.0f,220.0f), PointF(60.0f, 230.0f), PointF(50.0f, 250.0f)};g.DrawCurve(&pinkP, pts2, 3);Point pt1(10,300);Point pt2(50,400);Point pt3(100,350);Point pt4(30,250);g.DrawBezier(&pinkP, pt1, pt2, pt3, pt4);}//填充和绘制分开void FillShape(){Graphics g(m_hWnd);SolidBrush redB(Color(255,0,0));g.FillRectangle(&redB, 100,400,200,250);}

可以看到:

1.这里绘制时我们创建一个Pen对象,设置他的属性,然后Graphics绘制时作为参数

2.填充区域是Fill*函数,绘制图形是Draw*函数


3.路径和区域

在GDI中使用路径,必须包含在BeginPath和EndPath中,使用非常不方便。在GDI+中路径GraphicPath、区域Region和其他图元(如Pen)一样,都是基本的对象。如下:

void DrawPathAndRegion(HDC &hdc){PointF data[] = {PointF(40,140),PointF(275,200),PointF(105,255),PointF(50,350),PointF(20,180)};BYTE typeline[] = {PathPointTypeLine,PathPointTypeLine,PathPointTypeLine,PathPointTypeLine,PathPointTypeLine};GraphicsPath path1(data, typeline, 5);Graphics g(hdc);g.SetSmoothingMode(SmoothingModeAntiAlias);//绘制时反走样来消除锯齿g.TranslateTransform(400., 0.);g.FillPath(&SolidBrush(Color::Red), &path1);g.TranslateTransform(300., 0.);GraphicsPath path2;FontFamily fontFamily(L"Arial");path2.AddString(L"Add String", -1, &fontFamily, FontStyleRegular, 100, PointF(0,0), NULL);Pen pen(Color::Black);g.DrawPath(&pen, &path2);GraphicsState s = g.Save();g.SetClip(&path2);//path做剪切区域for (int i=0; i<100; i+=2){g.DrawLine(&pen, 0, i, 600, i);}Region rgn(&path2);//路径转区域g.Restore(s);//取消剪切区域g.TranslateTransform(0., 100.);g.FillRegion(&SolidBrush(Color::Black), &rgn);}

可以看到这里路径path1由指定的点连成的线构成,路径path2由字体输出构成,setclip可以类似GDI设置当前绘图的剪切区域,这里我们用字体做剪切区域,效果就是在字体区域中间填充一系列的横线。
路径和区域可以互转。
另外,这里g.SetSmoothingMode(SmoothingModeAntiAlias);指定绘制时反走样来消除锯齿,g.Save和g.Restore(s)可以完成Graphics的设置和恢复,g.TranslateTransform指明当前坐标系的平移变换(还支持旋转、缩放等高级变换功能)

4.文本绘制

GDI中DrawText来绘制文本和测量输出区域大小,GDI+提供了更强大的功能,而且支持字体的抗锯齿输出,如下:
void DrawText(HDC &hdc){Graphics g(hdc);Font myFont(L"Arial", 50, FontStyleItalic, UnitPixel);RectF layoutRect(10.0f, 10.0f, 197.0f, 50.0f);StringFormat format;format.SetAlignment(StringAlignmentCenter);//水平对齐format.SetLineAlignment(StringAlignmentCenter);//垂直对齐format.SetFormatFlags(StringFormatFlagsNoClip); //不截断format.SetHotkeyPrefix(HotkeyPrefixShow);//&前导符SolidBrush blackBrush(Color(255,0,0,0));LPCWSTR pszText = L"Out&putx Text";g.DrawString(pszText, -1, &myFont, layoutRect, &format, &blackBrush);g.SetTextRenderingHint(TextRenderingHintAntiAliasGridFit);//抗锯齿format.SetTrimming(StringTrimmingEllipsisCharacter);//结尾...layoutRect.Offset(0, 100);LPCWSTR pszText2 = L"Output Text Output Text Output Text Output Text";g.DrawString(pszText2, -1, &myFont, layoutRect, &format, &blackBrush);//测量指定文本大小RectF boundRect;g.MeasureString(pszText2, -1, &myFont, PointF(0,0), &boundRect);CString str;str.Format(L"W:%d-H:%d", boundRect.Width, boundRect.Height);OutputDebugString(str);//测量指定layoutRect宽度内,文本显示的个数、行数和包围框大小INT codepointsFitted = 0;INT linesFilled = 0;RectF layoutRect2(10.0f, 10.0f, 197.0f, 0.f);//高度必须为0才能自适应计算g.MeasureString(pszText2, -1, &myFont, layoutRect2, &format,&boundRect, &codepointsFitted, &linesFilled);}
可以看到类似GDI,这里我们使用StringFormat控制输出文本的格式g.SetTextRenderingHint(TextRenderingHintAntiAliasGridFit);指明字体抗锯齿输出
使用MeasureString测量文本输出大小,而且可以看到指定包围盒高度为0时它可以根据宽度来完成自适应计算,计算出文本显示的个数、行数和包围框大小。

5.图像绘制

GDI中图像功能有很多确定,只支持加载bmp格式,Alpha通道支持有限,图像处理功能较弱。GDI+很好的弥补了这一切。

GDI+支持BMP/TIFF/JPG/PNG/GIF等,且支持透明,GDI的AlphaBlend函数缩放时不支持抗锯齿,GDI+的DrawImage弥补了这些,如下:

void DrawImages(HDC &hdc){Graphics g(hdc);g.Clear(Color::WhiteSmoke);//清空当前背景Image img1(L".\\pop_bk.png");PointF pt1(0.f, 0.f);g.DrawImage(&img1, pt1);g.SetInterpolationMode(InterpolationModeHighQualityBicubic);//设置插值模式,处理图像缩放RectF rcDest(400.f,400.f,REAL(img1.GetWidth()), REAL(img1.GetHeight()));g.DrawImage(&img1, rcDest, 20.f,20.f,40.f,40.f, UnitPixel);}
g.SetInterpolationMode(InterpolationModeHighQualityBicubic)设置图像缩放时的插值方式,保证高质量显示。这里主要演示DrawImage使用,图像的加载和处理下一节再细述。

可在DrawImage时传入Attr参数,指定图像的色彩变换,具体参考API使用。


6.双缓冲绘制

GDI中我们都是通过创建一个兼容DC,一个兼容BMP,兼容BMP选入兼容DC绘制完成后,再将BMP一次性贴到实际DC上。

在GDI+中不用这么麻烦,GDI+将一切表面抽象,如果想在位图上绘制,直接将Graphics的表面选成位图即可,如下:

//双缓冲void DrawImages5(HDC &hdc){Bitmap bmp1(L".\\pop_bk.png");//内存绘图Bitmap bmpMem(bmp1.GetWidth(), bmp1.GetHeight());Graphics gMem(&bmpMem);//以内存Bitmap为表面绘图gMem.DrawImage(&bmp1, 0, 0);gMem.DrawRectangle(&Pen(Color::Black), 0,0,40,40);//一次性绘制到DCGraphics g(hdc);g.DrawImage(&bmpMem, 0, 0);}


测试程序下载链接

参考书籍《精通GDI+》,很多示例,可以当做手册查询

原创,转载请注明来自http://blog.csdn.net/wenzhou1219