1. 程式人生 > >Gdi+實用入門

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

   (GDI+ SDK參考.chm)

解壓後,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+

}


參考連結:點選開啟連結

其他連結:點選開啟連結             點選開啟連結