Improving Finger Painting Performance

2019-02-11 08:47发布

问题:

Simple Painting:
Fingering the iPhone screen paints temporary graphics to a UIView. These temporary graphics are erased after a touch ends, and are then stored into an underlying UIView.

The process is simple:
1) Touch Starts & Moves >>
2) Paint Temporary Graphics on top UIView>>
3) Touch Ends >>
4) Pass Temporary Graphics To underlying UIView >>
5) Underlying UIView adds Temporary Graphics to Stored Graphics >>
6) Underlying UIView Re-Draws all Stored Graphics >>
7) Delete Temporary Graphics on top UIView.

In this manner, I can accumulate graphics on the underlying UIView while maintaining responsive painting of the temporary graphics on the top UIView.

(Sidenote: Each "Drawing" is simply an NSArray of custom "Point" Objects which are just NSObject containers for CGPoints. And the underlying UIView has a seperate NSArray, where it stores these NSArrays of CGPoints)

The Problem Is:
When a great deal of graphics has accumulated on the underlying UIView, it takes time to draw it all out on the screen. And any new drawings on the top UIView will not be displayed until the drawing of the underlying stored graphics is complete. Thus, there is a noticeable lag when many graphics are on the screen.

Question:
Can anyone think of a good way to improve performance here, so that there is no noticable lag between drawings when there are a lot of graphics on the screen?

回答1:

An NSArray of CGPoints? You mean an NSArray of NSValues holding CGPoints? That's an incredibly time-expensive way to hold what has to be a huge number of values that you access constantly. You could store this information in many better ways. A 2-dimensional C-array representing the entire screen is the most obvious. You may also want to look into bitmap image representations, and draw directly into a CGImage rather than maintaining a bunch of CGPoints. Take a look at the Quartz 2D Programming Guide.

EDIT:

Your object (below) is the equivalent of an NSValue, just a little more specialized. There's a lot of overhead going on here when you have many, many objects (~100,000 I'm guessing when the screen is nearly full; more if you're not removing duplicates; run Instruments to profile it). Old-style C data structures are likely to be much faster for this, because you can avoid all the retains/releases, allocations, etc. There are other options, though. Duplicate point checking would be much faster with an NSMutableSet if you pixel-align your CGPoints, and overload -isEqual on your Point object.

Do make sure you're pixel-aligning your data. Drawing on fractional pixels (and storing them all), could dramatically increase the number of objects involved and the amount of drawing you're doing. Even if you want the anti-aliasing, at least round the pixels to .5 (or .25 or something). A CGPoint is made up of two doubles. You don't need that kind of precision to draw to the screen.



回答2:

Why not just draw everything onto a CGBitmapContextRef buffer so the drawing operations will accumulate, and then draw that to the screen in your drawRect:? You will be able to perform arbitrary graphics operations without slowing down as the total number of operations increases.

If undo support is necessary, one could always keep a copy for each change made and invalidate the oldest copies when a memory warning is received. (or for an even fancier solution, store the operations as you do now, but keep a cached copy every dozen user operations or so)