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.
Saving and re-using internal bitmap
Re: Saving and re-using internal bitmap
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.
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,
Yeray Alonso Development & Support Steema Software Av. Montilivi 33, 17003 Girona, Catalonia (SP) | |
Please read our Bug Fixing Policy |
Re: Saving and re-using internal bitmap
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!
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 586 times
Re: Saving and re-using internal bitmap
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.
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,
Yeray Alonso Development & Support Steema Software Av. Montilivi 33, 17003 Girona, Catalonia (SP) | |
Please read our Bug Fixing Policy |
Re: Saving and re-using internal bitmap
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.
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.
Re: Saving and re-using internal bitmap
Hello,
You could draw your million lines into a separate bitmap and draw it at
Find attached your application with these modifications.
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.
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);
}
}
Also note the logic above doesn't support chart zoom&scroll.
But it's much faster.
Best Regards,
Yeray Alonso Development & Support Steema Software Av. Montilivi 33, 17003 Girona, Catalonia (SP) | |
Please read our Bug Fixing Policy |
Re: Saving and re-using internal bitmap
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!
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!