GDI 是 Graphics Device Interface 的缩写,含义是图形设备接口,它的主要任务是负责系统与绘图程序之间的信息交换,处理所有 Windows 程序的图形输出。
在 Windows 操作系统下,绝大多数具备图形界面的应用程序都离不开 GDI,我们利用 GDI 所提供的众多 API 就可以方便的在屏幕、打印机及其它输出设备上输出图形、文本等操作。
GDI 具有如下特点:
GDI 函数大致可分类为:
GDI+ 是 GDI 的后续版本,最早于 2000 年随 Windows 2000 一起推出,后来又被包装进 .NET 框架的托管类库中,成为 .NET 中窗体绘图的主要工具。
GDI+ 主要提供了以下三类服务:
GDI 接口是基于函数的,而 GDI+ 是基于 C++ OO 的编程接口,因此使用起来比 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);
Direct2D 是一个基于 Direct3D 的 2D 图形 API,可以利用硬件加速特性来提供高性能、高质量的 2D 渲染。而且十分方便的是,Direct2D 与 GDI,GDI+ 和 D3D 都是可以交互的。一项技术总是有其受众面,看看微软怎么说的:
使用 Direct2D 渲染出来的效果要比 GDI 要好的多。因为 Direct2D 使用基于图元的反锯齿效果(这样会使线条更加的平滑),而且在渲染二维图元的时候,完全支持透明和 Alpha 混合。以下是对比的照片:
显然,右边的 Direct2D 的线条效果要好于左边的 GDI。
GDI 绘图代码:
void CChildView::_drawWithGDI( CPaintDC& dc )
{
CRect rect;
GetClientRect(&rect);
// 绘制 title
dc.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)));
//恢复原先的 pen
dc.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);
//创建ColorMatrix
ColorMatrix 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 赋给 ImageAttributes
ImageAttributes 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);
// 绘制 title
graphics.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 transform
m_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 path
float 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 red
m_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;
else
float_time += (float)(m_dwmTiming.rateCompose.uiDenominator) / (m_dwmTiming.rateCompose.uiNumerator);
InvalidateRect(hwnd, NULL, FALSE);
return hr;
}