Re: Drawing many thin vertical lines is very slow
Re: Drawing many thin vertical lines is very slow
- Subject: Re: Drawing many thin vertical lines is very slow
- From: Graham Cox <email@hidden>
- Date: Fri, 08 May 2015 15:00:15 +1000
> On 8 May 2015, at 2:43 pm, Michael David Crawford <email@hidden> wrote:
>
> My iOS app enables the user to toggle on and off a square grid. When
> the grid is enabled, a tap inside a cell toggles the color of the cell
> between black and blue. When the grid is disabled, one can use a
> pinch-zoom gesture to adjust the sizes of the cells.
>
> (I'm not so sure that's a sensible UI but that's not the problem I'm
> looking into just now.)
>
> If the pitch of the gride is fine, then there are many lines on the
> screen. If I autorotate, redrawing the vertical lines takes hundreds
> of times as long as redrawing the horizontal lines. That makes sense
> as one can draw a horizontal line by incrementing a pointer into the
> image buffer. To draw a vertical line one has to draw a pixel, then
> calculate the memory address of the next pixel vertically downwards.
>
> I wonder if there is a better way to do this? I'm using UIRectFill to
> draw 1.0 point thick rectangles. I thought of using CoreGraphics
> directly but Instruments tells me that all the time is spent way down
> in the innards of CG.
>
> Before I draw the grid, I erase the whole view to blue, then draw
> black rectangles for my cells. A small optimization is that if two or
> more cells are horizontal neighbors, I draw them in one UIRectFill
> operation.
>
> The vertical line drawing is so slow that it is disruptive to the user
> when autorotating - the whole UI freezes for as long as two seconds,
> looks distorted during that time then very quickly renders the desired
> appearance.
>
> One way would be to render a single vertical line into a tall, narrow
> offscreen image buffer then copy that buffer into multiple locations
> on the screen.
>
> Another way would be for me to render the entire view in an offscreen
> image buffer, which I expect I could do far more efficiently than by
> making many calls into UIRectFill or even CoreGraphics.
>
> I've done lots of direct memory pixel manipulations over the years,
> that wouldn't be a problem for me to implement but before I do, do you
> think it would be a good idea?
Not until you exhaust simpler options.
Drawing a lot of rects is definitely going to be inefficient compared with drawing lines. Consider a 100 x 100 cell grid - that’s 10,000 rectangles but only 200 lines.
When I’ve drawn grids I’ve always done it by drawing lines across the whole visible width or height, and I’ve not run into major performance issues. That said, I haven’t done it on iOS, only on Mac where there’s a lot more performance in hand. One way I’ve done it which would work on iOS too is to draw lines into a CALayer then tile that layer repeatedly - not once per cell but once per n cells, where n is something useful, 100 say. That way you incur the line drawing time once but the layer is cached and is simply copied when drawn multiple times (not disimilar to your image idea but likely to be a lot faster as layers are highly optimised for performance and you’re drawing the original tile in a fast way).
That still leaves you with filling in some of the cells. Depending on how many that could be, you might not win - worst case would be a chequer pattern where no cells share a neighbour in common. But if you only colour a few cells on the grid, just draw the grid using lines and deal with the filled rects as a separate step.
Also, never draw what you don’t have to - only draw what’s visible. As you zoom in, that should eliminate huge numbers of unneeded rects.
I bet you can get a lot more performance out of Core Graphics yet.
—Graham
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden