上文給新手學習GDI+講述了vs環境等的準備工做,而且能夠直接用GDI+繪圖了。本文開始,講述的可能偏理論,建議學習的過程當中大膽嘗試,多使用API。git
首先上官方文檔https://docs.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-gdi-startgithub
官方文檔是最權威與第一手(固然有時候有錯誤)的,其餘人的說法通過本身的加工,增長了解釋,也會帶來錯誤的風險。英文能力強,強烈建議經過官網算法
學習與嘗試。編程
GDI+的新特性。windows
一、圖像(Graphics)對象 與畫圖工具(如Pen、Brush、GraphicsPath、Font、Image)分離,這與GDI中須要將畫圖工具導入DC中徹底不一樣。所以GDI也dom
稱爲狀態模型編程,而GDI+則稱爲非狀態模型編程。鬆耦合老是易於方便拓展。在咱們這個例子中就是繪圖更自由了,調整畫筆時,再也不須要頻繁取出dc與存入dc了。函數
二、多函數重載。以DrawLine爲例,能夠傳入Point,也能夠傳入int,方便不一樣場景使用不一樣的API。而GDI則比較固定。工具
三、當前位置,GDI講究當前的繪圖點,模擬一我的畫畫的全過程。如畫一條線的話,須要調用MoveToEx(該函數甚至返回以前的點),再調用LineTo學習
而GDI+則無當前位置的概念,講究的是繪製過程。DrawLine傳入的起始點與結束點即可以劃線。
四、繪製與填充,GDI中Rectangle(矩形)直接使用dc的畫筆畫邊界(border),用dc的畫刷填充。而GDI+則分紅了兩個部分DrawRectangle與FillRectangle。
五、區域的操做,GDI提供的區域函數比較簡單 CreateRoundRectRgn、CreatePolygonRgn、CreateEllipseRgn等。GDI+不提供相似函數,經過Region維護,並提供了
Intersect,Union,Xor,Exclude,Complement,Translate等功能函數來構建複雜地區域。
GDI+的使用:
咱們通常在main函數入口附近進行初始化GdiplusStartup ,並在程序結束前進行資源回收GdiplusShutdown。若是不進行初始化,任何使用GDI+編譯不會報錯,甚至也能運行,
但如你所見,UI全是空的,由於GDI+對象沒法正常工做。
GDI+的基本操做:
一、重要的Graphics對象。
Graphics是GDI+的核心,他的構造函數的參數解釋下。
(繪圖本質是利用繪圖工具在繪圖平面上作畫,如下我把繪圖平面稱爲【畫布】)
hdevice:設備句柄,此時畫布如打印機
hwnd:窗口句柄,此時畫布是窗口
image:圖像對象,此時畫布是圖片(沒錯,圖片也能夠做畫,畫完後保存的話圖片變了)
hdc:設備上下文句柄,此時畫布要根據上下文才能知道
icm:是否使用色彩配置文件校訂色彩。
總之,Graphics能夠在應用程序窗口、圖片、打印機、繪圖儀、傳真機等等做畫!
動手試一試吧:
1)寫一個在打印機做畫的函數 注意須要 #include <commdlg.h>
void OnPrintOut() { //要打印的文檔信息 DOCINFO docInfo; ZeroMemory(&docInfo, sizeof(docInfo)); docInfo.cbSize = sizeof(docInfo); docInfo.lpszDocName = _T("TestPrint"); PRINTDLG printDlg; ZeroMemory(&printDlg, sizeof(printDlg)); printDlg.lStructSize = sizeof(printDlg); printDlg.Flags = PD_RETURNDC; if (PrintDlg(&printDlg)) { StartDoc(printDlg.hDC, &docInfo); StartPage(printDlg.hDC); //開始在打印機上做畫 Graphics graphics(printDlg.hDC); //調試的話放到cpp目錄,不然放到exe目錄 Image image(_T("test.png")); graphics.DrawImage(&image, 0, 0); Pen blue(Color(255, 0, 0, 255)); graphics.DrawRectangle(&blue, 200, 500, 200, 150); graphics.DrawEllipse(&blue, 200, 500, 200, 150); EndPage(printDlg.hDC); EndDoc(printDlg.hDC); } if (printDlg.hDevMode) { GlobalFree(printDlg.hDevMode); } if (printDlg.hDevNames) { GlobalFree(printDlg.hDevNames); } if (printDlg.hDC) { DeleteDC(printDlg.hDC); } }
2)在菜單功能,把「關於」菜單項的功能註釋掉,改爲咱們的功能
3)編譯運行看看,若是有打印機,看看打印出來的是不是你畫的呢?
二、畫基本圖形
GDI+的默認的座標系的原點位於畫布的左上角,x軸向右,y軸向下。默認的單位是像素。(讓我想起了高DPI的恐慌,目前網易雲信也是不支持動態變化的,但重啓會生效。還得抽時間攻克下。)注意:不一樣的畫布的像素的大小不必定同樣,更改了分辨率也會影響像素。好比從480*720 變到920*1440,明顯程序變小了。
1)畫直線
一條直線先前已經玩過了,這裏補充下一次畫多根線。
void GDIPlusDrawLines(HDC hdc) { Graphics graphics(hdc); Pen green(Color(255, 0, 255, 0), 3); PointF p1(10, 10); PointF p2(10, 100); PointF p3(50, 50); PointF p4(10, 10); PointF point[] = { p1, p2, p3, p4 }; graphics.DrawLines(&green, point, sizeof(point) / sizeof(point[0])); }
二、畫矩形,以前也玩過了,GDI+支持一次性畫多個矩形,試試吧。
void GDIPlusDrawRectangles(HDC hdc) { Graphics graphics(hdc); Pen blue(Color(255, 0, 255, 0), 3); RectF r1(10,10,100,50); RectF r2(40,40,100,50); RectF r3(80,40,50,100); RectF rs[] = { r1, r2, r3}; graphics.DrawRectangles(&blue, rs, sizeof(rs) / sizeof(rs[0])); }
三、畫曲線
DrawCurve、DrawClosedCurve(閉合曲線)、DrawBezier(貝塞爾曲線)
額,發現每一個函數都寫demo比較費時且無聊,你們又不必定跟着嘗試,增長點樂趣吧,點的位置隨機,矩陣的位置隨機。
1) 包含下頭文件 <time.h> 和<stdlib.h>,using namespace std;
2) 初始化指定下隨機數種子
//初始化種子
srand((unsigned)time(NULL));
3)寫隨機函數
Point GetRandomPoint(int xmax, int ymax) { Point t; t.X = rand() % xmax; t.Y = rand() % ymax; return t; } Rect GetRandomRect(int xmax, int ymax) { Rect t; Point t1 = GetRandomPoint(xmax, ymax); Point t2 = GetRandomPoint(xmax, ymax); if (t1.X < t2.X) { t.X = t1.X; t.Width = t2.X - t1.X; if (t1.Y<t2.Y) { t.Y = t1.Y; t.Height = t2.Y - t1.Y; } else { t.Y = t2.Y; t.Height = t1.Y - t2.Y; } } else { t.X = t2.X; t.Width = t1.X - t2.X; if (t1.Y < t2.Y) { t.Y = t1.Y; t.Height = t2.Y - t1.Y; } else { t.Y = t2.Y; t.Height = t1.Y - t2.Y; } } return t; }
4)描點函數
//描點 void DrawEllipsePoint(HDC hdc, Point t) { Graphics graphics(hdc); SolidBrush redbursh(Color::Red); graphics.FillEllipse(&redbursh, t.X-5, t.Y-5, 10, 10); }
5)開始做畫
void DrawCurves(HDC hdc, int xmax, int ymax) { Graphics graphics(hdc); Point t[] = { GetRandomPoint(xmax, ymax), GetRandomPoint(xmax, ymax), GetRandomPoint(xmax, ymax), GetRandomPoint(xmax, ymax) }; int t_size = sizeof(t) / sizeof(t[0]); //畫曲線 Pen green(Color::Green, 3); graphics.DrawCurve(&green, t, t_size); //增長彎曲程度 Pen blue(Color::Blue, 3); graphics.DrawCurve(&blue, t, t_size, 1.3f); //畫閉合曲線 Pen gray(Color::Gray, 3); graphics.DrawClosedCurve(&gray, t, t_size); //畫貝塞爾曲線 Pen orange(Color::Orange, 3); graphics.DrawBezier(&orange, t[0],t[1],t[2],t[3]); for (int i = 0; i < t_size;++i) { DrawEllipsePoint(hdc, t[i]); } }
四、畫圓弧與扇形
DrawArc 、DrawPie
void DrawArcPie(HDC hdc,int xmax, int ymax)
{
Graphics graphics(hdc);
Rect t = GetRandomRect(xmax, ymax);
//先畫矩形邊框(增長對左邊的認知)
Pen black(Color::Black); //默認一像素
graphics.DrawRectangle(&black, t);
//畫弧線
Pen red(Color::Red, 3);
graphics.DrawArc(&red, t, 0/*起始位置*/, 90/*須要畫的弧度大小*/);
//畫扇形
Pen green(Color::Green, 1);
graphics.DrawPie(&green, t, 90/*起始位置*/, 90/*須要畫的弧度大小*/);
}
五、填充區域、畫刷與顏色
FillClosedCurve(填充封閉曲線)、FillEllipse(填充橢圓)、FillPath(填充路徑)
FillPie(填充扇形)、FillPolygon(填充多邊形)、FillRectangle(填充矩形)
FillRectangles(填充巨型集)、FillRegion(填充區域)。
GDI+使用畫刷來填充的:單色畫刷、影線畫刷、紋理畫刷、線性漸變畫刷與路徑漸變畫刷(畫刷之後詳細展開)
咱們以前已經接觸過顏色了,Color,由argb組成。a是alpha色彩的透明度、r是red紅色、g是green綠色、b是blue藍色。GDI+對透明度的支持是基於如下算法:output = foreground*alpha/255 + background*(255-alpha)/255。(之後詳細展開)
下面,咱們來填充一個正弦圖形試試
//用半透明藍色填充 填充sinx 與 x軸的區域 void FillSinRegion(HDC hdc, int xmax, int ymax) { const REAL Pi = 3.1415926; //從0 到 2pi //計算1弧度=多少像素,從50px ->xmax-50px 畫 REAL perX = (xmax - 100)*1.0 / (2 * Pi); //衆項長度單位1=多少像素 REAL perY = (ymax - 100)*1.0/2; //畫點,理論上越多越精確,咱們取500+1點。你們能夠試試更多 const int counts = 500; Point t[counts*2]; t[0].X = 50; t[0].Y = ymax / 2; //步長 REAL stepX = (2 * Pi) / counts; REAL step = stepX*perX ; //計算正弦值 for (int i = 1; i < counts ; i++) { t[i].X = t[i - 1].X + step; REAL v = sin(i*stepX); t[i].Y = ymax / 2 - v*perY; } //畫x軸 t[counts].X = t[counts - 1].X; t[counts].Y = ymax / 2; for (int i = 1; i < counts; i++) { t[counts + i].X = t[counts + i - 1].X - step; t[counts + i].Y = ymax / 2; } Graphics graphics(hdc); SolidBrush blue(Color(255 / 2, 0, 0, 255)); Pen r(Color::Red); graphics.DrawPolygon(&r, t, counts*2); graphics.FillClosedCurve(&blue,t,counts*2); }
六、輸出問題
DrawString
void PrintText(HDC hdc, int xmax, int ymax) { TCHAR s[32]; _tcscpy_s(s, _T("Hello GDI+")); RectF t(10,10,200,50); Font f(_T("Arial"), 26); StringFormat Fmt; Fmt.SetAlignment(StringAlignmentCenter); Fmt.SetLineAlignment(StringAlignmentCenter); SolidBrush red(Color::Red); Graphics graphics(hdc); graphics.DrawString(s, _tcslen(s), &f, t, &Fmt, &red); Pen black(Color::Black); graphics.DrawRectangle(&black, t); }