将 TImageList 字形绘制到 TDirect2DCanvas

Drawing a TImageList glyph to a TDirect2DCanvas

我目前正准备将旧组件的绘图代码从 GDI + UniScribe 替换为 Direct2D 和 DirectWrite(后继者)。

到目前为止,转换是直截了当的,因为大部分时间我需要做的就是将对 Canvas (class TCanvas) 的调用替换为自定义 FDirect2D Canvas 实例(class TDirect2DCanvas,来自 Direct2D 单元)。

不幸的是,尝试从 TImageList 实例绘制字形到 FDirect2DCanvas 时似乎并不那么简单,因为 draw 方法仅适用于 TCanvas 而不是general TCustomCanvas(它是 TCanvas 和 TDirect2DCanvas 的祖先)。

这个难题的解决方案是将 TImageList 字形绘制到临时位图并将其绘制到 TDirect2DCanvas。但是,我担心这可能会大大降低绘图性能。

到目前为止有没有人这样做过?我有哪些选择?

如果您查看将图形对象绘制到 TDirect2DCanvas 的实现方式,您会发现它通过此例程进行路由。

procedure TDirect2DCanvas.StretchDraw(const Rect: TRect; Graphic: TGraphic;
  Opacity: Byte);
var
  D2DBitmap:  ID2D1Bitmap;
  D2DRect: TD2DRectF;
  Bitmap: TBitmap;
begin
  Bitmap := TBitmap.Create;
  try
    Bitmap.Assign(Graphic);

    D2DBitmap := CreateBitmap(Bitmap);

    D2DRect.Left   := Rect.Left;
    D2DRect.Right  := Rect.Right;
    D2DRect.Top    := Rect.Top;
    D2DRect.Bottom := Rect.Bottom;
    RenderTarget.DrawBitmap(D2DBitmap, @D2DRect, Opacity/255);
  finally
    Bitmap.Free;
  end;
end;

让我们取消选择涉及的步骤:

  1. 创建一个临时位图。
  2. 将图形复制到该位图中。
  3. 创建一个ID2D1Bitmap并将临时位图复制到其中。
  4. ID2D1Bitmap 绘制到渲染目标上。

这看起来效率很低。当然,如果调用这个传递 TBitmap 的函数并无缘无故地制作一个副本,那将是令人恼火的。

虽然当您尝试混合两个不同的图形框架时,这种事情很难避免。您的图像列表是基于 GDI 的,因此当您尝试将其发送到 Direct2D canvas 时必然会遇到摩擦。根本无法将 GDI 位图直接传递给 Direct2D canvas,必须先将它们转换为 Direct2D 位图。

如果性能对您很重要,那么您不应该从图像列表开始。当您从 GDI 图像列表中提取位图,然后将其转换为等效的 Direct2D 对象时,这将不可避免地产生成本 ID2D1Bitmap

为了获得最佳性能,请勿使用图像列表。从图像列表中提取每个图像并使用 TDirect2DCanvas.CreateBitmap 获取 Direct2D 位图,ID2D1Bitmap。存储这些而不是图像列表。然后当你需要绘制时,在 RenderTarget 上调用 DrawBitmap,传递一个 ID2D1Bitmap.