admin 管理员组

文章数量: 887021

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)