How to Invalidate only part of a paintbox/bitmap to optimise its performance?

我怕爱的太早我们不能终老 提交于 2020-01-23 01:48:18

问题


The question relates to: Drawing on a paintbox - How to keep up with mouse movements without delay?

I was going to at some point ask the question of how to repaint only part of a paintbox without invalidating the whole paintbox, which is slow when there is a lot of drawing going on or in my case when there are a lot of tiles drawn on screen.

From the link above Peter Kostov did touch on the subject briefly in one of his comments:

you can partly BitBlt the offscreen bitmap (only regions where it is changed). This will improve the performance dramatically.

I have limited graphic skills and have never really used BitBlt before but I will be reading more about it after I post this question.

With that said, I wanted to know how exactly could you determine if regions of a bitmap have changed? Does this involve something simple or is there more magic so to speak involved in determining which regions have changed?

Right now I am still painting directly to the paintbox but once I draw to the offscreen buffer bitmap my next step is to optimise the painting and the above comments sound exactly like what I need, only the determining regions that have changed has confused me slightly.

Of course if there are other ways of doing this please feel free to comment.

Thanks.


回答1:


You don't have to use BitBlt() directly if you draw to an offscreen TBitmap, you can use TCanvas.CopyRect() instead (which uses StretchBlt() internally). But either way, when you need to invalidate just a portion of the TPaintBox (the portion corresponding to the section of the offscreen bitmap that you drew on), you can use InvalidateRect() directly to specify the appropriate rectangle of the TPaintBox, instead of calling TControl.Invalidate() (which calls InvalidateRect() with the lpRect parameter set to NULL). Whenever the TPaintBox.OnPaint event is triggered, InvalidateRect() will have established a clipping rectangle within the canvas, any drawing you do outside of that rectangle will be ignored. If you want to manually optimize your drawing beyond that, you can use the TCanvas.ClipRect property to determine the rectangle of the TPaintBox that needs to be drawn, and just copy that portion from your onscreen bitmap.

The only gotcha is that TPaintBox is a TGraphicControl descendant, so it does not have its own HWND that you can pass to InvalidateRect(). You would have to use its Parent.Handle HWND instead. Which means you would have to translate TPaintBox-relative coordinates into Parent-relative coordinates and vice versa when needed.




回答2:


You need to take charge of the painting in order to do this:

  • Call InvalidateRect to invalidate portions of a window.
  • When handling WM_PAINT you call BeginPaint which yields a paint struct containing the rect to be painted.

All of this requires a window, and unfortunately for you, TPaintBox is not windowed. So you could use the parent control's window handle, but frankly it would be cleaner to use a windowed control.

You could use my windowed paint control from this question as a starting point: How could I fade in/out a TImage? Use the ClipRect of the control's canvas when painting to determine the part of the canvas that needs re-painting.



来源:https://stackoverflow.com/questions/25534907/how-to-invalidate-only-part-of-a-paintbox-bitmap-to-optimise-its-performance

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!