本問介紹了GDI+支持的大多數圖像文件格式,以及GDI+提供的處理圖像的文件的兩個類:Image和Bitmap 我們知道,在以往的圖像處理中,常常要根據不同圖像文件的格式及其數據存儲結構在不同格式中進行轉換。某個圖像文件的顯示也是依靠對文件數據結構的剖析,然後讀取相關圖像數據而實現的。現在,GDI+提供了Image和Bitmap類使我們能輕松容易地處理圖像。
概述
GDI+支持大多數流行的圖像文件格式,如BMP、GIF、JPEG、TIFF和PNG等。下面先來介紹這些圖像文件,然後再說明Image和Bitmap類支持的特性。
1.圖像文件格式簡介
圖像文件是描繪一幅圖像的計算機磁盤文件,其文件格式不下數十種。這裡僅介紹BMP、GIF、JPEG、TIFF和PNG等圖像文件格式。
BMP文件格式
BMP圖像文件格式是Microsoft為其Windows環境設置的標准圖像格式。一個Windows的BMP位圖實際上是一些和顯示像素相對應的位陣列,它有兩種類型:一種稱之為GDI位圖,另一種是DIB位圖(Device-Independent Bitmap)。GDI位圖包含了一種和Windows的GDI模塊有關的Windows數據結構,該數據結構是與設備有關的,故此位圖又稱為DDB位圖(Device-Dependent Bitmap)。當用戶的程序取得位圖數據信息時,其位圖顯示方式視顯示卡而定。由於GDI位圖的這種設備依賴性,當位圖通過網絡傳送到另一台PC,很可能就會出現問題。
DIB比GDI位圖有很多編程優勢,例如它自帶顏色信息,從而使調色板管理更加容易。且任何運行Windows的機器都可以處理DIB,並通常以後綴為.BMP的文件形式被保存在磁盤中或作為資源存在於程序的EXE或DLL文件中。
GIF文件格式
圖形交換格式(GIF--Graphics Interchange Format)最早由CompuServe公司於1987年6月15日制定的標准,主要用於CompuServe網絡圖形數據的在線傳輸、存儲。GIF提供了足夠的信息並很好地組織了這些信息,使得許多不同的輸入輸出設備能夠方便地交換圖像,它支持24位彩色,由一個最多256種顏色的調色板實現,圖像的大小最多是64K x 64K個像點。GIF的特點是LZW壓縮、多圖像和交錯屏幕繪圖。
JPEG文件格式
國際標准化組織(ISO)和國際電報電話咨詢委員會(CCITT)聯合成立的"聯合照片專家組"JPEG(Joint Photographic Experts Group)經過五年艱苦細致工作後,於1991年3月提出了ISO CD 10918號建議草案:"多灰度靜止圖像的數字壓縮編碼"(通常簡稱為JPEG標准)。這是一個適用於彩色和單色多灰度或連續色調靜止數字圖像的壓縮標准。它包括無損壓縮和基於離散余弦變換和Huffman編碼的有損壓縮兩個部分。前者不會產生失真,但壓縮比很小;後一種算法進行圖像壓縮時,信息雖有損失但壓縮比可以很大。例如壓縮20~40倍時,人眼基本上看不出失真。
JPEG圖像文件也是一種像素格式的文件格式,但它比BMP等圖像文件要復雜許多。所幸的是,GDI+的Image提供了對JPEG文件格式的支持,使得我們不需要對JPEG格式有太多的了解就能處理該格式的圖像。
TIFF文件格式
TIFF(Tagged Image Format File,標志圖像文件格式)最早由Aldus公司於1986年推出的,它能對從單色到24位真彩的任何圖像都有很好的支持,而且在不同的平台之間的修改和轉換是十分容易的。與其它圖像文件格式不同的是,TIFF文件中有一個標記信息區用來定義文件存儲的圖像數據類型、顏色和壓縮方法。
PNG文件格式
PNG(Portable Network Graphic,可移植的網絡圖像)文件格式是由Thomas Boutell、Tom Lane等人提出並設計的,它是為了適應網絡數據傳輸而設計的一種圖像文件格式,用於取代格式較為簡單、專利限制嚴格的GIF圖像文件格式。而且,這種圖像文件格式在某種程度上甚至還可以取代格式比較復雜的TIFF圖像文件格式。它的特點主要有:壓縮效率通常比GIF要高、提供Alpha通道控制圖像的透明度、支持Gamma校正機制用來調整圖像的亮度等。
需要說明的是,PNG文件格式支持三種主要的圖像類型:真彩色圖像、灰度級圖像以及顏色索引數據圖像。JPEG只支持前兩種圖像類型,而GIF雖然可以利用灰度調色板補償圖像的灰度級別,但原則上它僅僅支持第三種圖像類型。
mage和Bitmap類概述
GDI+的Image類封裝了對BMP、GIF、JPEG、PNG、TIFF、WMF(Windows元文件)和EMF(增強WMF)圖像文件的調入、格式轉換以及簡單處理的功能。而Bitmap是從Image類繼承的一個圖像類,它封裝了Windows位圖操作的常用功能。例如,Bitmap::SetPixel和Bitmap::GetPixel分別用來對位圖進行讀寫像素操作,從而可以為圖像的柔化和銳化處理提供一種可能。
3.DrawImage方法
DrawImage是GDI+的Graphics類顯示圖像的核心方法,它的重載函數有許多個。常用的一般重載函數有:
Status DrawImage( Image* image, INT x, INT y);
Status DrawImage( Image* image, const Rect& rect);
Status DrawImage( Image* image, const Point* destPoints, INT count);
Status DrawImage( Image* image, INT x, INT y, INT srcx, INT srcy,
INT srcwidth, INT srcheight, Unit srcUnit);
其中,(x,y)用來指定圖像image顯示的位置,這個位置和image圖像的左上角點相對應。rect用來指定被圖像填充的矩形區域, destPoints和count分別用來指定一個多邊形的頂點和頂點個數。若count為3時,則表示該多邊形是一個平行四邊形,另一個頂點由系統自動給出。此時,destPoints中的數據依次對應於源圖像的左上角、右上角和左下角的頂點坐標。srcx、srcy、srcwidth 和srcheight用來指定要顯示的源圖像的位置和大小,srcUnit用來指定所使用的單位,默認時使用PageUnitPixel,即用像素作為度量單位。
調用和顯示圖像文件
在GDI+中調用和顯示圖像文件是非常容易的,一般先通過Image或Bitmap調入一個圖像文件構造一個對象,然後調用Graphics::DrawImage方法在指定位置處顯示全部或部分圖像。例如下面的代碼:
void CEx_GDIPlusVIEw::OnDraw(CDC* pDC)
{
CEx_GDIPlusDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
using namespace Gdiplus;
Graphics graphics( pDC->m_hDC );
Image image(L"sunflower.jpg");
graphics.DrawImage(&image, 10,10);
Rect rect(130, 10, image.GetWidth(), image.GetHeight());
graphics.DrawImage(&image, rect);
}
結果如圖7.17所示,從圖中我們可以看出,兩次DrawImage的結果是不同的,按理應該相同,這是怎麼一回事?原來,DrawImage在不指定顯示區域大小時會自動根據設備分辨率進行縮放,從而造成顯示結果的不同。
當然,也可以使用Bitmap類來調入圖像文件來構造一個Bitmap對象,其結果也是一樣的。例如,上述代碼可改為:
Bitmap bmp(L"sunflower.jpg");
graphics.DrawImage(&bmp, 10,10);
Rect rect(130, 10, bmp.GetWidth(), bmp.GetHeight());
graphics.DrawImage(&bmp, rect);
需要說明的是,Image還提供GetThumbnailImage的方法用來獲得一個縮略圖的指針,調用DrawImage後可將該縮略圖顯示,這在圖像預覽時極其有用。例如下面的代碼:
Graphics graphics( pDC->m_hDC );
Image image(L"sunflower.jpg");
Image* pThumbnail = image.GetThumbnailImage(50, 50, NULL, NULL);
// 顯示縮略圖
graphics.DrawImage(pThumbnail, 20, 20);
// 使用後,不要忘記刪除該縮略圖指針
delete pThumbnail;
圖像旋轉和拉伸
圖像的旋轉和拉伸通常是通過在DrawImage中指定destPoints參數來實現,destPoints包含對新的坐標系定義的點的數據。圖7.18說明了坐標系定義的方法。
從圖中可以看出,destPoints中的第一個點是用來定義坐標原點的,第二點用來定義X軸的方法和圖像X方向的大小,第三個是用來定義Y軸的方法和圖像Y方向的大小。若destPoints定義的新坐標系中兩軸方向不垂直,就能達到圖像拉伸的效果。
下面的代碼就是圖像旋轉和拉伸的一個示例,其結果如圖7.19所示。
Image image(L"sunflower.jpg");
graphics.DrawImage(&image, 10,10);
Point points[] = { Point(0, 0), Point(image.GetWidth(), 0),
Point(0, image.GetHeight())};
Matrix matrix(1,0,0,1,230,10); // 定義一個單位矩陣,坐標原點在(230,10)
matrix.Rotate(30); // 順時針旋轉30度
matrix.Scale(0.63,0.6); // X 和 Y 方向分別乘以0.63和0.6比例因子
matrix.TransformPoints(points, 3); // 用該矩陣轉換points
graphics.DrawImage(&image, points, 3);
Point newpoints[] = {Point(450, 10), Point(510, 60), Point(350, 80)};
graphics.DrawImage(&image, newpoints, 3);
當然,對於圖像旋轉還可直接使用Graphics::RotateTransform來進行,例如下面的代碼。但這樣設置後,以後所有的繪圖結果均會旋轉,有時可能感覺不方便。
Image image(L"sunflower.jpg");
graphics.TranslateTransform(230,10); // 將原點移動到(230,10)
graphics.RotateTransform(30); // 順時針旋轉30度
graphics.DrawImage(&image, 0,0);
調整插補算法的質量
當圖像進行縮放時,需要對圖像像素進行插補,不同的插補算法其效果是不一樣的。Graphics:: SetInterpolationMode可以讓我們根據自己的需要使用不同質量效果的插補算法。當然,質量越高,其渲染時間越長。下面的代碼就是使用不同質量效果的插補算法模式,其結果如圖7.20所示。 Graphics graphics( pDC->m_hDC );
Image image(L"log.gif");
UINT width = image.GetWidth();
UINT height = image.GetHeight();
// 不進行縮放
graphics.DrawImage( &image,10,10);
// 使用低質量的插補算法
graphics.SetInterpolationMode(InterpolationModeNearestNeighbor);
graphics.DrawImage( &image,
Rect(170, 30, (INT)(0.6*width), (INT)(0.6*height)));
// 使用中等質量的插補算法
graphics.SetInterpolationMode(InterpolationModeHighQualityBilinear);
graphics.DrawImage( &image,
Rect(270, 30, (INT)(0.6*width), (INT)(0.6*height)));
// 使用高質量的插補算法
graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic);
graphics.DrawImage( &image,
Rect(370, 30, (INT)(0.6*width), (INT)(0.6*height)));
事實上,Image功能還不止這些,例如還有不同格式文件之間的轉換等。但這些功能和MFC的新類CImage功能基本一樣,但CImage更符合MFC程序員的編程習慣,因此下一節中我們來重點介紹CImage的使用方法和技巧。