【发布时间】:2023-03-26 10:01:01
【问题描述】:
我有一个我的应用程序的表单,它可以有多达 1000 个可视组件,我在其中使用位图的 Canvas 绘制每个组件,并保存每个组件的位图(一种双缓冲),因为每个操作需要 20 毫秒。
我正在使用线程来绘制位图并将带有此位图的通知发送到 MainThread,以刷新可视化组件 UI。
理论上,当组件的位图被绘制在线程中时,它必须具有流体形式以显示组件,但实际上它不是流体。我决定看一下 delphi 的 TCanvas,我发现了一些惊人的东西:
class var // <<<<<<<<<<<<<<<<<<<<<<<<<<<< class var
FLock: TObject;
function TCanvas.BeginScene(AClipRects: PClipRects = nil; AContextHandle: THandle = 0): Boolean;
begin
Lock;
...
end;
procedure TCanvas.EndScene;
begin
...
Unlock;
end;
class procedure TCanvas.Lock;
begin
TMonitor.Enter(FLock);
end;
class procedure TCanvas.Unlock;
begin
TMonitor.Exit(FLock);
end;
这绝对看起来不对。为什么 embarcadero 无法在不同线程中同时使用 TCanvas?创建 10 个线程来绘制位图是没有用的,因为每次都会处理 1 个线程...
- 为什么会存在?
- 有什么解决方法吗?如果我制作我的版本会发生什么 每个 TCanvas 仅具有本地监视器的 FMX.Graphics?
- 是否有任何第三方库拥有自己的 TCanvas?
我知道很多人会建议我使用原生类,Android 中的 JCanvas 和 iOS 中的 CGContextRef,但我想要一个 TCanvas 的解决方案,因为它的工作是封装所有平台的绘图功能,并且易于使用使用。
============= @EDIT =============
我将 FMX.Graphics 单元中 TCanvas 的 Lock 和 Unlock 更改为使用本地而不是全局监视器,以及 FMX.Types3D 单元中 TContext3D 的 BeginScene 和 EndScene。 我对这种变化感到非常担忧,但显然应用程序运行正常,最大的工作是重新编译整个 FMX。
【问题讨论】:
-
做好失望的准备。此外,成千上万的视觉组件是一个不好的迹象。这不可能是最好的方法。我敢打赌,这个问题可以很容易地解决,而无需使用线程。
-
特别是在移动平台的Firemonkey中,拥有太多控件是非常糟糕的设计。它变得太重了。相反,您应该 a) 尽可能重复使用控件,b) 为单独的主题制作单独的表单,并且只在第一次需要时创建它们,以及 c) 在不再需要时销毁控件。
-
@JerryDodge 我正在展示产品卡片,它位于 vertscrollbox 内。我已经减少了控件的数量,只制作了“显示控件”,它的位图缓冲区有很多产品卡......不幸的是,我不能只创建一个“大显示控件”,因为画布对每个设备都有大小限制(TCanvasAttribute.MaxBitmapSize)。但这不是问题。所有主要的移动应用程序都使用许多线程来解码 jpeg、绘制缓冲区等,只是为了节省 MainThread 并允许快速打开表单,并且不影响最低 60 fps 以使屏幕流畅。
-
用户能否一次显示所有这些产品卡片?如果没有,就没有理由画它们——如果我理解正确,你画了所有的? - 一次全部,但确定哪些是可见的,然后绘制这些。也许甚至有概览的缩略图,而在详细视图中只有完整的产品图像?
标签: delphi canvas bitmap firemonkey