Saving and re-using internal bitmap

TeeChart VCL for Borland/CodeGear/Embarcadero RAD Studio, Delphi and C++ Builder.
Post Reply
arekay
Newbie
Newbie
Posts: 4
Joined: Tue Apr 12, 2022 12:00 am

Saving and re-using internal bitmap

Post by arekay » Sat Apr 30, 2022 11:32 pm

I have an application where I use BeforeDrawSeries to draw lines manually (e.g., with MoveTo and LineTo) prior to regular rendering. When there are just tens or hundreds of lines, it works fine, but when there are thousands, it gets bogged down, and so I would like to improve its efficiency. In addition, when I save the chart as a PDF, the lines get saved individually, making the PDF file also very large and slow to render.

What I would like to do is use bitmaps instead. Ideally, I would like to render the chart background normally, but then save it as a bitmap. Then, the next time BeforeDrawSeries is called, I could run a check to see if anything important has changed; if not, I can just render the bitmap, or otherwise I can go through the line-drawing procedure again. I'm thinking that I can do this by capturing the bitmap for the BackWall at the end of BeforeDrawSeries, and then setting it as the BackWall picture, but it's not clear how to capture just that part.

Also, it would be very nice if, when rendering thousands of lines like this, I could turn off the on-screen update until the background rendering is completed, as sometimes they compete with each other. Can this behavior be controlled?

Any hints or guidance would be much appreciated.

Yeray
Site Admin
Site Admin
Posts: 9622
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Saving and re-using internal bitmap

Post by Yeray » Mon May 02, 2022 2:25 pm

Hello,

Thanks for the description but I'm not sure how are you exactly generating that pdf.
Can you please arrange a simple example project we can run as-is to reproduce the problem here?
Thanks in advance.
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

arekay
Newbie
Newbie
Posts: 4
Joined: Tue Apr 12, 2022 12:00 am

Re: Saving and re-using internal bitmap

Post by arekay » Wed May 18, 2022 3:05 am

Hello,

Thanks for your answer. Here is a project (in C++ Builder 11 Update 1) that demonstrates the various aspects of my question. By shift-clicking on the chart, you can move the non-zero point, and cause the chart to redraw. The drop-down list controls how many background lines are drawn. At one thousand lines everything is responsive on my computer, but as you raise it to one million it bogs down. Saving the thousand-line version as PDF gives a file size of 81 kb; the one million version is 62 Mb.

In my real application the lines are generated by a model, and so I have little control over how many there are.

The things I've thought of that might help are:
- Stopping screen rendering while lines are being drawn in background
- After lines are drawn (at end of BeforeDrawSeries), save the back panel as a bitmap.
- At the beginning of BeforeDrawSeries, add an internal check to see if the lines need to be redrawn; if not, just redraw the bitmap.
However, I'm not sure of how to do these things. Alternatives ideas are also welcome.

Let me know if you need anything more. Thanks!
Attachments
BitmapQuestion.zip
(8.14 KiB) Downloaded 644 times

Yeray
Site Admin
Site Admin
Posts: 9622
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Saving and re-using internal bitmap

Post by Yeray » Mon May 23, 2022 2:05 pm

Hello,

Drawing a million lines is always slow. That's why the majority of techniques we use to improve drawing performance involve reducing the number of lines to be drawn as you can see here.
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

arekay
Newbie
Newbie
Posts: 4
Joined: Tue Apr 12, 2022 12:00 am

Re: Saving and re-using internal bitmap

Post by arekay » Tue May 24, 2022 12:58 am

Hello:

Yes, I'm aware that drawing a million lines is always slow. But, when my application needs it, it needs it. The link you provided does not present any solutions I can identify; I can't see anything about reducing the number of lines to draw.

To be clear, I am OK with being slow drawing a million lines once. What I want to prevent is being just as slow on every subsequent screen update, or redraw of the one series that might be moving. Hence my question is about capturing one good image of the state of the screen after the lines are drawn, and re-using it for as long as possible. Probably all I need to do is to capture the background picture (bitmap) after it is drawn, and put it up as a background on the back panel until I need to update. An additional nice thing would be to prevent screen updates while it is generating the expensive background picture, but that would be gravy. The capture/reuse should not be a terribly complex operation, but I cannot work out the way to do it within the documentation.

Thanks.

Yeray
Site Admin
Site Admin
Posts: 9622
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Saving and re-using internal bitmap

Post by Yeray » Tue May 24, 2022 3:27 pm

Hello,

You could draw your million lines into a separate bitmap and draw it at Chart1BeforeDrawSeries:

Code: Select all

void __fastcall TForm3::Chart1BeforeDrawSeries(TObject *Sender)
{
	//...
	if (backInvalid) {
		backChart = new TBitmap();
		//backChart->Transparent = true;
		backChart->Canvas->Pen->Color = clGray;
		backChart->SetSize(Chart1->ChartRect.Width(), Chart1->ChartRect.Height());
		for (int i=0; i < linesToDraw; i++) {
			int xmid = Series1->CalcXPosValue(0)-Chart1->ChartRect.Left;
			int ymid = Series1->CalcYPosValue(0)-Chart1->ChartRect.Top;
			backChart->Canvas->MoveTo(xmid,ymid);
			//Chart1->Canvas->MoveTo(xmid,ymid);
			xmid = Series1->CalcXPosValue(xPoints[i])-Chart1->ChartRect.Left;
			ymid = Series1->CalcYPosValue(yPoints[i])-Chart1->ChartRect.Top;
			backChart->Canvas->LineTo(xmid,ymid);
			//Chart1->Canvas->LineTo(xmid,ymid);
		}
		backInvalid = false;
	}
	if (backChart != NULL) {
		Chart1->Canvas->Draw(Chart1->ChartRect.Left, Chart1->ChartRect.Top, backChart);
	}
}
Find attached your application with these modifications.
BitmapQuestion.zip
(8.58 KiB) Downloaded 638 times
I guess this could be improved to add antialias to those lines and to make the bitmap transparent to show the chart grid.
Also note the logic above doesn't support chart zoom&scroll.
But it's much faster.
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

arekay
Newbie
Newbie
Posts: 4
Joined: Tue Apr 12, 2022 12:00 am

Re: Saving and re-using internal bitmap

Post by arekay » Mon Jun 06, 2022 1:50 pm

Thank you for your answer; it is very helpful.

To make the bitmap work with transparency, it turns out the draw command needs to be:
Chart1->Canvas->ReferenceCanvas->Draw(Chart1->ChartRect.Left, Chart1->ChartRect.Top, backChart);
In other words, you need to use the ReferenceCanvas, as TTeeCanvas does not implement transparency in bitmap renderings.
In the TeeChart documentation, there is a vague warning that ReferenceCanvas is "Used for low level tasks only." So, I'd like to double-check that this is a safe thing to do.

I also have a tangential question: in your code, you never delete the old TBitmap before creating a new one, but there is no memory leak. I gather that this is because reference counting is going on somewhere. Do you know where this is -- in TeeChart, VCL, or somewhere else?

Thanks!

Post Reply