Overview
I need to draw lots of graphics onto a canvas, in this case a TPaintBox
canvas. The paintbox is child to a TScrollBox
and the size of the paintbox could be very large in height and width, for example 5000x5000.
I have a TList
which holds my own objects, each object has its own X and Y property as well as its own graphic. In the OnPaint
method of the paintbox I iterate through each object in my list and then draw each objects graphic onto the paintbox canvas at coordinates that are stored in the objects X and Y position.
Obviously drawing all this slows down the application and becomes very heavy, so I needed a way to optimise this.
The obvious way is to somehow utilise the GPU rather than the CPU but that is likely much more complex, Firemonkey could help me but I am strictly running in a VCL type project not FMX.
Possible Solution?
I came across CreateRectRgn
and providing I understand it correctly, thought that if I could paint on the paintbox with a custom region, for example in the visible area of the scrollbox this should improve performance a lot. So with that in mind I tried something along the lines of:
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;
Question
I suppose first and foremost is whether this even a good approach to optimizing how I paint on the paintbox canvas or not, what other possible options do I have? The only other thing of note that I do is check the X and Y of each object and if it is outside the visible area of the scrollbox I do not paint it.
I am still getting familiar with the idea of creating a clipping region, but the above code sample feels wrong. I don't like the idea of constantly creating and deleting the region, especially within the paint method.
My project is getting fairly large, in fact I am actually putting this into a custom control but when dealing with hundreds of objects and painting them on the paintbox canvas I gradually get a slowdown and I am fairly sure it is to do with either me not correctly implementing the clipping region or the way that I constantly create, draw and then delete the region from the OnPaint
method.
Is there a more practical suited way to achieve this? Perhaps it is possible to create the region at form create and destroy it on form destroy (or from the custom controls constructor/destructor) for example? But then how would I resize the clipping region if the form/control for example is resized?
I could really use some advice and get some clarity on this problem I am facing to help me better understand what I might be able to do better or differently.
Thanks.
TWinControl.PaintControls
. – Sertac Akyuz