概述
我需要在画布上绘制大量图形,在本例中为TPaintBox
画布。油漆箱是TScrollBox
的子项,油漆箱的大小和高度可能非常大,例如5000x5000。
我有一个TList
来保存我自己的对象,每个对象都有自己的X和Y属性以及它自己的图形。在paintbox的OnPaint
方法中,我遍历列表中的每个对象,然后将每个对象图形绘制到存储在对象X和Y位置的坐标处的paintbox画布上。
显然,所有这些都会减慢应用程序的速度并变得非常沉重,所以我需要一种方法来优化它。
显而易见的方法是以某种方式利用GPU而不是CPU,但这可能要复杂得多,Firemonkey可以帮助我,但我严格运行的是VCL类型项目而不是FMX。
可能的解决方案?
我遇到了CreateRectRgn
并提供我正确理解,认为如果我可以使用自定义区域在绘图框上绘画,例如在滚动框的可见区域中,这应该可以提高性能。因此,考虑到这一点,我尝试了以下几点:
procedure TForm1.PaintBox1Paint(Sender: TObject);
var
MyRgn: HRGN;
begin
// iterate and draw objects onto FBuffer (offscreen bitmap) first,
// note: FBuffer size is the same as the scrollboxes clientwidth
// and clientheight
//begin
// ...
//end;
// create and paint on a region (visible area of the scrollbox) on
// the paintbox rather than painting the whole paintbox.
MyRgn := CreateRectRgn(0, 0, ScrollBox1.ClientWidth, ScrollBox1.ClientHeight);
try
SelectClipRgn(PaintBox1.Canvas.Handle, MyRgn);
PaintBox1.Canvas.Draw(ScrollBox1.HorzScrollBar.Position,
ScrollBox1.VertScrollBar.Position, FBuffer);
SelectClipRgn(PaintBox1.Canvas.Handle, HRGN(nil));
finally
DeleteObject(MyRgn);
end;
问题
我首先想到的是,这是否是一种优化我在画笔画布上绘画的优秀方法,我还有其他可能的选择吗?我要注意的另一件事是检查每个对象的X和Y,如果它在滚动框的可见区域之外,我不会绘制它。
我仍然熟悉创建剪辑区域的想法,但上面的代码示例感觉不对。我不喜欢不断创建和删除区域的想法,特别是在paint方法中。
我的项目变得相当大,事实上我实际上把它放到一个自定义控件中但是当处理数百个对象并在paintbox画布上绘制它时,我逐渐减速并且我很确定它与要么我没有正确实现剪辑区域,要么我不断创建,绘制,然后从OnPaint
方法删除区域。
有没有更实用的方法来实现这一目标?也许有可能在表单创建区域创建并在表单销毁(或从自定义控件构造函数/析构函数)中销毁它?但是,如果调整表格/控件的大小,我将如何调整剪裁区域的大小?
我真的可以使用一些建议,并清楚地了解我面临的这个问题,以帮助我更好地理解我可以做得更好或不同的事情。
感谢。
答案 0 :(得分:2)
根据您的代码示例和注释,您似乎首先将所有对象绘制到屏幕外的位图中。
如果是这种情况,则使用或不使用剪辑区域不会产生太大的影响,因为瓶颈在于将所有对象绘制到屏幕外位图的代码中。
优化代码的最佳解决方案是仅绘制可见区域中的对象。
所以现在你的主要问题应该是找出哪些物体在可见物中是哪种物体的最佳方法。
由于你在评论中提到你正在进行类似地图的控制,我认为最好的方法是将地图划分为由多个扇区组成的网格,然后根据对象位置存储每个扇区的对象数据。每个部门都是一个独立的TList。
这将允许您仅基于可见扇区迭代少量对象,这些扇区可以提高您的性能。
现在这些扇区的大小应该取决于对象的常见大小。
此外,在确定要绘制哪些扇区时,请确保您还要在可见区域之外绘制一点,以便仍然绘制位置与扇区边界交叉并且可能仅部分可见的对象。如果您的对象尺寸都没有超过单个扇区的大小,那么绘制所有可见扇区加上一行或一行就足够了。