GDI+簡介

一、 GDI+的特點和新增功能

GDI+與GDI一樣,都具有設備無關性。應用程序的程序員可利用GDI+這樣的圖形設備接口在屏幕或打印機上顯示信息,而不需要考慮特定顯示設備的具體情況。應用程序的程序員調用GDI+類提供的方法,而這些方法又反過來相應地調用特定的設備驅動程序。GDI+將應用程序與圖形硬件隔離,而正是這種隔離允許開發人員創建設備無關的應用程序。


1、 GDI+的功能

GDI+主要提供了以下三種功能:

1) 二維矢量圖形

矢量圖形包括座標系統中的系列點指定的繪圖基元(如直線、曲線和圖形)。例如,直線可通過它的兩個端點來指定,而矩形可通過確定其左上角位置的點並給出其寬度和高度的一對數字來指定。簡單路徑可由通過直線連接的點的數組來指定。貝塞爾樣條是由四個控制點指定的複雜曲線。

GDI+提供了存儲基元自身相關信息的類(結構)、存儲基元繪製方式相關信息的類,以及實際進行繪製的類。例如,Rectangle結構存儲矩形的位置和尺寸;Pen類存儲有關線條顏色、線條粗細和線型的信息;而Graphics類具有用於繪製直線、矩形、路徑和其它圖形的方法(類似於GDI中的CDC類)。還有幾種Brush類,它們存儲有關如何使用顏色或圖案來填充封閉圖形和路徑的信息。

用戶可以在圖元文件中記錄矢量圖像(圖形命令的序列)。GDI+提供了Metafile類,可用於記錄、顯示和保存圖元文件。MetafileHeader和MetaHeader類允許您檢查圖元文件頭中存儲的數據。

2) 圖像處理

某些種類的圖片很難或者根本無法用矢量圖形技術來顯示。例如,工具欄按鈕上的圖片和顯示爲圖標的圖片就難以指定爲直線和曲線的集合。擁擠的棒球運動場的高分辨率數字照片會更難以使用矢量技術來製作。這種類型的圖像可存儲爲位圖,即代表屏幕上單個點顏色的數字數組。

GDI+提供了Image、Bitmap和Metafile類,可用於顯示、操作和保存位圖。它們支持衆多的圖像文件格式,還可以進行多種圖像處理的操作。

3) 文字顯示版式

就是使用各種字體、字號和樣式來顯示文本。GDI +爲這種複雜任務提供了大量的支持。GDI+中的新功能之一是子像素消除鋸齒,它可以使文本在LCD 屏幕上呈現時顯得比較平滑。

 

2、 GDI+新增特性

1)、漸變畫刷

漸變畫刷(gradient brush梯度刷)通過提供用於填充圖形、路徑和區域的線性漸變畫筆和路徑漸變畫筆,GDI+擴展了GDI 的功能。漸變畫筆還可用於繪製直線、曲線和路徑。線性漸變畫筆可用於使用顏色來填充圖形,畫筆在圖形中移動時,顏色會逐漸改變。例如,假定通過指定圖形左邊爲藍色、右邊爲綠色,創建了一個水平漸變畫筆。當用水平漸變畫筆填充該圖形時,隨着畫筆從圖形的左邊移至右邊,顏色就會由藍色逐漸變爲綠色。用類似方法定義的垂直漸變畫筆填充的圖形,顏色從上到下變化。圖顯示了用水平漸變畫筆填充的橢圓和用斜式漸變畫筆填充的區域。

 

 

圖 水平和斜式漸變畫筆

 

用路徑漸變畫筆填充圖形時,可選擇不同的方法來指定當從圖形的一部分至另一部分移動畫筆時顏色的變化方式。一種選擇是指定中心顏色和邊緣顏色,在從圖形中間向外邊緣移動畫筆時,像素逐漸從一種顏色變化到另一種顏色。圖顯示了用路徑漸變畫筆填充的路徑(該路徑是用一對貝塞爾樣條創建的)。

 

 

圖 路徑漸變畫筆

2)、基數樣條函數

GDI+支持在GDI 中不支持的基數樣條(cardinal spines)。基數樣條是一連串單獨的曲線,這些曲線連接起來形成一條較長的光滑曲線。樣條由點的數組指定,並通過該數組中的每一個點。基數樣條平滑地(沒有銳角)通過數組中的每一個點,因此,比通過連接直線創建的路徑更光滑精準。圖顯示了兩個路徑:一個以基數樣條的形式創建;另一個通過連接直線創建。

 

 

圖 基數樣條路徑和折線路徑

3)、持久路徑對象

在GDI 中,路徑屬於設備上下文,並且會在繪製時被毀壞。利用GDI +,繪圖由Graphics對象執行,可以創建並維護幾個與Graphics對象分開的持久的路徑對象(persistent path object)—— GraphicsPath對象。繪圖操作不會破壞GraphicsPath 對象,因此可以多次使用同一個GraphicsPath 對象來繪製路徑。

4)、變換和矩陣對象

GDI+提供了Matrix(矩陣) 對象,它是一種可以使(縮放、旋轉和平移等)變換(transformation)簡易靈活的強大工具。矩陣對象一般與變換對象聯合使用。例如,GraphicsPath 對象具有Transform 方法,此方法接收Matrix 對象作爲參數。單一的3×3矩陣可存儲一種變換或一個變換序列。圖顯示了一個路徑在執行兩種變換前後的情況。

 

 

圖 路徑的變換

5)、可伸縮區域

GDI+ 通過對可伸縮區域(Scalable Regions)的支持極大地擴展了GDI。在GDI 中,區域被存儲在設備座標中,而且,可應用於區域的惟一變換是平移。而GDI+在全局座標中存儲區域,並且允許區域發生任何可存儲在變換矩陣中的變換(如縮放和旋轉)。圖顯示一個區域在執行三種變換(縮放、旋轉和平移)前後的情況。

 

 

圖 區域的三種變換(縮放、旋轉和平移)

6)、α混色

在下圖中,可以在變換區域(用藍色陰影畫筆填充)中看到未變換區域(用紅色填充)。這是由GDI+支持的α混色(Alpha Blending,透明混合)實現的。使用α混色,可以指定填充顏色的透明度。透明色與背景色相混合———填充色越透明,透出的背景色就越多。圖顯示四個用相同顏色(紅色)填充、但透明層次不同的橢圓。

 

 

圖 不同透明度

7)、豐富的圖像格式支持

GDI+提供Image、Bitmap 和Metafile 類,可以用不同的格式加載、保存和操作圖像。GDI+支持BMP、GIF、JPEG、EXIF、PNG、TIFF、ICON、WMF、EMF共9種常見的圖像格式。

8)、GDI+的不足

雖然,相對於GDI來說,GDI+ 確實增加了許多新特性,而且功能更強大,使用也更方便。但是,這並不等於GDI+ 就能夠完全代替GDI。

因爲GDI+實際上是GDI的封裝和擴展,GDI+的執行效率一般要低於GDI的。另外,GDI+不支持圖的位運算,那麼就不能進行異或繪圖等操作。而且在VC中,GDI+ 還不支持雙緩存機制(如內存DC和顯示DC),這將大大影響GDI+ 在高速圖形、圖像、動畫和視頻等方面的應用。

 

3、 GDI+的使用

1) GDI+開發包

若採用的是Visual C++ 2008,則已經包含了開發GDI+應用程序所需的所有東西。如果使用的是Visual C++6.0而非VS.Net,我們需要下載微軟的GDIPLUS支持包。在微軟官方網站下載時需認證Windows爲正版,我們可從這個地址下載:http://www.codeguru.com/code/legacy/gdi/GDIPlus.zip。一個完整的GDI+支持包至少包括如下文件:

  (1)頭文件:gdiplus.h

  (2)動態鏈接庫的.lib文件:gdiplus.lib

  (3)動態鏈接庫的.dll文件:gdiplus.dll

  少了(1)、(2)程序不能編譯,少了(3)程序能以共享DLL的方式編譯但是不能運行,運行時找不到.dll文件。

如果你使用的操作系統是Windows XP或Windows Server 2003,則GDI+所對應的動態鏈接庫,已經被包含在其中。gdiplus.dll一般位於操作系統的WinSxS(Windows side-by-side assembly,視窗並行程序集)目錄中,例如:

C:/WINDOWS/WinSxS/x86_Microsoft.Windows.GdiPlus_6595b64144ccf1df_1.0.0.0_x-ww_8d353f13/gdiplus.dll(1661KB,2002.10.8)

而GDI的動態鏈接庫gdi32.dll,卻一般在操作系統的32位系統目錄中:

F:/WINDOWS/system32/gdi32.dll(272KB,2004.8.4)

2) VC使用GDI+初始化準備工作。

#define UNICODE

#ifndef ULONG_PTR

#define ULONG_PTR unsigned long*

#endif

#include "c:/gdiplus/includes/gdiplus.h"

using namespace Gdiplus;

#pragma comment(lib, "c://gdiplus//lib//gdiplus.lib")

//在CWinApp派生類的InitInstance函數中加入:

 //初始化gdiplus的環境

 GdiplusStartupInput gdiplusStartupInput;

 ULONG_PTR gdiplusToken;

 // 初始化GDI+.

 GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

//在CWinApp派生類的ExitInstance函數中加入:

 //關閉gdiplus的環境

 GdiplusShutdown(gdiplusToken);

 

4、GDI+的組成

GDI+ API包含54個類、12個函數、6類(226個)圖像常量、55種枚舉和19種結構。

1)、類

GDI+ API中共有54個類,核心類是Graphics,它是實際繪製直線、曲線、圖形、圖像和文本的類。許多其它GDI+類是與Graphics類一起使用的。例如,DrawLine方法接收Pen對象,該對象中存有所要繪製的線條的屬性(顏色、寬度、虛線線型等)。FillRectangle方法可以接收指向LinearGradientBrush對象的指針,該對象與Graphics對象配合工作來用一種漸變色填充矩形。Font和StringFormat對象影響Graphics對象繪製文本的方式。Matrix對象存儲並操作Graphics對象的仿射變換——旋轉、縮放和翻轉圖像。

GDI+還提供了用於組織圖形數據的幾種結構類(例如 Rect、Point和Size)。而且,某些類的主要作用是結構化數據類型。例如,BitmapData類是Bitmap類的幫助器,PathData類是GraphicsPath類的幫助器。

下面是所有GDI+的API類的列表:

 

GDI+的API類(54個)

名稱

功能

調整箭頭帽
AdjustableArrowCap
創建自定義箭頭線帽

位圖
Bitmap
提供裝入和保存矢量和光柵圖像的方法,並可以創建和操作光柵圖像

位圖數據
BitmapData
保存位圖的屬性

模糊
Blur
將高斯模糊效果作用到圖像

亮度對比度
BrightnessContrast
改變圖像的亮度和對比度


Brush
定義刷對象

緩存圖像
CachedBitmap
用爲特點設備顯示而優化過的格式存儲位圖

字符範圍
CharacterRange
指定串內字符位置的範圍

顏色
Color
保存表示顏色的32位值

色平衡
ColorBalance
改變位圖的顏色平衡

顏色曲線
ColorCurve
可調整位圖的曝光度、密度、對比度、加亮、陰影、色調、白飽和和黑飽和。

顏色查找表
ColorLUT
用於定製位圖的顏色調整

顏色矩陣效果
ColorMatrixEffect
對位圖進行仿射變換

定製線帽
CustomLineCap
封裝了自定義線帽

效果
Effect
作用於圖像的效果和調整類的基類

編碼器參數
EncoderParameter
保存圖像編碼器的參數

編碼器參數組
EncoderParameters
圖像編碼器參數的數組

字體
Font
封裝了字體的族系、高度、大小和風格等特性

字體集
FontCollection
包含枚舉字體集中的字體族系的方法

字體族
FontFamily
封裝了構成一個字體族的字體集合

GDI+基類
GdiplusBase
提供對GDI+對象的存儲分配與釋放,是其它GDI+類的基類

圖形
Graphics
提供繪製圖形、圖像和文本的方法,存儲顯示設備和被畫項目的屬性

圖形路徑
GraphicsPath
保存一個供繪圖用的直線、曲線和形狀序列

圖形路徑迭代器
GraphicsPathIterator
提供從保存在GraphicsPath對象中的路徑裏選擇孤立子集的方法

影線刷
HatchBrush
定義具有影線風格和前景色/背景色的矩形刷

色調飽和度亮度
HueSaturationLightness
改變位圖的色調H、飽和度S和亮度L

圖像
Image
提供裝入和保存矢量和光柵圖像的方法

圖像屬性
ImageAttributes
含渲染時如何操作位圖和圖元文件顏色的信息

圖像編解碼信息
ImageCodecInfo
存儲與圖像編解碼有關的信息

圖像項數據
ImageItemData
用於存儲和獲取自定義圖像的元數據

已裝入字體集
InstalledFontCollection
定義表示已裝入系統中的字體集

級別
Levels
可調整位圖的加亮、陰影和色調

線形梯度刷
LinearGradientBrush
定義線性漸變刷

矩陣
Matrix
表示3×3的仿射變換矩陣

圖元文件
Metafile
定義包含描述一系列圖形API調用記錄的圖形元文件,可被記錄(構造)和回放(顯示)

圖元文件頭
MetafileHeader
保存關聯圖元文件的性質

路徑數據
PathData
GraphicsPath和GraphicsPathIterator類的助手類,用於獲取和設置路徑中的數據點及其類型

路徑梯度刷
PathGradientBrush
保存顏色的梯度屬性,用於漸變色填充路徑內部


Pen
用於繪製直線和曲線的筆對象


Point
封裝2D整數座標系統中的點

浮點點
PointF
封裝2D浮點座標系統中的點

專用字體集
PrivateFontCollection
保存用於特定應用程序的字體集,可含未裝入系統中的字體

特性項
PropertyItem
Image和Bitmap類的助手類,保存一塊(piece)圖像元數據

矩形
Rect
保存矩形的左上角、寬度和高度之對象(整數)

浮點矩形
RectF
保存矩形的左上角、寬度和高度之對象(浮點數)

紅眼校正
RedEyeCorrection
校正有時在閃光照片中出現的紅眼

區域
Region
描述顯示錶面的範圍,可以是任意形狀

銳化
Sharpen
調整位圖的清晰度

大小
Size
封裝2D整數座標系統中的寬和高

浮點大小
SizeF
封裝2D浮點數座標系統中的寬和高

實心刷
SolidBrush
定義實心顏色的刷子對象

串格式
StringFormat
封裝文本的格式(layout)信息和顯示操作

紋理刷
TextureBrush
用於填充的包含圖像對象的刷子

濃淡
Tint
改變位圖的色彩濃淡
 

 

下面是GDI+ API類的層次結構圖:

GdiplusBase

Graphics

GraphicsPath

GraphicsPathIterator

Pen

Brush

SolidBrush

HatchBrush

TextureBrush



LinearGradientBrush

PathGradientBrush

Image

Bitmap

Metafile

CustomLineCap

AdjustableArrowCap

CachedBitmap

ImageAttributes

FontCollection

InstalledFontCollection

PrivateFontCollection

StringFormat

Region

Font

FontFamily

Matrix

Point

PointF

Size

SizeF

Rect

RectF

Color

Effect

Blur

BrightnessContrast

ColorBalance

ColorCurve

ColorLUT

ColorMatrixEffect

HueSaturationLightness

Levels

RedEyeCorrection

Sharpen

Tint

ImageItemData

BitmapData

MetafileHeader

PropertyItem

EncoderParameter

EncoderParameters

ImageCodecInfo

PathData

CharacterRange

獨立類

繪圖類

效果類
 


GDI+類的層次結構圖

 

2)、函數

GDI+命名空間中的函數(12個)

名稱
函數
功能

關閉GDI+
GdiplusShutdown
清除GDI+所使用的資源

啓動GDI+
GdiplusStartup
初始化GDI+

獲取圖像解碼器
GetImageDecoders
獲取含有可用圖像解碼器信息的ImageCodecInfo對象數組

獲取圖像解碼器的大小
GetImageDecodersSize
獲取含有可用圖像解碼器的數目

獲取圖像編碼器
GetImageEncoders
獲取含有可用圖像編碼器信息的ImageCodecInfo對象數組

獲取圖像編碼器的大小
GetImageEncodersSize
獲取含有可用圖像編碼器的數目

獲取像素格式大小
GetPixelFormatSize
返回指定像素格式的每像素二進制位數

是否爲α像素格式
IsAlphaPixelFormat
確定指定像素格式是否有α分量

是否爲規範像素格式
IsCanonicalPixelFormat
確定指定像素格式是否爲規範格式之一

是否爲擴展像素格式
IsExtendedPixelFormat
確定指定像素格式是否使用16位色

是否爲索引像素格式
IsIndexedPixelFormat
確定指定像素格式是否是索引格式

對象類型是否有效
ObjectTypeIsValid
確定ObjectType枚舉元素是否表示一個有效對象類型
 

 

3)、常量

GDI+中定義瞭如下6類圖像常量(226個):(GdiplusImaging.h)

類型
常量
說明

圖像

文件

格式

ImageFormat*

(11個)
ImageFormatBMP
BMP(BitMaP位圖)

ImageFormatEMF
EMF(Enhanced MetaFile增強圖元文件)

ImageFormatEXIF
Exif(Exchangeable Image File可交換圖像文件)

ImageFormatGIF
GIF(Graphics Interchange Format圖形交換格式)

ImageFormatIcon
Icon(圖標)

ImageFormatJPEG
JPEG(Joint Photographic Experts Group聯合圖象專家組)

ImageFormatMemoryBMP
從內存位圖構造的圖像

ImageFormatPNG
PNG(Portable Network Graphics可移植網絡圖形)

ImageFormatTIFF
TIFF(Tagged Image File Format標籤圖像文件格式)

ImageFormatUndefined
不能確定格式

ImageFormatWMF
WMF(Windows Metafile Format視窗圖元文件格式)

圖像

幀維
FrameDimensionPage
多幀TIFF圖像

FrameDimensionTime
多幀GIF圖像

圖像

編碼器

(13個)
EncoderChrominanceTable
色度表

EncoderColorDepth
顏色深度

EncoderColorSpace
顏色空間

EncoderCompression
壓縮

EncoderLuminanceTable
亮度表

EncoderQuality
質量

EncoderRenderMethod
渲染方法

EncoderSaveFlag
保存標誌

EncoderScanMethod
掃描方法

EncoderTransformation
變換

EncoderVersion
版本

EncoderImageItems
圖像項

EncoderSaveAsCMYK
保存爲CMYK(Cyan青、Magenta品紅、Yellow黃、blacK黑,用於印刷的四分色)

圖像

像素

格式

(14個)
PixelFormat1bppIndexed
每像素1位,索引色

PixelFormat4bppIndexed
每像素4位,索引色

PixelFormat8bppIndexed
每像素8位,索引色

PixelFormat16bppARGB1555
每像素16位,α分量1位、RGB分量各5位

PixelFormat16bppGrayScale
每像素16位,灰度

PixelFormat16bppRGB555
每像素16位,RGB分量各5位,另1位未用

PixelFormat16bppRGB565
每像素16位,RB分量各5位、G分量6位

PixelFormat24bppRGB
每像素24位,RGB分量各8位

PixelFormat32bppARGB
每像素32位,αRGB分量各8位

PixelFormat32bppPARGB
每像素32位,αRGB分量各8位,RGB分量預乘α分量

PixelFormat32bppRGB
每像素24位,RGB分量各8位,另8位未用

PixelFormat48bppRGB
每像素48位,RGB分量各16位

PixelFormat64bppARGB
每像素64位,αRGB分量各16位

PixelFormat64bppPARGB
每像素64位,αRGB分量各16位,RGB分量預乘α分量

圖像

特性

標誌

類型

(9個)
PixelFormat4bppIndexed
格式爲每像素4位,索引色

PropertyTagTypeASCII
值數據成員爲以null結尾的ASCII字符串

PropertyTagTypeByte
值數據成員爲字節數組

PropertyTagTypeLong
值數據成員爲32位無符號長整數的數組

PropertyTagTypeRational
值數據成員爲32位無符號長整數對的數組,每對數中的第一個整數爲分子,第二個整數爲分母

PropertyTagTypeShort
值數據成員爲16位無符號短整數的數組

PropertyTagTypeSLONG
值數據成員爲32位有符號長整數的數組

PropertyTagTypeSRational
值數據成員爲32位有符號長整數對的數組,每對數中的第一個整數爲分子,第二個整數爲分母

PropertyTagTypeUndefined
值數據成員爲字節數組,可保存任何數據類型的值

圖像

特性

標誌

(217個)
PropertyTagGpsVer ~
GPS(Global Positioning Systems全球定位系統)版本

PropertyTagGpsDestDist
(0x0000)~ 到目標點的距離(0x001A)(27個)

PropertyTagNewSubfileType ~
子文件數據類型(0x00FE)~

PropertyTagPageNumber
被掃描圖像的頁數(0x0129)(44個)

PropertyTagTransferFunction
圖像傳送函數表(0x012D)

PropertyTagSoftwareUsed
指定用於生成圖像的設備之軟件或固件的名稱和版本的以null結尾的字符串(0x0131)

PropertyTagDateTime
圖像創建的日期和時間(0x0132)

PropertyTagArtist ~
指定圖像創建者姓名的以null結尾的字符串(0x013B)

PropertyTagTileByteCounts
~ 標題的字節數(0x0145)(11個)

PropertyTagInkSet ~
在分開圖像中使用的墨水集(0x014C)

PropertyTagNumberOfInks
~ 墨水數目(0x014D)(3個)

PropertyTagDotRange ~
對應於0%點和100%點的顏色分量值(0x0150)~

PropertyTagTransferRange
擴充傳送函數範圍的值表(0x0156)(7個)

PropertyTagJPEGProc ~
JPEG壓縮過程(0x0200)~

PropertyTagImageTitle
圖像標題的以null結尾的字符串(0x0320)(17個)

PropertyTagResolutionXUnit ~
顯示水平分辨率的單位(0x5001)~(27個)

PropertyTagThumbnailData
RGB或JPEG中的原始縮略圖中的位數據(0x501B)

PropertyTagThumbnailImage

Width ~
略圖像的每行像素數(0x5020)~(28個)

PropertyTagThumbnailCopy

Right
含縮略圖像版權信息的以null結尾的字符串(0x503B)

PropertyTagLuminanceTable
亮度表(0x5090)

PropertyTagFrameDelay ~
GIF動畫中兩幀之間的延時,單位爲10毫秒(0x5100)

PropertyTagPaletteHistogram
~ 調色板直方圖(0x5113)(9個)

PropertyTagCopyright ~
含版權信息的以null結尾的字符串(0x8298B)~

PropertyTagExifCfaPattern
顏色濾波器數組(0xA302)(48個)
 


 

4)、枚舉

GDI+定義了55種枚舉,它們都是相關常數的集合。例如,LineJoin枚舉包含元素Bevel、Miter和Round,它們指定可用於連接兩個線條的線型。下面是所有枚舉類型的列表:

 

GDI+枚舉類型(55種)

枚舉類型
名稱

枚舉類型
名稱

BrushType
刷類型
ImageType
圖像類型

ColorAdjustType
顏色調整類型
InterpolationMode
插值類型

ColorChannelFlags
顏色通道標誌
ItemDataPosition
項數據位置

ColorMatrixFlags
顏色矩陣標誌
LinearGradientMode
線性梯度模式

CombineMode
組合模式
LineCap
線帽

CompositingMode
合成模式
LineJoin
線連接

CompositingQuality
合成質量
MatrixOrder
矩陣序(左右乘)

CoordinateSpace
座標空間
MetafileFrameUnit
圖元文件幀單位

CurveAdjustments
曲線調整
MetafileType
圖元文件類型

CurveChannel
曲線通道
ObjectType
對象類型

DashCap
虛線帽
PaletteFlags
調色板標誌

DashStyle
虛線風格
PaletteType
調色板類型

DitherType
抖動類型
PathPointType
路徑點類型

DriverStringOptions
驅動器串選項
PenAlignment
筆對齊

EmfPlusRecordType
EMF+等圖元文件記錄類型
PenType
筆類型

EmfToWmfBitsFlags
EMF轉WMF的標誌位
PixelOffsetMode
像素偏移模式

EmfType
EMF類型
RotateFlipType
旋轉翻轉類型

EncoderParameterValueType
編碼器參數值類型
SmoothingMode
平滑模式

EncoderValue
編碼器值
Status
狀態

FillMode
填充模式
StringAlignment
串對齊

FlushIntention
刷新意圖
StringDigitSubstitute
串數字替換

FontStyle
字體風格
StringFormatFlags
串格式標誌

HatchStyle
影線風格
StringTrimming
串修整

HistogramFormat
直方圖格式
TextRenderingHint
文本渲染提示

HotkeyPrefix
熱鍵前綴
Unit
單位

ImageCodecFlags
圖像編解碼標誌
WarpMode
彎曲模式

ImageFlags
圖像標誌
WrapMode
覆蓋模式

ImageLockMode
圖像加鎖模式

 

 

5)、結構

GDI+ API中還定義了19種結構,用於GDI+的各種函數調用中。下面是所有GDI+ API結構的列表:

GDI+ API中的結構(19種)

結構
名稱

BlurParams
模糊參數

BrightnessContrastParams
亮度對比度參數

ColorBalanceParams
顏色平衡參數

ColorCurveParams
顏色曲線參數

ColorLUTParams
顏色查找表參數

ColorMap
顏色映射

ColorMatrix
顏色矩陣

ColorPalette
顏色調色板

ENHMETAHEADER3
增強圖元文件頭

GdiplusAbort
GDI+異常中斷

GdiplusStartupInput
GDI+啓動輸入

GdiplusStartupOutput
GDI+啓動輸出

HueSaturationLightnessParams
色調飽和度亮度參數

LevelsParams
級別參數

PWMFRect16
可定位WMF矩形(INT16整數值)

RedEyeCorrectionParams
紅眼校正參數

SharpenParams
銳化參數

TintParams
濃淡參數

WmfPlaceableFileHeader
可定位WMF文件頭
 

6)、GDI+平面API

GDI+暴露出(exposes)一個平面(flat)API,它包含大約600個函數,被實現在Gdiplus.dll中,聲明在Gdiplusflat.h內。這些函數被包裝到了前面討論過的GDI+ API的54個C++類的集合之中。不要直接調用這些函數,而推薦用調用類成員方法來替代。因爲微軟產品支持服務(Microsoft Product Support Services),不會爲直接調用平面API的代碼提供支持。

作爲C++封裝的替代方案,微軟網絡框架(Microsoft .NET Framework),提供了GDI+的一個託管代碼封裝類集,包含大約60個類、50個枚舉和8個結構。它們屬於下列命名空間: // 在C#中使用之

System.Drawing

System.Drawing.Drawing2D

System.Drawing.Imaging

System.Drawing.Text

System.Drawing.Printing

DllExports

System.Drawing[.dll]

Gdiplus

Gdiplus.h

afxwin.h

C++封裝

(MFC)

C++封裝

託管代碼封裝

設備驅動程序

計算機硬件(顯示器、打印機等圖形設備)

GDI API

GDI+平面API

C++

C#、VB、J#

GDI+ API

GDI+託管類接口

GDI類與結構

Gdi32.dll

WinGDI.h

Gdiplus.dll

GdiplusFlat.h
 


GDI+的封裝與使用

這兩種包裝(C++和託管代碼)都採用了面向對象方法,所以二者在將參數傳遞給封裝的方法和將參數傳遞給平面API函數的方式上存在差別。

 

二、GDI+編程

本部分簡單介紹GDI+編程中的一些概念與技巧,具體的編程細節請參考《精通GDI+編程》、陳寶楷《GDI+編程》等書籍。

1、Point、浮點數點類PointF;Size、浮點數大小類SizeF;Rect、浮點數矩形類RectF等。

 

浮點數版的幾何對象和繪圖函數,是GDI+新增的功能,這些在各種工程技術領域都非常有用。因爲一般的實際圖形設計,都是基於實數座標的。包括機械(機牀/汽車/輪船/飛機等)、建築(房屋/橋樑/道路/堤壩等)和圖形動畫設計(形狀/物體/人物/背景/軌跡等)等設計,都必須使用浮點參數和座標系。

2、Color:在GDI+中,色彩是通過Color(色彩)類來描述的。Color類的構造函數分別爲:

Color();

Color(BYTE a,BYTE r,BYTE g,BYTE b);

Color(ARGB argb);

Color(BYTE r,BYTE g,BYTE b);

參數:

a:色彩的透明度(0~255)

r、g、b:紅、綠、藍3種色彩分量值(0~255)

不同於GDI,GDI+在對色彩支持方面主要體現在對色彩的透明度支持。從本質上講,透明度是像素之間的一種合成運算。它的計算公式是:

輸出色彩=前景色*Alpha/255 + 背景色*(255-Alpha)/255

3、Graphics(圖形)

圖形類Graphics是GDI+的核心,它提供繪製圖形、圖像和文本的各種方法(操作/函數)(似GDI中的CDC類),還可以存儲顯示設備和被畫項目的屬性(到圖元文件)。Graphics類及其成員函數都被定義在頭文件Gdiplusgraphics.h中。

Graphics類的構造函數有如下4種:

Graphics(Image* image); // 用於繪製圖像

Graphics(HDC hdc); // 用於在當前窗口中繪圖

Graphics(HDC hdc, HANDLE hdevice); // 用於在指定設備上繪製圖形

Graphics(HWND hwnd, BOOL icm = FALSE); // 用於在指定窗口中繪圖可以進行顏色調整

其中,最常用的是第二種——在當前視圖窗口中繪圖的圖形類構造函數。

注意,該構造函數的輸入參數,是設備上下文的句柄,而不是CDC類對象的指針。一般可以由CDC對象得到(CDC類含有公用數據成員HDC m_hDC;)

6種繪製直線和折線的函數:(前三個爲整數版,後三個爲對應的浮點數版) // 畫折線DrawLines

Status DrawLine(const Pen* pen, INT x1, INT y1, INT x2, INT y2);

Status DrawLine(const Pen* pen, const Point& pt1, const Point& pt2);

Status DrawLines(const Pen* pen, const Point* points, INT count); // 畫折線

Status DrawLine(const Pen* pen, REAL x1, REAL y1, REAL x2, REAL y2);

Status DrawLine(const Pen* pen, const PointF& pt1, const PointF& pt2);

Status DrawLines(const Pen* pen, const PointF* points, INT count);

6種繪製矩形和矩形組的函數:(也是前三個爲整數版,後三個爲對應的浮點數版) // Rectangle = rect angle

Status DrawRectangle(const Pen* pen, const Rect& rect);

Status DrawRectangle(const Pen* pen, INT x, INT y, INT width, INT height);

Status DrawRectangles(const Pen* pen, const Rect* rects, INT count);

Status DrawRectangle(const Pen* pen, const RectF& rect);

Status DrawRectangle(const Pen* pen, REAL x, REAL y, REAL width, REAL height);

Status DrawRectangles(const Pen* pen, const RectF* rects, INT count);

繪製橢圓的函數,如果輸入參數所確定的外接矩形的寬高相等,則畫圓。(也是前兩個爲整數版,後兩個爲對應的浮點數版)

Status DrawEllipse(const Pen* pen, const Rect& rect);

Status DrawEllipse(const Pen* pen, INT x, INT y, INT width, INT height)

Status DrawEllipse(const Pen* pen, const RectF& rect);

Status DrawEllipse(const Pen* pen, REAL x, REAL y, REAL width, REAL height);

繪製橢圓弧的函數,如果輸入參數所確定的外接矩形的寬高相等,則畫圓弧。(也是前兩個爲整數版,後兩個爲對應的浮點數版)

Status DrawArc(const Pen* pen, INT x, INT y, INT width, INT height, REAL startAngle, REAL sweepAngle); // sweep 掠過

Status DrawArc(const Pen* pen, const Rect& rect, REAL startAngle, REAL sweepAngle);

Status DrawArc(const Pen* pen, REAL x, REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle);

Status DrawArc(const Pen* pen, const RectF& rect, REAL startAngle, REAL sweepAngle);

 

 

畫弧函數的輸入參數 // 注意:順時鐘方向

該函數的功能與GDI的Arc相同:

BOOL Arc( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 );

BOOL Arc( LPCRECT lpRect, POINT ptStart, POINT ptEnd );

但是也有區別,主要是,最後的參數不再是弧的終角,而是弧段所對應的掃描角。這倒是與另一個GDI畫圓弧函數類似(其中(x, y)爲圓心、nRadius爲半徑、fStartAngle爲起始角、fSweepAngle也爲弧段跨角):

BOOL AngleArc(int x, int y, int nRadius, float fStartAngle, float fSweepAngle);

當然,GDI+確定矩形的後兩個參數也不再是右下角座標,而改成寬高了,這已經是老問題了。

另外要注意的是,角度的單位是度(不是弧度,C++的三角函數採用的是弧度單位),而且都必須是實數。零度角爲x軸方向,順時針方向爲正(這與數學上反時針方向爲正剛好相反)。 // 如上圖所示。

繪製多邊形的函數,第一個爲整數版,後一個爲對應的浮點數版:

Status DrawPolygon(const Pen* pen, const Point* points, INT count);

Status DrawPolygon(const Pen* pen, const PointF* points, INT count);

其中,各參數的含義同畫折線函數DrawLines的,只是DrawPolygon函數會將點數組中的起點和終點連接起來,形成一個封閉的多邊形區域。

該函數的功能與GDI的Polygon相同:

BOOL Polygon( LPPOINT lpPoints, int nCount );

 

注意:GDI+中沒有提供,與GDI函數RoundRect(圓角矩形)和Chord(弓弦),具有類似功能的繪圖函數。可以利用矩形+橢圓和弧+直線等函數自己來實現。

在GDI+中畫填充圖,不需像GDI那樣得先將刷子選入DC,而是與GDI+畫線狀圖的函數類似,將刷子作爲畫填充圖函數的第一個輸入參數。

l 畫填充矩形[組] FillRectangle[s]

Status FillRectangle(const Brush* brush, const Rect& rect);

Status FillRectangle(const Brush* brush, INT x, INT y, INT width, INT height);

Status FillRectangles(const Brush* brush, const Rect* rects, INT count);

Status FillRectangle(const Brush* brush, const RectF& rect);

Status FillRectangle(const Brush* brush, REAL x, REAL y, REAL width, REAL height);

Status FillRectangles(const Brush* brush, const RectF* rects, INT count);

用指定刷子Brush,填充rect的內部區域,無邊線,填充區域包括矩形的左邊界和上邊界,但不包括矩形的右邊界和下邊界。功能與GDI的FillRect類似:

void FillRect( LPCRECT lpRect, CBrush* pBrush );

但是,GDI中沒有同時填充一個矩形數組的函數。不過GDI卻有GDI+沒有的畫填充圓角矩形的函數FillSolidRect。

l 畫填充[橢]圓FillEllipse

Status FillEllipse(const Brush* brush, const Rect& rect);

Status FillEllipse(const Brush* brush, INT x, INT y, INT width, INT height);

Status FillEllipse(const Brush* brush, const RectF& rect);

Status FillEllipse(const Brush* brush, REAL x, REAL y, REAL width, REAL height);

GDI中沒有類似函數,但可以用(採用當前刷填充的)Ellipse來代替。

l 畫餅圖DrawPie // pie餡餅 DrawPie與FillPie

Status DrawPie(const Pen* pen, const Rect& rect, REAL startAngle, REAL sweepAngle);

Status DrawPie(const Pen* pen, INT x, INT y, INT width, INT height, REAL startAngle, REAL sweepAngle);

Status DrawPie(const Pen* pen, const RectF& rect, REAL startAngle, REAL sweepAngle);

Status DrawPie(const Pen* pen, REAL x, REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle);

與GDI的下列函數類似,但是部分輸入參數的含義有所不同:

BOOL Pie( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 );

BOOL Pie( LPCRECT lpRect, POINT ptStart, POINT ptEnd );

 

l 畫填充多邊形FillPolygon

Status FillPolygon(const Brush* brush, const Point* points, INT count);

Status FillPolygon(const Brush* brush, const Point* points, INT count, FillMode fillMode);

Status FillPolygon(const Brush* brush, const PointF* points, INT count);

Status FillPolygon(const Brush* brush, const PointF* points, INT count, FillMode fillMode);

前面講的各種畫線狀圖或填充圖的GDI+函數,雖然在形式上與GDI的有所不同(函數名前加了Draw或Fill、將筆或刷作爲第一個輸入參數、部分位置輸入參數改成了大小參數、並增加了浮點數版),但是在功能上卻是相同的。

現在要講的曲線繪製,則是GDI+新增加的內容。曲線在機械設計、工程建築和圖形動畫等領域,都有十分廣泛應用。

常用的曲線有Bezier(貝塞爾)曲線和樣條(spline)曲線。貝塞爾曲線比較簡單,適合於畫控制點少的曲線。當控制點太多時,要不曲線的次數(比點數少1)太高,要不拼接比較困難,而且沒有局部性(即修改一點影響全局),性能不太好。而樣條曲線則可以畫任意多個控制點的曲線,曲線的次數也可以指定(一般爲二次或三次,如TrueType字體採用的是二次B樣條曲線),並且具有局部性。貝塞爾曲線特別是樣條曲線有很多變種。常見的貝塞爾曲線有普通貝塞爾曲線和有理貝塞爾曲線。常用的樣條曲線有:B樣條、β樣條、Hermite(厄密)樣條、基數樣條、Kochanek-Bartels樣條和Catmull-Rom樣條等。

GDI+中所實現的是普通貝塞爾曲線(不過控制點,位於控制多邊形的凸包之內)和基數樣條曲線(過控制點)。

l 基數樣條曲線(cardinal spline curve) // DrawCurve與DrawClosedCurve

Status DrawCurve(const Pen* pen, const Point* points, INT count); // tension = 0.5f

Status DrawCurve(const Pen* pen, const Point* points, INT count, REAL tension);

Status DrawCurve(const Pen* pen, const Point* points, INT count, INT offset, INT numberOfSegments, REAL tension = 0.5f); // 只畫部分點

Status DrawCurve(const Pen* pen, const PointF* points, INT count);

Status DrawCurve(const Pen* pen, const PointF* points, INT count, REAL tension);

Status DrawCurve(const Pen* pen, const PointF* points, INT count, INT offset, INT numberOfSegments, REAL tension = 0.5f);

Status DrawClosedCurve(const Pen* pen, const Point* points, INT count);

Status DrawClosedCurve(const Pen *pen, const Point* points, INT count, REAL tension);

Status DrawClosedCurve(const Pen* pen, const PointF* points, INT count);

Status DrawClosedCurve(const Pen *pen, const PointF* points, INT count, REAL tension);

其中:

參數tension(張力)指定曲線的彎曲程度,tension = 0.0(直線)~ 1.0(最彎曲)

無張力版的函數的 tension = 0.5(缺省值)

第3/6個DrawCurve,只畫從points[offset]開始的numberOfSegments個點組成的部分曲線段

DrawClosedCurve函數(連接首尾點)畫封閉的基數樣條曲線

l 貝塞爾曲線(Bezier curve) DrawBezier

Status DrawBezier(const Pen* pen, INT x1, INT y1, INT x2, INT y2, INT x3, INT y3, INT x4, INT y4);

Status DrawBezier(const Pen* pen, const Point& pt1, const Point& pt2, const Point& pt3, const Point& pt4);

Status DrawBeziers(const Pen* pen, const Point* points, INT count);

Status DrawBezier(const Pen* pen, REAL x1, REAL y1, REAL x2, REAL y2, REAL x3, REAL y3, REAL x4, REAL y4);

Status DrawBezier(const Pen* pen, const PointF& pt1, const PointF& pt2, const PointF& pt3, const PointF& pt4);

Status DrawBeziers(const Pen* pen, const PointF* points, INT count);

l 填充封閉基數樣條曲線 FillClosedCurve

Status FillClosedCurve(const Brush* brush, const Point* points, INT count);

Status FillClosedCurve(const Brush* brush, const Point* points, INT count, FillMode fillMode, REAL tension = 0.5f);

Status FillClosedCurve(const Brush* brush, const PointF* points, INT count);

Status FillClosedCurve(const Brush* brush, const PointF* points, INT count, FillMode fillMode, REAL tension = 0.5f);

GDI中沒有用於清屏的專門函數,得自己用背景色畫窗口大小的填充矩形,或者調用窗口類的Invalidate和UpdateWindow函數。現在,GDI+有了清屏函數Clear:

Status Clear(const Color &color);

其中的輸入參數color,爲用戶指定的填充背景色。例如:

Graphics graph(GetDC()->m_hDC);

graph.Clear(Color::White); // 使用Graphics類之對象調用

 

4、 Pen

與GDI中的一樣,GDI+中的筆(pen鋼筆/畫筆)也是畫線狀圖的工具,但是功能更加強大。例如:透明筆、圖案筆、自定義虛線風格、線帽、筆的縮放和旋轉、筆的連接點屬性等。

GDI+中的筆對應於Pen類,被定義在GdiplusPen.h頭文件中。

筆的構造函數主要有兩個:

Pen(const Color &color, REAL width = 1.0); // 單色筆

Pen(const Brush *brush, REAL width = 1.0); // 紋理圖案筆

其中,最常用的是第一個,它構造一個顏色爲color,寬度爲width(缺省爲1)的單色筆。如果顏色的α值<255,則所創建的筆就是帶透明度的筆。

5、 Brush

與GDI中的一樣,GDI+中的刷(brush畫刷/畫筆)也是畫填充圖的工具,GDI+中也有與GDI相對應的實心刷(單色刷)、條紋刷(影線刷)和紋理刷(圖像刷)。不過,GDI+又新增加了功能強大的線性漸變刷和路徑漸變刷,而且還爲所有這些刷各自建立了對應的類,基類是Brush(功能少)。

下面是GDI+中各種刷類的層次結構圖:

GdiplusBase

Brush

SolidBrush

HatchBrush

TextureBrush



LinearGradientBrush

PathGradientBrush
 


GDI+刷類的層次結構

// (1) SolidBrush實心刷

// (2) HatchBrush 條紋刷

// (3) TextureBrush 紋理刷

// (4) LinearGradientBrush 線性漸變刷 // gradient傾斜的,梯度

// (5) PathGradientBrush 路徑漸變刷

所有刷類都被定義在頭文件GdiplusBrush.h中。

6、 文字

GDI+的文本排版和字體處理的功能比GDI的更加強大。特別是Windows XP提供了對LCD(液晶)顯示器的特殊優化功能,GDI+也提供了對應的ClearType(清晰活字)文字處理技術,以增強字體的清晰度。另外,GDI+還提供了構造專用字體集的功能,可以包含私有的臨時字體(不需預先安裝到系統中)。

Windows中使用的字體,一般是TrueType(真實活字)字體(TTF = TrueType Font),它是1991年Apple 和Microsoft 聯合開發的一種字體技術,採用二次B樣條曲線來描述字符的輪廓。

在GDI+中,與文字相關的類有:字體族類FontFamily、字體類Font和字體集類FontCollection及其兩個派生類InstalledFontCollection(已安裝字體集)和PrivateFontCollection(專用字體集)。(在GDI中只有CFont一個字體類)

這些類的層次結構爲:

FontCollection

InstalledFontCollection

PrivateFontCollection

Font

FontFamily

GdiplusBase
 


在GDI中,我們用CDC類的成員函數TextOut、DrawText和ExtTextOut等來輸出文本串。在GDI+中,我們則是利用Graphics類的重載成員函數DrawString來繪製文本。

7、 路徑

路徑(path)是一系列相互連接的直線和曲線,由許多不同類型的點所構成,用於表示複雜的不規則圖形,也叫做圖形路徑(graphics path)。路徑可以被畫輪廓和填充,也可以用於創建區域和路徑漸變刷等。

在GDI中也有路徑(我們沒有講),但是它只是作爲DC的一種狀態才能存在。獨立的路徑對象,則是GDI+的新特點。

8、 區域

區域(region)由若干幾何形狀所構成的一種封閉圖形,主要用於複雜圖形的繪製、圖形輸出的剪裁和鼠標擊中的測試。最簡單也是最常用的區域是矩形,其次是橢圓和多邊形以及它們的組合。這些也正是GDI所支持的區域類型。

GDI+中的區域是一種顯示錶面的範圍(an area of the display surface),可以是任意形狀(的圖形的組合),邊界一般爲路徑。除了上面所講的矩形、橢圓、多邊形之外,其邊界還可以含直線、折線、弧、貝塞爾曲線和樣條曲線等開圖形,其內容還可以包含餅、閉曲線等閉圖形。

在GDI+中,區域所對應的類是Region,它是一個獨立的類(沒有基類,也沒有派生類)。但是它又若干相關的類,如各種圖形類和圖形路徑類等。

Region類有6個構造函數:

Region(VOID); // 創建一個空區域

Region(const Rect &rect); // 創建一個整數型矩形區域

Region(const RectF &rect); // 創建一個浮點數型矩形區域

Region(const GraphicsPath *path); // 由圖形路徑來創建區域

Region(const BYTE *regionData, INT size);// 由(另一)區域的數據構造區域

Region(HRGN hRgn); // 由GDI的區域句柄構造區域

其中,創建矩形區域最簡單,由路徑創建區域最常用。

9、 變換

變換(transform)是GDI+新增加的強大功能,包括圖形對象的簡單變換和基於矩陣的座標變換、圖形變換、圖像變換、色彩變換、路徑變換和區域變換等。

GDI+的核心——圖形類Graphics,提供了3個成員函數,可以對其所繪製的圖形進行平移(translate)、旋轉(rotate)和伸縮(scale比例尺/縮放)等基本的圖形變換:(與紋理刷類中的對應函數的原型是一樣的)

Status TranslateTransform(REAL dx, REAL dy, MatrixOrder order = MatrixOrderPrepend);

Status RotateTransform(REAL angle, MatrixOrder order = MatrixOrderPrepend);

Status ScaleTransform(REAL sx, REAL sy, MatrixOrder order = MatrixOrderPrepend);

其中的最後一個輸入參數爲矩陣相乘的順序,取值爲矩陣順序枚舉類型MatrixOrder中的符號常量,缺省值都爲MatrixOrderAppend(左乘):

typedef enum {

MatrixOrderPrepend = 0, // 矩陣左乘(預先序,前置)

MatrixOrderAppend = 1 // 矩陣右乘(追加序,後綴)

} MatrixOrder;

因爲這些變換都可以用矩陣表示,而且與圖形對象已經設置的現有變換矩陣要進行合成(相當於兩個變換矩陣進行乘法運算)。

在圖形對象的這三種基本變換中,最常用的是第一種——平移變換。我們在前面曾多次使用,避免了重複定義(有座標平移的)繪圖區域的麻煩。

10、 圖像

GDI+支持如下9種用於Windows的常見圖像格式:

l BMP——BitMaP(位圖),擴展名爲.BMP,由Microsoft與IBM於1980年代中期爲Windows和PS/2制訂的圖像格式,一般不壓縮。支持黑白、僞彩圖、灰度圖和真彩圖,每像素位數可爲1、4、8、16、24、32、64等,常用的是24位位圖。

l GIF——Graphics Interchange Format(可交換圖形格式),擴展名爲.GIF,由CompuServe公司1987年制定,採用無損的變長LZW壓縮算法。只支持僞彩圖(最多256索引色),寬高用雙字節無符號整數表示(最多64K*64K像素)。可存儲多幅圖片,常用於簡單的網絡動畫。壓縮比較高,使用廣泛。

l JPEG——Joint Photographic Experts Group(聯合圖象專家組),擴展名爲.JPG,是國際標準化組織ISO和IEC於1991年聯合制定的靜態圖像壓縮標準,採用以DCT爲主的有損壓縮方法。支持灰度圖和真彩圖,但是不支持僞彩圖。壓縮比高,使用廣泛。

l Exif——EXchangeable Image File Format(可交換圖像文件格式),擴展名爲.Exit?,由JEIDA(Japan Electronic Industry Development Association日本電子工業發展協會/日本電子情報技術產業協會)於1996年10月制定。用於數碼相機,內含JPEG圖像,另包含拍攝日期、快門速度、曝光時間、照相機製造廠商和型號等相關信息。

l PNG——Portable Network Graphic Format(可移植網絡圖形格式,讀成“ping”),擴展名爲.png,由W3C(World Wide Web Consortium萬維網協會)於1996年10月推出的一種標準圖像格式,2003年成爲ISO國際標準。PNG採用與GIF一樣的無損壓縮方法,但是除了僞彩圖外,PNG還支持多達16位深度的灰度圖像和48位深度的彩色圖像,並且還可支持多達16位的α通道數據。

l TIFF——Tag Image File Format(標籤圖像文件格式),擴展名爲.tif,由Aldus於1986年秋聯合多家掃描儀製造商和軟件公司共同開發,支持黑白、索引色、灰度、真彩圖,可校正顏色和調色溫,支持多種壓縮編碼(如Huffman、LZW、RLE),可存儲多幅圖片。常用於對質量要求高的專業圖像的存儲。

l ICON——icon(圖標),擴展名爲.ico,由Microsoft與IBM於1980年代中期爲Windows和PS/2制訂的圖標圖像格式。圖像大小爲16*16、32*32或54*64。

l WMF——Windows MetaFile(視窗元文件),擴展名爲.WMF,由Microsoft與IBM於1980年代中期爲Windows和PS/2制訂的圖形文件格式,用於保存GDI的繪圖指令記錄。

l EMF——Enhanced Windows MetaFile(增強型視窗元文件),擴展名爲.EMF,是微軟公司於1993年隨32位操作系統Windws NT推出的一種改進的WMF格式,用於Win32。GDI+使用的是擴展EMF格式——EMF+。

 

GDI+的圖像及其處理的功能十分強大,可以用不同的格式加載、保存和操作圖像。但由於篇幅所限,本小節只介紹最基本的內容。

GDI+中有三個圖像類,其中的Image(圖像)爲基類,其他兩個爲它的派生類——Bitmap(位圖)和Metafile([圖]元文件/矢量圖形)。它們的類層次結構如下圖所示:

GdiplusBase

Image

Bitmap

Metafile
 


圖像類的層次結構

 

除此之外,還有大量與圖像處理有關的GDI+類,如Effect類及其11個派生類以及與圖像數據和信息有關的7個獨立類。由於時間關係,我們只准備介紹上面這三個主要的圖像類及其基本操作。

11、 圖元文件

從一開始GDI就支持(圖)元文件(metafile),早期(1985年)的版本爲WMF(Windows MetaFile視窗元文件),主要針對Win16(Win3.x),後來(1990年)也支持Win32(Win95/ 98/Me)。以後(1993年)隨Windows NT推出了改進的元文件版本——EMF(Enhanced Windows MetaFile增強型視窗元文件),只支持Win32(Win95/98/Me/NT/2000/XP)。現在(2001年)又隨GDI+推出了加強型EMF——EMF+,可以同時支持GDI和GDI+。

元文件所支持的GDI類型

元文件類型
Win16 GDI
Win32 GDI
Win32/64 GDI+

WMF


×

EMF
×

×

EMF+
×


 

 

雖然在GDI+中,將圖元文件所對應的類Metafile作爲Image的派生類,但這只是爲了圖元文件可以同時處理圖形和圖像。其實圖元文件中所包含的就是一系列繪圖(包括繪製圖像)指令及參數,屬於矢量圖形文件。它所佔空間小、可以任意縮放(不會產生馬賽克效應),但是繪製圖形需要一定的時間。

在GDI+中,圖元文件對應的類爲Metafile,它是Image類的派生類。GDI+的Metafile類支持三種類型的圖元文件:僅EMF類型、僅EMF+類型、EMF及EMF+雙重類型(缺省值)。它們對應於枚舉類型:

typedef enum {

EmfTypeEmfOnly = MetafileTypeEmf, // 僅EMF類型

EmfTypeEmfPlusOnly = MetafileTypeEmfPlusOnly, // 僅EMF+類型

EmfTypeEmfPlusDual = MetafileTypeEmfPlusDual // EMF及EMF+雙重類型

} EmfType; // enhance meta file

Metafile類有13個構造函數:

// 文件型

Metafile(const WCHAR *filename);

Metafile(const WCHAR *fileName, HDC referenceHdc, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);

Metafile(const WCHAR *fileName, HDC referenceHdc, const Rect &frameRect, MetaFileFrameUnit frameUnit = MetafileFrameUnitGdi, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);

Metafile(const WCHAR *fileName, HDC referenceHdc, const RectF &frameRect, MetafileFrameUnit frameUnit = MetafileFrameUnitGdi, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);

// 流型

Metafile(IStream *stream);

Metafile(IStream *stream, HDC referenceHdc, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);

Metafile(IStream *stream, HDC referenceHdc, const Rect &frameRect, MetafileFrameUnit frameUnit = MetafileFrameUnitGdi, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);

Metafile(IStream *stream, HDC referenceHdc, const RectF &frameRect, MetafileFrameUnit frameUnit = MetafileFrameUnitGdi, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);

// DC句柄型

Metafile(HDC referenceHdc, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);

Metafile(HDC referenceHdc, const Rect &frameRect, MetafileFrameUnit frameUnit = MetafileFrameUnitGdi, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);

Metafile(HDC referenceHdc, const RectF &frameRect, MetaFileFrameUnit frameUnit = MetafileFrameUnitGdi, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);

// WMF/EMF句柄型

Metafile(HENHMETAFILE hEmf, BOOL deleteEmf = FALSE);

Metafile(HMETAFILE hWmf, const WmfPlaceableFileHeader *wmfPlaceableFileHeader, BOOL deleteWmf = FALSE);

其中用到的枚舉類型有:

typedef enum {

MetafileFrameUnitPixel = UnitPixel, // 象素

MetafileFrameUnitPoint = UnitPoint, // 點

MetafileFrameUnitInch = UnitInch, // 英寸

MetafileFrameUnitDocument = UnitDocument, // 文擋

MetafileFrameUnitMillimeter = UnitDocument + 1, // 毫米

MetafileFrameUnitGdi = UnitDocument + 2 // GDI+單位數目

} MetafileFrameUnit;

typedef struct {

UINT32 Key; // 鍵

INT16 Hmf; //

PWMFRect16 BoundingBox; // 邊界盒

INT16 Inch; // 英寸

UINT32 Reserved; // 保留

INT16 Checksum; // 檢測和

} WmfPlaceableFileHeader;

http://www.caenet.cn/Forums/

其中,最簡單常用的構造函數是:// 不帶DC參數,只能用於打開已經存在的元文件

Metafile(const WCHAR *filename);

它由文件名來構造元文件對象。例如:

Metafile mf(L"yyy.emf");

常用且完整的構造函數是:// 帶DC參數,只用於創建新圖元文件

Metafile(const WCHAR *fileName, HDC referenceHdc, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);

它可以指定元文件類型,並加上描述串。例如:

Metafile mf(L"yyy.emf", GetDC()->m_hDC, MetafileTypeEmf, L"陰陽魚");

另一個較爲常用的構造函數是:

Metafile(HDC referenceHdc, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);

它用於構造內存元文件。這些內存元文件構造函數還有對應的流構造函數版本。

Metafile類的其他成員函數有:

// 顯示元文件記錄,需要與Graphics類的EnumerateMetafile

Status PlayRecord(EmfPlusRecordType recordType, UINT flags, UINT dataSize, const BYTE *data);

// 函數及用戶自定義的回調函數配套使用(似GDI的)

static UINT EmfToWmfBits(HENHMETAFILE hemf, UINT cbData16, LPBYTE pData16, INT iMapMode, EmfToWmfBitsFlags eFlags); // 用於EMF到WMF的轉換

HENHMETAFILE GetHENHMETAFILE(VOID); // 可用於EMF的SDK函數

// 獲取和設置底層光柵限制,用於減少刷空間大小 // 陳寶楷???

UINT GetDownLevelRasterizationLimit(VOID);

Status SetDownLevelRasterizationLimit(UINT metafileRasterizationLimitDpi);

// 獲取元文件頭

Status GetMetafileHeader(MetafileHeader *header) const;

static Status GetMetafileHeader(const WCHAR *filename, MetafileHeader *header);

static Status GetMetafileHeader(IStream *stream, MetafileHeader *header);

static Status GetMetafileHeader(HENHMETAFILE *hEmf, MetafileHeader *header);

static Status GetMetafileHeader(HMETAFILE hWmf, const WmfPlaceableFileHeader *wmfPlaceableFileHeader, MetafileHeader *header);

爲了將繪圖記錄保存到圖元文件中,需要先創建元文件對象,然後用該圖元文件對象再來創建圖形對象,最後調用圖形類的各種繪圖函數來向圖元文件中添加繪圖記錄。

具體方法如下:

可以先使用Metafile類的用於創建新圖元文件的構造函數(帶DC參數的),如

Metafile(const WCHAR *fileName, HDC referenceHdc, EmfType type =

EmfTypeEmfPlusDual, const WCHAR *description = NULL);

來創建元文件對象。

然後使用Graphics類的構造函數(注意,Metafile是Image的派生類)

Graphics(Image* image);

來創建圖形對象。

最後調用各種圖形類的圖形設置、操作和繪製函數成員函數來向圖元文件添加繪圖記錄。

例如:

Metafile *myMetafile = new Metafile(L"MyDiskFile.emf", GetDC()->m_hDC);

Graphics *myGraphics = new Graphics(myMetafile);

// SmoothingMode::tiAlias不能在VC中使用,可在C#中使用。

myGraphics->SetSmoothingMode(SmoothingModeAntiAlias);

myGraphics->RotateTransform(30);

// Create an elliptical clipping region.

GraphicsPath myPath;

myPath.AddEllipse(0, 0, 200, 100);

Region myRegion(&myPath);

myGraphics->SetClip(&myRegion);

Pen myPen(Color(255, 0, 0, 255));

myGraphics->DrawPath(&myPen, &myPath);

for(int j=0; j<=300; j+=10) myGraphics->DrawLine(&myPen,0,0,300-j,j);

delete myGraphics;

delete myMetafile;

可以先使用Metafile類的用於打開已有圖元文件的構造函數(不帶DC參數的),如

Metafile(const WCHAR *filename);

來創建元文件對象。

然後再調用Graphics類的各種DrawImage成員函數,如:

Status DrawImage(Image *image, INT x, INT y);

來重畫圖元文件中的所有繪圖記錄。

另外,爲了獲取當前圖元文件的邊界矩形,可以先調用Metafile類的成員函數:

Status GetMetafileHeader(MetafileHeader *header) const;

來獲取MetafileHeader對象,然後再用MetafileHeader類的成員函數:

void GetBounds(Rect *rect);

得到邊界矩形。可用於Graphics類的DrawImage成員函數:

DrawImage(Image *image, const Rect &rect);

 

注意,如果用帶DC參數的構造函數來創建Metafile對象,則會清空原圖元文件(以便重新開始添加記錄),不能用於圖元文件的播放。

可以利用Metafile類的成員函數

Status PlayRecord(EmfPlusRecordType recordType, UINT flags,

UINT dataSize, const BYTE *data);

來重畫圖元文件中指定記錄。與EMF中討論的類似,該函數需要與Graphics類的枚舉元文件成員函數(共有12個同名的重載函數),如:

Status EnumerateMetafile(const Metafile *metafile, const PointF &destPoint, EnumerateMetafileProc callback, VOID *callbackData = NULL, ImageAttributes *imageAttributes = NULL);

配套使用,該函數遍歷圖元文件的每個記錄,並調用用戶自定義的回調函數(該函數可以自己命名)

BOOL CALLBACK metaCallback(EmfPlusRecordType recordType, unsigned int flags, unsigned int dataSize, const unsigned char* pStr, void* callbackData);

對記錄進行各種處理,包括使用元文件的成員函數PlayRecord來繪製(播放)記錄。

在GDI+中,想實現交互繪圖時的窗口動態重畫,非常困難。

雖然Metafile類有一個成員函數

HENHMETAFILE GetHENHMETAFILE(VOID);

可以用於獲取圖元文件的句柄,但經過我的實驗發現,它只對使用不帶DC輸入參數的構造函數所創建的不能用於添加繪圖記錄的Metafile對象有效。

另外,雖說可以創建內存Metafile對象,但是GDI+卻沒有提供任何辦法(沒有複製、保存、克隆等函數,父類Image的對應函數對寫入型Metafile對象都是無效的),可將其保存到圖元文件中。因爲無法獲得用於添加記錄的圖元文件的句柄,所以各種SDK函數也派不上用場。

 

因爲除了幫助文檔,幾乎無資料可看,唯一的途徑就是編碼做試驗。下面是我經過很長時間,好不容易纔摸索出來的,一種可行的解決辦法(但是很臭。你們可以尋找其他辦法,如果有了更好的方法,請與大家共享):

 

(說明:爲了防止重畫圖元文件時,圖形的位置有偏移或其大小發生變化,可以採用如下的構造函數:

Metafile(const WCHAR *fileName, HDC referenceHdc, const Rect &frameRect, MetaFileFrameUnit frameUnit = MetafileFrameUnitGdi, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);

來創建Metafile對象。其中的邊框矩形,可以設置爲屏幕大小,並使用像素單位。該邊框同時還用於進行圖元文件重畫的Graphics類的DrawImage函數。)

 

在視圖類中定義如下幾個類變量:Metafile對象及其對應的Graphics對象的指針、邊框矩形、兩個圖元文件名的寬字符串數組(以便繞開GDI+的文件鎖定功能)、以及在這兩個文件名中切換的整數。如:

Metafile *mf;

Graphics *mfGraph;

Rect rect0;

wchar_t *fns[2]; // wchar_t

int fni;

 

在視圖類的構造函數中,初始化部分類變量:

mf = NULL;

mfGraph = NULL;

fns[0] = L"draw.emf";

fns[1] = L"draw0.emf";

fni = 0;

 

在視圖類的初始化函數OnInitialUpdate中,計算邊框矩形、創建Metafile對象:

HDC hdcRef = GetDC()->m_hDC;

rect0.X = 0;

rect0.Y = 0;

rect0.Width = GetDeviceCaps(hdcRef, HORZRES);

rect0.Height = GetDeviceCaps(hdcRef, VERTRES);

mf = new Metafile(fns[fni], hdcRef, rect0, MetafileFrameUnitPixel);

mfGraph = new Graphics(mf);

 

在視圖類的OnLButtonUp等函數中,利用圖元文件所對應的圖形對象,向圖元文件添加各種繪圖記錄。如:

mfGraph->DrawLine(&Pen(Color::Green), p0.x, p0.y, point.x , point.y);

……

 

在視圖類的OnDraw函數中,刪除當前元文件對象(系統纔會將元文件的內容寫入磁盤)和對應的圖形對象,打開該磁盤元文件並播放。然後,切換文件名,創建新的元文件對象和對應的圖形對象,並將老元文件中現有的記錄,通過新元文件所對應的圖形對象的圖像繪製,加入到新元文件中,最後刪除老元文件的句柄。如:

delete mfGraph;

delete mf;

Metafile *mf0 = new Metafile(fns[fni]);

Graphics graph(pDC->m_hDC);

graph.DrawImage(mf0, rect0);

fni = !fni; // 相當於if(fni) fni = 0; else fni = 1;

mf = new Metafile(fns[fni], pDC->m_hDC, rect0, MetafileFrameUnitPixel);

mfGraph = new Graphics(mf);

mfGraph->DrawImage(mf0, rect0);

delete mf0;

 

最後,在視圖類的析構函數中,刪除當前元文件對象(系統會將元文件的內容寫入磁盤)和對應的圖形對象。例如:

delete mfGraph;

delete mf;

 

 

三、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頭文件,解決辦法是:

l 將該頭文件複製到你的項目目錄中;

l 註釋掉該頭文件裏面GdiplusBase類的內容(保留類定義);

l 在你項目中所有的#include <gdiplus.h>語句之前,包含"GdiplusBase.h"頭文件,形如:

#include "gdiplusBase.h"

#include <gdiplus.h>

l 則編譯系統會優先包含項目目錄中的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);

或直接改爲

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

 

3)、調試問題

現在版本的VC05存在許多Bug,特別是GDI+程序在調試時的問題就更多。解決辦法是:

l 在編譯運行時,不使用Debug配置,而改用Release配置;

l 運行時不使用調試運行(F5),而改用不調試直接運行(Ctrl +F5);

l 最好是用靜態鏈接的MFC庫,而不用DLL動態庫。

 

常用的調試方法有:

l 使用MessageBox信息框:

n 在視圖類中的常用格式爲

MessageBox(L"提示信息");

n 在應用程序類和文檔類中的常用格式爲

MessageBox(NULL, L"提示信息", L"標題", MB_OK); // Win32 API

l 設置斷點,然後逐步運行(F10)或F11。

l 運行當前位置,然後逐步運行(F10)

l 利用調試界面中的“局部變量”和“監視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);

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"大小漸變文字測試", -1, // 將文本串加入路徑

&FontFamily(L"隸書"), FontStyleRegular, 100, Point(0, 0), NULL);

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]; // 動態創建點數組

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"MagicLinux.bmp");

Image* pScaledImage = NULL;

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

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

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

delete pScaledImage;

delete image;

 

方法二 :使用 Graphics::DrawImage, 這樣還可以控制 InterpolationMode( 插值模式 , 在縮放和旋轉時候使用 ), 即可以控制縮放質量高低 . 也可以通過 Graphics 對象做其他的控制 .

 

Bitmap * ScaleBitmap(Bitmap * pBitmap,UINT nWidth,UINT nHeight)

{

Bitmap * pTemp = new Bitmap(nWidth,nHeight,pBitmap->GetPixelFormat());

if( pTemp )

{

Graphics * g = Graphics::FromImage(pTemp);

if( g )

{

// use the best interpolation mode

g->SetInterpolationMode(InterpolationModeHighQualityBicubic);

g->DrawImage(pBitmap,0,0,nWidth,nHeight);

delete g;

}

}

return pTemp;

}

pScaledImage = ScaleBitmap(image,nWidth,nHeight);

 

保存圖片的代碼 :

bool SaveAsImageFile(Image * pImage,LPCWSTR lpszFileName,LPCWSTR lpszImageType)

{

UINT num = 0;

// number of image encoders

UINT size = 0;

// size of the image encoder array in bytes

ImageCodecInfo* pImageCodecInfo = NULL;

if(GetImageEncodersSize(&num, &size)!= Ok || size == 0 )

return false; // Failure

 

pImageCodecInfo = (ImageCodecInfo*)(malloc(size));

 

if(pImageCodecInfo == NULL)

return false; // Failure

 

if(GetImageEncoders(num, size, pImageCodecInfo)!= Ok )

return false;

 

bool bOk = false;

for(UINT j = 0; j < num; ++j)

{

if( wcscmp(pImageCodecInfo[j].MimeType, lpszImageType) == 0 )

{

pImage->Save(lpszFileName,&(pImageCodecInfo[j].Clsid));

bOk = true;

break;

}

}

free(pImageCodecInfo);

return bOk;

}

調用象這樣就可以保存成各種圖片 ( 當然要求有對應的 encoder 才行 ):

SaveAsImageFile (pScaledImage,L"1.bmp",L"image/bmp");

SaveAsImageFile (pScaledImage,L"1.jpg",L"image/jpeg");

SaveAsImageFile (pScaledImage,L"1.png",L"image/png");

 


9)GetPixel/SetPixel速度慢問題

GDI+的Bitmap類提供了兩個罪惡的函數GetPixel, SetPixel,用來獲取某個像素點的顏色值。這個2個函數如果只調用一次兩次也就罷了,如果調用多次速度就很慢了,可以使用LockBits方法,就是把圖像的內存區域根據格式鎖定,拿到那塊內存的首地址,通過直接操作內存。比如將圖象灰度化:

int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)

{

UINT num = 0; // number of image encoders

UINT size = 0; // size of the image encoder array in bytes

 

ImageCodecInfo* pImageCodecInfo = NULL;

 

GetImageEncodersSize(&num, &size);

if(size == 0)

return -1; // Failure

 

pImageCodecInfo = (ImageCodecInfo*)(malloc(size));

if(pImageCodecInfo == NULL)

return -1; // Failure

 

GetImageEncoders(num, size, pImageCodecInfo);

 

for(UINT j = 0; j < num; ++j)

{

if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )

{

*pClsid = pImageCodecInfo[j].Clsid;

free(pImageCodecInfo);

return j; // Success

}

}

 

free(pImageCodecInfo);

return -1; // Failure

}

//圖象灰度處理

void CGDIPlusTestView::OnImageGray()

{ Graphics g(this->m_hWnd);

Bitmap imLoad(_T("c://0250001207A.bmp"));

long nImageWidth = imLoad.GetWidth();

long nImageHeight = imLoad.GetHeight();

Rect rectDest(0,0,400,400);

g.DrawImage(&imLoad,rectDest,0,0,nImageWidth,nImageHeight,UnitPixel,NULL,NULL,NULL);

Bitmap *pGrayImg = new Bitmap(nImageWidth, nImageHeight,PixelFormat8bppIndexed);

//生成調色版

ColorPalette* pal = (ColorPalette*)malloc(sizeof(ColorPalette) + 256*sizeof(ARGB));

pal->Count = 256;

pal->Flags = 0;

for(int m=0;m<256;m++)

{

pal->Entries[m] = Color::MakeARGB(255,m,m,m);

}

pGrayImg->SetPalette(pal);

BitmapData bmGrayData;

pGrayImg->LockBits(new Rect(0,0,nImageWidth,nImageHeight),ImageLockModeWrite,PixelFormat8bppIndexed,&bmGrayData);

BitmapData bmData;

Status iSucess = imLoad.LockBits(new Rect(0,0,nImageWidth,nImageHeight),ImageLockModeRead,PixelFormat24bppRGB,&bmData);

BYTE * p = (BYTE*)bmData.Scan0;

BYTE * q = (BYTE*)bmGrayData.Scan0;

BYTE * pt = p, *qt = q;

int i,j;

BYTE val;

for (j = 0; j < nImageHeight; j++)

{

// Stride是指圖像每一行需要佔用的字節數。根據BMP格式的標準,Stride一定要是4的倍數。

pt = p + j * bmData.Stride;

qt = q + j * bmGrayData.Stride;

for (i = 0; i < nImageWidth; i++)

{

val = (*pt)*0.114 + (*(pt+1))*0.587 + (*(pt+2))*0.299;

if(val>255)

{

val = 255;

}

if(val<0)

{

val = 0;

}

*qt = val;

pt += 3;

qt += 1;

}

}

imLoad.UnlockBits(&bmData);

pGrayImg->UnlockBits(&bmGrayData);

CLSID cidImage;

GetEncoderClsid(L"image/tiff", &cidImage);

//保存圖像

pGrayImg->Save(L"C://Gray.tif",&cidImage,NULL);

if(pal!=NULL)

{

free(pal);

pal = NULL;

}

 

//顯示圖像

rectDest.X = 401;

rectDest.Y = 0;

g.DrawImage(pGrayImg,rectDest,0,0,nImageWidth,nImageHeight,UnitPixel,NULL,NULL,NULL);

if(pGrayImg!=NULL)

{

delete []pGrayImg;

pGrayImg = NULL;

}

}

 

10)透明,半透明和不透明

在WinForm/WPF裏面我們經常會看到一些關於透明的屬性,比如Backcolor裏面可以選擇Transparant, Form裏面有一個叫Opacity的屬性。都是和透明以及透明度相關的。在其實是在GDI+應用層上的一些東西,在這裏我就不講了。主要從更基本的地方講起,其中還包括兩塊完全不同的內容。在LockBits的時候把PixelFormat設定成爲Format24bppRgb。但是如果你仔細研究,會發現其實裏面有各種各樣的圖片格式,其中有一種叫做Format32bppArgb。這個意思是說除了RGB,在圖像中還存在一個通道,叫做A。這個A就是用來描述當前像素是透明,半透明,還是全透明的分量。這個通道是2個叫Catmull和Smith在上世紀70年代初發明的。通過這個分量,我們可以進行alpha混合的一些計算。從而使表面的圖像和背景圖像混合,從而造成透明半透明的效果。在這種格式下A作爲一個byte,取值可以從0到255,那麼0表示圖像完全透明,則完全不可見,255則表示圖像完全不透明。每個像素都可以實現這種透明或者半透明的效果。更詳細解釋可以參考http://en.wikipedia.org/wiki/Alpha_compositing,或者去買本數字圖像處理的書回來看。讓我們來看看下面這段代碼,這個函數可以把圖像變成半透明的。

public unsafe Bitmap GenerateBitmap(byte alpha)

{

FileStream fs = new FileStream(image, FileMode.Open, FileAccess.Read);

Image img = Image.FromStream(fs, false, false);

Bitmap bmp = new Bitmap(img);

img.Dispose();

fs.Close();

int width = bmp.Width;

int height = bmp.Height;

BitmapData bmData = bmp.LockBits(

new Rectangle(0, 0, width, height),

ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);

byte* p = (byte*)bmData.Scan0;

int offset = bmData.Stride - width * 4;

for (int j = 0; j < height; j++)

{

for (int i = 0; i < width; i++)

{

p[3] = alpha;

p += 4;

}

p += offset;

}

bmp.UnlockBits(bmData);

 

return bmp;

}

 

大家可以注意一下第17,22和23行,由於圖像格式不對了,所以我們計算offset和遞加的操作都該了,此外由於用小數端存儲方式,Alpha通道在p[3]的位置。

順便提一句,還有一種格式叫做Format32bppPArgb,這叫做premultiplied alpha,就是說在RGB分量裏面,alpha分量的數據已經被預先乘進去了。比如說,一個半透明的紅色點,在ARGB下,矢量是(255,0,0,128),而在PARGB下就變成了(128,0,0,128)。這是爲了不要每次都做乘法。

還有要注意的是,如果你想把這個Bitmap保存成爲一個文件,那麼必須用png格式,才能夠保存alpha通道的信息。如果你存爲JPG/BMP/GIF,那麼alpha通道的信息將會被丟失。如果存爲BMP,那麼文件格式將變成Format32bppRgb,其中1個字節不再使用;如果保存爲JPEG,那麼是Format24bppRgb;存爲GIF,格式將變成Format8bppIndexed。根據標準,BMP/JPG本來就不支持透明通道,所以沒有可能保留透明信息。GIF倒是支持透明,但是GIF中顏色的信息都是索引,所以Alpha的解釋對GIF完全沒有效果,接下去我們來分析怎麼樣使用GIF的透明。

 

11) 一個不錯的 GDI+ FAQ site : http://www.bobpowell.net/faqmain.htm

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章