Gdi+實用入門
大部分是參照其它資料,然後加以自己的理解,那是什麼,總結。算不得什麼教程。。。。。。。汗,自己看著就行了。。如果別人能看那就更好了。
首先下載GDI+檔案包,一個動態連結庫,使用GDI+就是呼叫那個動態連結庫裡的函式。類似畫圖什麼的,瞭解這個主要是想把bmp圖片轉換成jpg的,然後做個簡單螢幕監控,幾個月前嘗試做了一下,差不多是半分鐘才傳過來一張圖片。知識有限,沒辦法,那時候,也沒怎麼上心,就落下了。廢話就不多說了,先下載GDI+檔案包。
GDI+檔案包下載地址:www.codeguru.com/code/legacy/gdi/GDIPlus.zip
另附一個GDI+教程:http://ishare.iask.sina.com.cn/f/22577823.html
解壓後,Includes裡的檔案就複製到VC98\include資料夾裡,lib裡的檔案也一樣,複製到對應的lib資料夾裡,那個gdiplus就複製到工程資料夾裡,就跟使用平常的動態連結庫一樣。
先在控制檯下來測試一下,新建一個控制元件臺工程,程式碼如下:
#include<windows.h>
#define ULONG_PTR ULONG
#include<gdiplus.h>//gdi+標頭檔案
using namespace Gdiplus;
#pragma comment(lib,"gdiplus.lib")
int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR pGdiToken;
GdiplusStartup(&pGdiToken,&gdiplusStartupInput,NULL);//初始化GDI+
HWND hWnd=::FindWindow(NULL,"無標題.txt - 記事本");
HDC hDC=::GetDC(hWnd);
Graphics graphics(hDC);
Pen newPen(Color(255,0,0),3);//畫筆,最後一個引數,畫筆大小
while(true)
{
graphics.DrawRectangle(&newPen,50,50,100,60);//畫一個矩形
Sleep(350);
}
//死迴圈,下面這句不會呼叫,只是想把那個意思表明
GdiplusShutdown(pGdiToken);//關閉GDI+
}
Color解釋
上面的例子中畫筆的顏色由Color(255,0,0)返回的值來確定,這個也就是顏色值,跟GDI中的RGB一樣,不過前者可以有四個引數,多出的一個引數用來表示什麼呢?Alpha值,也就是透明度。0~255,0是完全透明。255是不透明,如果Color有四個引數的話,那個Alpha值就由第一個引數指定。看下面例子。
#include<windows.h>
#define ULONG_PTR ULONG
#include<gdiplus.h>
using namespace Gdiplus;
#pragma comment(lib,"gdiplus.lib")
int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR pGdiToken;
GdiplusStartup(&pGdiToken,&gdiplusStartupInput,NULL);//初始化GDI+
HWND hWnd=::FindWindow(NULL,"無標題.txt - 記事本");
HDC hDC=::GetDC(hWnd);
Graphics graphics(hDC);
SolidBrush newBrush(Color(40,0,0,255));
while(true)
{
graphics.FillRectangle(&newBrush,0,0,120,120);//重複畫
Sleep(2000);
}
//死迴圈,下面這句不會呼叫,只是想把那個意思表明。
GdiplusShutdown(pGdiToken);//關閉GDI+
return 0;
}
漸變畫刷
這裡我就複製原文了,事實上對於幾個引數,我只有一個固定的瞭解。
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
由於在前面的示例中,對這種簡單的畫刷的使用已介紹過,因而這裡著重討論漸變畫刷
的建立和使用。
GDI+提供了 LinearGradientBrush和 PathGradientBrush 類分別用來建立一個直線漸變和
路徑漸變畫刷。
直線漸變是指在一個矩形區域使用兩種顏色進行過渡(漸變),過渡方向可以是水平、垂
直以及對角線方向。LinearGradientBrush 建構函式的原型如下:
LinearGradientBrush(Point & point1, Point & point2,
Color & color1, Color & color2);
LinearGradientBrush(Rect & rect, Color & color1, Color & color2,
REAL angle, BOOL isAngleScalable);
LinearGradientBrush(Rect & rect, Color & color1, Color & color2,
LinearGradientMode mode);
其中, point1和point2分別用來指定矩形區域的左上角和右下角點座標, color1和color2
分別用來指定漸變起始和終止的顏色。rect 用來指定一個矩形區域的大小和位置,angle 用
來指定漸變的方向角度,正值為順時針。isAngleScalable 是一個即將廢除的引數。mode
用來指定漸變的方法,它可以是 LinearGradientModeHorizontal(水平方向)、
LinearGradientModeVertical (垂直方向)、LinearGradientModeForwardDiagonal(從左下到
右上的對角線方向)和 LinearGradientModeBackwardDiagonal(從左上到右下的對角線方
向)。
需要說明的是,Point 和 Rect是GDI+新的資料型別,它們和 MFC的 CPoint 和 CRect
類的功能基本一樣,但它們相互之間不能混用。
路徑漸變畫刷是用漸變顏色來填充一個封閉的路徑。 一個路徑既可以由一系列的直線和
曲線構成,也可以由其它物件來構造。路徑漸變是一種中心顏色漸變模式,它從路徑的中心
點向四周進行顏色漸變。PathGradientBrush 建構函式的原型如下:
PathGradientBrush(const GraphicsPath* path);
PathGradientBrush(const Point * points, INT count, WrapMode wrapMode);
其中,path用來指定一個路徑指標,points 和 count 分別用來指定組成路徑的一系列
直線端點的陣列及其大小,wrapMode 是一個可選項,用來指定填充的包圍模式。一個包圍
模式用來決定是否在區域內部、在區域外部以及所有區域都填充。預設時,其值為
WrapModeClamp,即在區域內部填充。
下面的程式碼說明了上述兩種漸變畫刷的使用方法:
Graphics graphics( pDC->m_hDC );
GraphicsPath path; // 構造一個路徑
path.AddEllipse(50, 50, 200, 100);
// 使用路徑構造一個畫刷
PathGradientBrush pthGrBrush(&path);
// 將路徑中心顏色設為藍色
pthGrBrush.SetCenterColor(Color(255, 0, 0, 255));
// 設定路徑周圍的顏色為藍芭,但alpha 值為0
Color colors[] = {Color(0, 0, 0, 255)};
INT count = 1;
pthGrBrush.SetSurroundColors(colors, &count);
graphics.FillRectangle(&pthGrBrush, 50, 50, 200, 100);
LinearGradientBrush linGrBrush(
Point(300, 50),
Point(500, 150),
Color(255, 255, 0, 0), // 紅色
Color(255, 0, 0, 255)); // 藍色
graphics.FillRectangle(&linGrBrush, 300, 50, 200, 100);
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
反鋸齒
不管是畫圓,還是直線,線條給人的感覺很粗糙,不怎麼平滑,怎麼解決呢,對了,就像標題說的那樣,反鋸齒,其實就是函式的呼叫。
Graphics類的SetSmoothingMode函式可以設定,根據函式字面上的意思,就是設定平滑模式。這裡我舉一個例子,關於這個函式的可選引數,自己可以到網上找找。
例子:(對比兩條線有什麼區別)單文件程式 OnDraw函式程式碼如下:
void CShowView::OnDraw(CDC* pDC)
{
Graphics graphics( pDC->m_hDC );
Pen myPen(Color(255,0,0,0),1);
graphics.SetSmoothingMode(SmoothingModeHighSpeed);//要速度不要質量
graphics.DrawLine(&myPen,0,0,50,200);
//還有一個引數SmoothingModeAntiAlias,估計是折中的意思。速度也要,質量也不能落下。
graphics.SetSmoothingMode(SmoothingModeHighQuality);//高質量
graphics.DrawLine(&myPen, 50, 0 ,130,200);
// TODO: add draw code for native data here
}
效果如下圖:
文字也可以反鋸齒Graphics類裡的SetTextRenderingHint函式可以做到,具體用法,參考百度吧。。。
顯示圖片(直接從教程上覆制的)
用Image類可以做到,可識別的圖片有gif,bmp,jpg,png等。先說一下幾種載入圖片的方式,通過建構函式。如:
Image img(L"d:\\test.jpg");//定義時載入。
還有一種是通過FromFile函式。如:
Image *pImg=Image::FromFile(L"d:\\abc.bmp");
Graphics類裡的DrawImage可以繪製圖片,兩個常用DrawImage過載函式:
Status DrawImage( Image* image, INT x, INT y);
Status DrawImage( Image* image, const Rect& rect);
DrawImage有幾個過載函式,這裡不列出它們的區別了,有興趣可以自己去看一下,或者其它的。如旋轉圖片。。。。。
格式轉換
獲取圖片的CLSID採用了一個自定義函式GetEncoderClsid,這個函式是我從網上找的,具體怎麼實現的有興趣的朋友可以去了解,反正我只是把它複製過來直接使用了,沒看過程式碼。。
圖片格式轉換程式碼如下,假設D盤有一個名為abc的點陣圖,把它轉換為JPEG檔案。。
#include<windows.h>
#define ULONG_PTR ULONG
#include<gdiplus.h>
using namespace Gdiplus;
#pragma comment(lib,"gdiplus.lib")
//獲取圖片格式CLSID的自定義函式
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid);
int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR pGdiToken;
GdiplusStartup(&pGdiToken,&gdiplusStartupInput,NULL);//初始GDI+
Image image(L"d:\\abc.bmp");//載入圖片
CLSID encoderClsid;
GetEncoderClsid(L"image/jpeg",&encoderClsid);
image.Save(L"d:\\Jpegabc.jpg",&encoderClsid);
//如果不註釋掉下面的語句,就會出錯,之前也有過錯誤,程式碼沒任何錯誤,但執行的時候,不知道怎麼,記憶體不可讀取
//上次是載入圖片的時候,就會出現這個錯誤,這些未知錯誤,只能重建個工程,把程式碼再打一遍,看還有問題麼
//問題依舊,不過卻是出現在程式結束的時候,我想問題會不會出現在GdiplusShutdown這個函式上,這個函式釋放掉記憶體,可能系統並沒有
//發現,等程式結束的時候,不是會自動清理沒有釋放掉的記憶體麼,那麼就會起衝突,然後我把這這個函式的呼叫註釋掉,問題果然沒有了
//但也不知道我的猜想對不對,我想最大的問題可能出現在編譯器上。。。。
//GdiplusShutdown(pGdiToken);//關閉GDI+
}
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num= 0;
UINT size= 0;
ImageCodecInfo* pImageCodecInfo= NULL;
GetImageEncodersSize(&num, &size);
if(size== 0)
{
return -1;
}
pImageCodecInfo= (ImageCodecInfo*)(malloc(size));
if(pImageCodecInfo== NULL)
{
return -1;
}
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;
}
}
free(pImageCodecInfo);
return -1;
}
在記憶體中轉換圖片格式
//瞭解下面這個例子,就可以通過GDI+實現把bmp圖片轉換JPEG格式(在記憶體中),然後通過網路傳送到另一端,
//另一端接收再顯示,
//大概步驟是,先用Image載入圖片,然後建立流,通過Image類的Save函式以JPEG格式把圖片資料儲存到
//流中,之後讀取資料,再用Image類的FromStream從流中載入(還原)
#include<windows.h>
#define ULONG_PTR ULONG
#include<gdiplus.h>
using namespace Gdiplus;
#pragma comment(lib,"gdiplus.lib")
//獲取圖片格式CLSID的自定義函式
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid);
int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR pGdiToken;
GdiplusStartup(&pGdiToken,&gdiplusStartupInput,NULL);//初始GDI+
Image image(L"d:\\abc.bmp");//載入圖片
CLSID encoderClsid;
//獲取JPEG圖片格式CLSID
GetEncoderClsid(L"image/jpeg",&encoderClsid);
//建立流
IStream *pStream;
CreateStreamOnHGlobal(NULL,TRUE,&pStream);
//以JPEG圖片格式儲存資料到流中
image.Save(pStream,&encoderClsid);
//獲得與流對應的記憶體控制代碼
HGLOBAL hMem;
GetHGlobalFromStream(pStream,&hMem);
//獲得記憶體塊大小
DWORD dwSize=GlobalSize(hMem);
//再建立一塊記憶體控制代碼,用於目標流
HGLOBAL hDesMem=GlobalAlloc(GMEM_MOVEABLE,dwSize);
IStream *pDesStream;
CreateStreamOnHGlobal(hDesMem,TRUE,&pDesStream);
//獲得記憶體塊首地址
BYTE *pImgData=(BYTE *)GlobalLock(hMem);
BYTE *pDesData=(BYTE *)GlobalLock(hDesMem);
//複製記憶體,如果通過網路,就把pImgData裡的資料傳送過去。
CopyMemory(pDesData,pImgData,dwSize);
::GlobalUnlock(hMem);
GlobalUnlock(hDesMem);
Image *pImg=Image::FromStream(pDesStream);
HWND hWnd=FindWindow(NULL,"無標題.txt - 記事本");
HDC hDC=GetDC(hWnd);
Graphics graphics(hDC);
while(TRUE)
{
//繪製圖片,測試是否正確
graphics.DrawImage(pImg,0,0,300,300);
Sleep(500);
}
//GdiplusShutdown(pGdiToken);//關閉GDI+
}
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num= 0;
UINT size= 0;
ImageCodecInfo* pImageCodecInfo= NULL;
GetImageEncodersSize(&num, &size);
if(size== 0)
{
return -1;
}
pImageCodecInfo= (ImageCodecInfo*)(malloc(size));
if(pImageCodecInfo== NULL)
{
return -1;
}
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;
}
}
free(pImageCodecInfo);
return -1;
}
//通過HBITMAP載入圖片,Bitmap類可以做到,而Bitmap類是從Image派生出來的,那麼Image類裡的函式,它都可以使用。
//為什麼提到從HBITMAP載入函式呢,看VC API常用函式第八十八和第八十九個函式,螢幕截圖。那裡面是以HBITMAP形式
//儲存螢幕圖片的。而我前面說過了,打算做一個簡單的螢幕監控。雖然不知道最終效果怎麼樣,但我想,應該比上次要好,
//希望是我想象的那樣。直接看程式碼了,(我相信有了上面的基礎)有什麼比直接看程式碼讓人明白的呢,況且只有幾句。。
#include<windows.h>
#define ULONG_PTR ULONG
#include<gdiplus.h>
using namespace Gdiplus;
#pragma comment(lib,"gdiplus.lib")
int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR pGdiToken;
GdiplusStartup(&pGdiToken,&gdiplusStartupInput,NULL);//初始GDI+
//載入圖片
HBITMAP hBmp=(HBITMAP)LoadImage(NULL,"d:\\abc.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
Bitmap bmp(hBmp,NULL);//還可通過FromHBITMAP函式
HWND hWnd=FindWindow(NULL,"無標題.txt - 記事本");
HDC hDC=GetDC(hWnd);
Graphics graphics(hDC);
while(TRUE)
{
graphics.DrawImage(&bmp,10,10);
Sleep(500);
}
//GdiplusShutdown(pGdiToken);//關閉GDI+
}
參考連結:點選開啟連結