Drawing Invalidation Question
-
When inside of -[NSView drawRect:], what's the difference between
- (void)drawRect:(NSRect)rect;
{
if ([self needsToDrawRect:someRect])
....
}
... and ...
- (void)drawRect:(NSRect)rect;
{
if (NSIntersectsRect(rect, someRect))
....
}
... ?
AFAICT from the documentation, there isn't a difference. Am I
misreading something important?
--
Seth Willits -
On 1 Feb 2009, at 05:41:49, Seth Willits wrote:
> When inside of -[NSView drawRect:], what's the difference between
>
> - (void)drawRect:(NSRect)rect;
> {
> if ([self needsToDrawRect:someRect])
> ....
> }
>
>
> ... and ...
>
>
> - (void)drawRect:(NSRect)rect;
> {
> if (NSIntersectsRect(rect, someRect))
> ....
> }
>
>
> ... ?
Apart from anything else, the first makes it clear what your intent
is, which would be useful when you return to that bit of code later. -
> From the View Programming Guide for Cocoa (http://developer.apple.com/DOCUMENTATION/Cocoa/Conceptual/CocoaViewsGuide/O
ptimizing/chapter_8_section_5.html#//apple_ref/doc/uid/TP40002978-CH11-SW4
):
"In Mac OS X version 10.3 and later, views can constrain their drawing
even further by using the NSView methods getRectsBeingDrawn:count: and
needsToDrawRect:. These methods provide direct and indirect access,
respectively, to the detailed representation of a view's invalid
areas—that is, its list of non-overlapping rectangles—that the
Application Kit maintains for each NSView instance. The Application
Kit automatically enforces clipping to this list of rectangles, and
you can further improve performance in views that do complex or
expensive drawing by having them limit their drawing to objects that
intersect any of the rectangles in this list."
The parameter to -drawRect: specifies a rectangle that includes all
those dirty rectangles. If, for example, your view consists of lots
of expensive-to-draw regions, you can test each of those regions using
-needsToDrawRect: to avoid drawing ones that aren't in a dirty region
but wind up being included in the big rectangle.
--Kyle Sluder -
> When inside of -[NSView drawRect:], what's the difference between
>
> - (void)drawRect:(NSRect)rect;
> {
> if ([self needsToDrawRect:someRect])
> ....
> }
>
> - (void)drawRect:(NSRect)rect;
> {
> if (NSIntersectsRect(rect, someRect))
> ....
> }
The NSView keeps a list of the actual subrectangles inside its bounds
which are dirty, but it unions them all together into the single
'rect' parameter in -drawRect:, so 'rect' can be much larger than the
strict minimum of what needs to be drawn.
You can get the actual list of dirty rects with -
getRectsBeingDrawn:... or, alternately, if you have an expensive
element you can check ahead of time if you need to draw it at all with
-needsToDrawRect: -- even though the element might lie inside your
bounds and inside 'rect', it still might not be touched by a dirty
region.
-W -
On 1 Feb 2009, at 4:41 pm, Seth Willits wrote:
>
>
> When inside of -[NSView drawRect:], what's the difference between
>
> - (void)drawRect:(NSRect)rect;
> {
> if ([self needsToDrawRect:someRect])
> ....
> }
>
>
> ... and ...
>
>
> - (void)drawRect:(NSRect)rect;
> {
> if (NSIntersectsRect(rect, someRect))
> ....
> }
>
>
> ... ?
>
>
> AFAICT from the documentation, there isn't a difference. Am I
> misreading something important?
As others have mentioned, there is potentially a huge difference. If
you invalidate a lot of small rectangles, the update region might be
quite complex in shape; the <rect> passed in -drawRect: is only the
bounds of this complex area.
But there is also a difference in intent between -needsToDrawRect: and
-getRectsBeingDrawn:count:, even though they also appear to amount to
much the same thing. The first method implies you'll be iterating a
list and testing each element against the view to see if it needs to
be drawn or not. In many (most?) cases that's going to work out just
fine. But if you have a huge number of possible objects to draw,
merely iterating the list and testing for every update could incur a
noticeable performance penalty. The second method offers the
possibility of using a different approach that allows you to
efficiently search and locate the objects needing to be drawn without
iterating the entire list to do so. (An analogy would be the
difference between a linear search and a binary search). That can have
great benefits (though as I say, typically only for large numbers of
objects). Algorithms such as BSP, R*-Trees and so forth can be used
with the second approach. My own tests indicate that the number at
which a BSP storage approach starts being worth considering is around
the 1000 object mark, depending on what exactly it is you're drawing.
YMMV.
This is probably more than you need or care to know at this point but
I thought it might be worth mentioning ;-)
--Graham -
On Feb 1, 2009, at 3:07 AM, Graham Cox wrote:
> As others have mentioned, there is potentially a huge difference. If
> you invalidate a lot of small rectangles, the update region might be
> quite complex in shape; the <rect> passed in -drawRect: is only the
> bounds of this complex area.
Which is why the docs confused me. I knew there were subrectangles,
but the weird thing was this line in the docs:
"It is optimized to efficiently reject any rectangle that lies outside
the bounding box of the area the receiver is being asked to draw in
drawRect:."
So it's not optimized to reject anything outside of the sub-
rectangles? This sentence really threw me off. In retrospect and I can
see that it simply means that it's doing a simple check up front like
below, but it's a rather poorly worded description as it doesn't
mention the sub-rectangles at all. Documentation fail. ;-)
- (BOOL)needsDisplayInRect:(NSRect)rect;
{
if (!NSIntersectsRect(rect, theBigBoundingRect)) {
return NO;
}
... other checks
}
> But there is also a difference in intent between....
> This is probably more than you need or care to know at this point
> but I thought it might be worth mentioning ;-)
Actually, it is very relevant to what I am doing. :-)
Thanks all,
--
Seth Willits -
On 2 Feb 2009, at 8:05 am, Seth Willits wrote:
> So it's not optimized to reject anything outside of the sub-
> rectangles? This sentence really threw me off. In retrospect and I
> can see that it simply means that it's doing a simple check up front
> like below, but it's a rather poorly worded description as it
> doesn't mention the sub-rectangles at all. Documentation fail. ;-)
I agree it's a long-winded way to say it's performing a basic
intersection check, but I think it's probably historic - the
subrectangles weren't available until 10.3 (I think, maybe it was
10.2?) so previous to that you only had the overall bounding box to go
on. The docs may not have been revised since then.
--Graham -
On Sun, Feb 1, 2009 at 4:05 PM, Seth Willits <slists...> wrote:
> On Feb 1, 2009, at 3:07 AM, Graham Cox wrote:
>
>> As others have mentioned, there is potentially a huge difference. If you
>> invalidate a lot of small rectangles, the update region might be quite
>> complex in shape; the <rect> passed in -drawRect: is only the bounds of this
>> complex area.
>
> Which is why the docs confused me. I knew there were subrectangles, but the
> weird thing was this line in the docs:
> "It is optimized to efficiently reject any rectangle that lies outside the
> bounding box of the area the receiver is being asked to draw in drawRect:."
>
> So it's not optimized to reject anything outside of the sub-rectangles? This
> sentence really threw me off. In retrospect and I can see that it simply
> means that it's doing a simple check up front like below, but it's a rather
> poorly worded description as it doesn't mention the sub-rectangles at all.
> Documentation fail. ;-)
You left out the word "efficiently" from the formulation of your
contrapositive, which I think is important. To me, the words
"optimized" and "efficiently" make it clear that it's only describing
a speed enhancement, and telling you that there's no need to do your
own bounding-rect culling beforehand. The full behavior of the method
is described at the top of the documentation.
Of course documentation quality and clarity is a matter of opinion,
and may vary.
Mike



