CachedComponentImage invalidation logic


#1

Forgive me for asking questions about drawing I realise this topic has come up many times in the past but I’m just trying to wrap my head around some logic that I was unable to make sense off (yet).

When repaint() is called on a component why does it invalidate() it’s parent? I don’t mean why does it repaint it, that makes perfect sense if the component is not opaque.

For example, lets say the parent has a CachedComponentImage set, and I call repaint() on a child component, that child will then call internalRepaint() on it’s parent (which I’m sure is all well and good) but that then calls internalRepaintUnchecked() which will in turn invalidates the parent and forces it to repaint into the image before drawing the image to the screen.

Compare that scenario to if there are two siblings, with the new sibling now having the CachedComponentImage and having it’s size follow that of the parent. In this case the sibling would be asked to draw the image but it wouldn’t be marked invalidated.

The outcome of both scenarios ‘should’ visually be the same - but one is much more efficient than the other (assuming the paint routine is more expensive than drawing an image).

I intuitively thought that repaint() would only invalidate the component it was explicitly called on, I didn’t expect it to implicitly call it on parent components.

Even more surprisingly it seems that marking the original child component (that is having repaint called on it) as opaque also has no effect, again I intuitively thought marking it opaque would mean the parent component would not have to redraw into its image, actually I would have thought it didn’t even need to draw the image to screen at all!

I’m just wondering why this is, I suspect there is a good reason it is the way it is, maybe paintOverChildren() is an issue intertwined with this?

Thanks for your time.
Anthony.


#2

Individual components don’t have any concept of being “invalid”. What repaint() does is to invalidate a region of the parent window, and everything in the dirty regions gets repainted later on.


#3

Thanks for replying Jules.

I realise ordinary components don’t have this concept but the CachedComponentImage does via invalidateAll() and invalidate (const Rectangle<int>& area), right?

What surprised me was if you have…

Component A

  • call setBufferedToImage (true);

Componnt B

  • is a child of Component A

if I call repaint() on Component B, Component A’s invalidate() methods will be called, why? I understand it should repaint the portion of the image to the screen, but unless I’m mistaken (perfectly possible) it shouldn’t repaint itself into the image (which is what the invalidate calls do).

More surprisingly than that is if you call setOpaque (true) on Component B, it still results in calling invalidate() on Component A - in other words we repaint the image even though we are guaranteed that the image will not be drawn to the screen because the dirty rectangle is completely covered by the opaque component (Component B).


#4

If the parent is buffered, then part of that image is the child component. So if the child needs repainting, of course the parent needs to invalidate its image!

Having nested, buffered, opaque components that aren’t perfectly efficiently handled could just be an edge-case that was overlooked, or where it just wasn’t worth the extra step of checking for occlusion just to handle a rare situation.


#5

Ah OK that does make sense, thankyou Jules.

Am I right in thinking that the children are only really part of the component because of paintOverChildren()? If there was a flag such as setPaintsOverChildren (false); (I respect this would have to be on by default for backwards compatibility), then the invalidation of the cached image wouldn’t be required AND the paint() method of CachedComponentImage could call paint() on the component as opposed to paintEntireComponent(). Or am I talking out my behind?

If I’m right it seems like some potentially nice efficiency improvements could be gained with this and the opaque flag.


#6

No, that’s not the reason.

If you’re trying to cache just the stuff behind the children which you render with your paint() method then there are lots of ways to do that, e.g. embed another child component at the back that is cached, and which doesn’t contain anything else. Or render into your own Image and draw it in paint().

But if it was all done the other way round, and only the paint() content was cached, that might suit what you’re doing but would make it impossible for other people to cache the result of rendering all the children as well, which is also a valid use-case.


Saving CPU while repainting component and its child components with setBufferedToImage
#7

OK gotcha, thanks for your invaluable input Jules.