Windows 2D 绘图 (GDI, GDI+, Direct2D)
Windows 2D 繪圖
- GDI
- GDI 函數
- GDI+
- GDI 和 GDI+ 的區別
- GDI+ 新特性
- Direct2D
- 視覺效果
- demo
GDI
GDI 是 Graphics Device Interface 的縮寫,含義是圖形設備接口,它的主要任務是負責系統與繪圖程序之間的信息交換,處理所有 Windows 程序的圖形輸出。
在 Windows 操作系統下,絕大多數具備圖形界面的應用程序都離不開 GDI,我們利用 GDI 所提供的眾多 API 就可以方便的在屏幕、打印機及其它輸出設備上輸出圖形、文本等操作。
GDI 具有如下特點:
- 不允許程序直接訪問物理顯示硬件,通過稱為“設備環境”的抽象接口間接訪問顯示硬件
- 程序需要與顯示硬件(顯示器、打印機等) 進行通訊時,必須首先獲得與特定窗口相關聯的設備環境
- 用戶無需關心具體的物理設備類型
- Windows 參考設備環境的數據結構完成數據的輸出
GDI 函數
GDI 函數大致可分類為:
- 設備上下文函數(如 GetDC、CreateDC、DeleteDC)
- 畫線函數(如 LineTo、Polyline、Arc)
- 填充畫圖函數(如 Ellipse、FillRect、Pie)
- 畫圖屬性函數(如 SetBkColor、SetBkMode、SetTextColor)
- 文本、字體函數(如 TextOut、GetFontData)
- 位圖函數(如 SetPixel、BitBlt、StretchBlt)
- 坐標函數(如 DPtoLP、LPtoDP、ScreenToClient、ClientToScreen)
- 映射函數(如 SetMapMode、SetWindowExtEx、SetViewportExtEx)
- 元文件函數(如 PlayMetaFile、SetWinMetaFileBits)
- 區域函數(如 FillRgn、FrameRgn、InvertRgn)
- 路徑函數(如 BeginPath、EndPath、StrokeAndFillPath)
- 裁剪函數(如 SelectClipRgn、SelectClipPath)
GDI+
GDI+ 是 GDI 的后續版本,最早于 2000 年隨 Windows 2000 一起推出,后來又被包裝進 .NET 框架的托管類庫中,成為 .NET 中窗體繪圖的主要工具。
GDI+ 主要提供了以下三類服務:
- 二維矢量圖形:GDI+ 提供了存儲圖形基元自身信息的類(或結構體)、存儲圖形基元繪制方式信息的類以及實際進行繪制的類;
- 圖像處理:大多數圖片都難以劃定為直線和曲線的集合,無法使用二維矢量圖形方式進行處理。因此,GDI+ 為我們提供了 Bitmap、Image 等類。它們可用于顯示、操作和保存 BMP、JPG、GIF 等圖像。
- 文字顯示:GDI+ 支持使用各種字體、字號和樣式來顯示文本。
GDI 接口是基于函數的,而 GDI+ 是基于 C++ OO 的編程接口,因此使用起來比 GDI 要方便。因為 GDI+ 實際上是 GDI 的封裝和擴展,所以執行效率一般要低于 GDI。
GDI 和 GDI+ 的區別
GDI 的核心是設備上下文,GDI 函數都依賴于設備上下文句柄,其編程方式是基于句柄的;GDI+ 無需時刻依賴于句柄或設備上下文,用戶只需創建一個 Graphics 對象,就可以用面向對象的方式調用其成員函數進行圖形操作,編程方式是基于對象的。
GDI 在使用設備上下文繪制線條之前,必須先調用 SelectObject 以使鋼筆對象和設備上下文關聯。其后,在設備上下文中繪制的所有線條均使用該鋼筆,直到選擇另一支不同的鋼筆為止。GDI 中有當前位置的概念,所以在使用 GDI 繪制線條前應該先使用MoveTo 移動當前位置,再使用 LineTo 畫線。
CPen pen(PS_SOLID, 1, RGB(255, 0, 0));dc.SelectObject(pen.GetSafeHandle());dc.MoveTo(0, 0);dc.LineTo(100, 100);在 GDI+ 中,只需將 Pen 對象直接作為參數傳遞給 Graphics 類的 DrawLine 等方法即可,而不必使 Pen 對象與 Graphics 對象關聯。且 GDI+ 中則沒有當前位置的概念,畫線函數中可以直接指定起點和終點。
Pen myPen(Color::Red);graphics.DrawLine(&myPen, 0, 0, rect.right, 0);GDI+ 新特性
- 改進了顏色管理
GDI+ 不僅提供了更多可供選擇使用的顏色,使其支持 Alpha 通道合成運算,而且還保持了與其他顏色的兼容性。 - 繪圖支持反鋸齒
通過設置 GDI+ 對象的相關屬性,GDI+ 可以與相關的顯示驅動程序搭配完成圖形繪制時的反鋸齒功能,使得繪制的圖形更加平滑、美觀,而整個過程是由 GDI+ 對象自動計算完成的。 - 提供漸變畫刷
GDI+ 拓展了 GDI 的功能,提供線性漸變和路徑漸變畫刷來填充圖形、路徑和區域,甚至也可用來繪制直線、曲線等。 - 獨立的路徑對象
GDI+ 使用 Graphics 對象來進行繪圖操作,并將路徑操作從 Graphics 對象分離出來,提供一個 Graphics 類供用戶使用,用戶不必擔心對象會受到 Graphics 對象操作的影響,從而可以使用同一個操作對象進行多次的路徑繪制操作。 - 樣條曲線
GDI+ 封裝了繪制基數樣條曲線和貝塞爾樣條曲線的方法。 - 變形和矩陣運算
GDI+ 提供了功能強大的 Matrix 類來實現矩陣的旋轉,錯切、平移、比例等變換操作,以便產生復雜的新圖形。 - 多圖片格式的支持
GDI+ 改進了圖形處理能力,通過 GDI+,用戶能夠訪問多種格式的圖片文件,轉換文件格式等,還能進行圖像重新著色、色彩修正、消除走樣等圖像處理。
Direct2D
Direct2D 是一個基于 Direct3D 的 2D 圖形 API,可以利用硬件加速特性來提供高性能、高質量的 2D 渲染。而且十分方便的是,Direct2D 與 GDI,GDI+ 和 D3D 都是可以交互的。一項技術總是有其受眾面,看看微軟怎么說的:
- 大型企業級本機應用程序開發人員。
- 創建供下游開發人員使用的控件工具包和庫的開發人員。
- 需要對二維圖形進行服務器端呈現的開發人員。
- 使用 Direct3D 圖形,并且需要在菜單、用戶界面 (UI) 元素和抬頭顯示器 (HUD) 中使用高性能的簡單二維和文本呈現的開發人員。
在 Direct2D 架構的右下方,有一個軟件光柵化(software rasterizer),假如顯卡不支持硬件加速,那么 Direct2D 可以使用軟件方式渲染,即便是這樣,其效果還是要優于 GDI。
視覺效果
使用 Direct2D 渲染出來的效果要比 GDI 要好的多。因為 Direct2D 使用基于圖元的反鋸齒效果(這樣會使線條更加的平滑),而且在渲染二維圖元的時候,完全支持透明和 Alpha 混合。以下是對比的照片:
顯然,右邊的 Direct2D 的線條效果要好于左邊的 GDI。
demo
- GDI
正弦曲線 - GDI+
漸變色,Alpha混合 - D2D
螺旋路徑動畫
GDI 繪圖代碼:
void CChildView::_drawWithGDI( CPaintDC& dc ) {CRect rect;GetClientRect(&rect);// 繪制 titledc.TextOut(10, 10, _T("GDI"), 3);//邏輯坐標與設備坐標變換dc.SetMapMode(MM_ANISOTROPIC);dc.SetWindowOrg(0, 0);dc.SetWindowExt(rect.right, rect.bottom);dc.SetViewportOrg(0, rect.bottom / 2);dc.SetViewportExt(rect.right, - rect.bottom);//創建繪制 x 軸的 pen 并將其選入設備上下文CPen penx(PS_SOLID, 1, RGB(0, 0, 255));HGDIOBJ oldObject = dc.SelectObject(penx.GetSafeHandle());//繪制 x 軸dc.MoveTo(0, 0);dc.LineTo(rect.right, 0);//創建繪制正旋曲線的 pen 并將其選入設備上下文CPen pen(PS_SOLID, 1, RGB(255, 0, 0));dc.SelectObject(pen.GetSafeHandle());//繪制正弦曲線dc.MoveTo(0, 0);for (int i = 0; i < rect.right; i++)dc.LineTo(i, (int)(100 * sin(2 *(i / (rect.right / 5.0)) * M_PI)));//恢復原先的 pendc.SelectObject(oldObject);// 恢復邏輯坐標與設備坐標變換dc.SetViewportOrg(0, 0);dc.SetViewportExt(rect.right, rect.bottom); }GDI+ 繪圖代碼:
void CChildView::_drawWithGDIPlus( CPaintDC& dc ) {using namespace Gdiplus;CRect rect;GetClientRect(&rect);Graphics graphics(dc);//創建漸變畫刷LinearGradientBrush lgb(Point(0, 0), Point(rect.right, rect.bottom), Color::Blue, Color::Purple);graphics.FillRectangle(&lgb, 0, 0, rect.right, rect.bottom);//創建ColorMatrixColorMatrix ClrMatrix ={1.0f, 0.0f, 0.0f, 0.0f, 0.0f,0.0f, 1.0f, 0.0f, 0.0f, 0.0f,0.0f, 0.0f, 1.0f, 0.0f, 0.0f,0.0f, 0.0f, 0.0f, 0.5f, 0.0f,0.0f, 0.0f, 0.0f, 0.0f, 1.0f};//將 ColorMatrix 賦給 ImageAttributesImageAttributes ImgAttr;ImgAttr.SetColorMatrix(&ClrMatrix, ColorMatrixFlagsDefault,ColorAdjustTypeBitmap);FontFamily fontFamily1(_T("Calibri"));Font font1(&fontFamily1, 12, FontStyleRegular, UnitPoint);//Alpha 混合graphics.DrawString(_T("LENA"), 4, &font1, PointF(140.0f, 140.0f), &SolidBrush(Color::White));TCHAR szImgPath[MAX_PATH] = {0};//確保程序所在目錄下存在 lena.png (res 文件夾下有)PathHelper::makeFullPathWithModuleDir(szImgPath, MAX_PATH, _T("lena.png"));Image img(szImgPath);graphics.DrawImage(&img, RectF(60, 140, 200, 200), 0, 0, (REAL)img.GetWidth(), (REAL)img.GetHeight(), UnitPixel, &ImgAttr);// 繪制 titlegraphics.DrawString(_T("GDI+"), 4, &font1, PointF(10.0f, 10.0f), &SolidBrush(Color::White)); }Direct2D 繪制代碼(部分):
HRESULT D2DImpl::render(HWND hwnd) {using namespace D2D1;HRESULT hr;hr = createDeviceResources(hwnd);RETURN_IF_FAILED(hr);int wndState = m_pRenderTarget->CheckWindowState();RETURN_IF_TRUE(wndState & D2D1_WINDOW_STATE_OCCLUDED, E_FAIL);D2D1_SIZE_F rtSize = m_pRenderTarget->GetSize();// Prepare to draw.m_pRenderTarget->BeginDraw();// Reset to identity transformm_pRenderTarget->SetTransform(Matrix3x2F::Identity());m_pRenderTarget->Clear(ColorF(ColorF::Black));TCHAR szTitle[] = _T("Direct2D");m_pRenderTarget->DrawText(szTitle,_tcslen(szTitle),m_pTextFormat,D2D1::RectF(10, 10, rtSize.width, rtSize.height),m_pTextBrush);//center the pathfloat minWidthHeightScale = min(rtSize.width, rtSize.height) / 512;Matrix3x2F scale = Matrix3x2F::Scale(minWidthHeightScale, minWidthHeightScale);Matrix3x2F translation = Matrix3x2F::Translation(rtSize.width / 2, rtSize.height / 2);m_pRenderTarget->SetTransform(scale * translation);//draw the path in redm_pRenderTarget->DrawGeometry(m_pPathGeometry, m_pRedBrush);static float float_time = 0.0f;float length = m_animation.GetValue(float_time);D2D1_POINT_2F point;D2D1_POINT_2F tangent;// Ask the geometry to give us the point that corresponds with the length at the current time.hr = m_pPathGeometry->ComputePointAtLength(length, NULL, &point, &tangent);// Reorient the triangle so that it follows the direction of the path.D2D1_MATRIX_3X2_F triangleMatrix = Matrix3x2F(tangent.x, tangent.y,-tangent.y, tangent.x,point.x, point.y );m_pRenderTarget->SetTransform(triangleMatrix * scale * translation);// Draw the yellow triangle.m_pRenderTarget->FillGeometry(m_pObjectGeometry, m_pYellowBrush);// Commit the drawing operations.hr = m_pRenderTarget->EndDraw();if (hr == D2DERR_RECREATE_TARGET) {hr = S_OK;discardDeviceResources();}// When we reach the end of the animation, loop back to the beginning.if (float_time >= m_animation.GetDuration())float_time = 0.0f;elsefloat_time += (float)(m_dwmTiming.rateCompose.uiDenominator) / (m_dwmTiming.rateCompose.uiNumerator);InvalidateRect(hwnd, NULL, FALSE);return hr; }
– EOF –
總結
以上是生活随笔為你收集整理的Windows 2D 绘图 (GDI, GDI+, Direct2D)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 动画七、动画的PropertyValue
- 下一篇: 94、基于51单片机的无线调频对讲机系统