1.GDI+ 常用方法詳解

文大俠發表於2017-10-15

作為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);

		//一次性繪製到DC
		Graphics g(hdc);
		g.DrawImage(&bmpMem, 0, 0);
	}


測試程式下載連結

參考書籍《精通GDI+》,很多示例,可以當做手冊查詢

原創,轉載請註明來自http://blog.csdn.net/wenzhou1219

相關文章