作为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.路径和区域
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);
}
4.文本绘制
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);指明字体抗锯齿输出。
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);
//一次性绘制到DC
Graphics g(hdc);
g.DrawImage(&bmpMem, 0, 0);
}
测试程序下载链接
参考书籍《精通GDI+》,很多示例,可以当做手册查询
原创,转载请注明来自http://blog.csdn.net/wenzhou1219