将 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;
让我们取消选择涉及的步骤:
- 创建一个临时位图。
- 将图形复制到该位图中。
- 创建一个
ID2D1Bitmap
并将临时位图复制到其中。
- 将
ID2D1Bitmap
绘制到渲染目标上。
这看起来效率很低。当然,如果调用这个传递 TBitmap
的函数并无缘无故地制作一个副本,那将是令人恼火的。
虽然当您尝试混合两个不同的图形框架时,这种事情很难避免。您的图像列表是基于 GDI 的,因此当您尝试将其发送到 Direct2D canvas 时必然会遇到摩擦。根本无法将 GDI 位图直接传递给 Direct2D canvas,必须先将它们转换为 Direct2D 位图。
如果性能对您很重要,那么您不应该从图像列表开始。当您从 GDI 图像列表中提取位图,然后将其转换为等效的 Direct2D 对象时,这将不可避免地产生成本 ID2D1Bitmap
。
为了获得最佳性能,请勿使用图像列表。从图像列表中提取每个图像并使用 TDirect2DCanvas.CreateBitmap
获取 Direct2D 位图,ID2D1Bitmap
。存储这些而不是图像列表。然后当你需要绘制时,在 RenderTarget
上调用 DrawBitmap
,传递一个 ID2D1Bitmap
.
我目前正准备将旧组件的绘图代码从 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;
让我们取消选择涉及的步骤:
- 创建一个临时位图。
- 将图形复制到该位图中。
- 创建一个
ID2D1Bitmap
并将临时位图复制到其中。 - 将
ID2D1Bitmap
绘制到渲染目标上。
这看起来效率很低。当然,如果调用这个传递 TBitmap
的函数并无缘无故地制作一个副本,那将是令人恼火的。
虽然当您尝试混合两个不同的图形框架时,这种事情很难避免。您的图像列表是基于 GDI 的,因此当您尝试将其发送到 Direct2D canvas 时必然会遇到摩擦。根本无法将 GDI 位图直接传递给 Direct2D canvas,必须先将它们转换为 Direct2D 位图。
如果性能对您很重要,那么您不应该从图像列表开始。当您从 GDI 图像列表中提取位图,然后将其转换为等效的 Direct2D 对象时,这将不可避免地产生成本 ID2D1Bitmap
。
为了获得最佳性能,请勿使用图像列表。从图像列表中提取每个图像并使用 TDirect2DCanvas.CreateBitmap
获取 Direct2D 位图,ID2D1Bitmap
。存储这些而不是图像列表。然后当你需要绘制时,在 RenderTarget
上调用 DrawBitmap
,传递一个 ID2D1Bitmap
.