Given an UIImage and a CGRect, what is the most efficient way (in memory and time) to draw the part of the image corresponding to the CGRect (without scaling)?
For reference, this is how I currently do it:
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect frameRect = CGRectMake(frameOrigin.x + rect.origin.x, frameOrigin.y + rect.origin.y, rect.size.width, rect.size.height);
CGImageRef imageRef = CGImageCreateWithImageInRect(image_.CGImage, frameRect);
CGContextTranslateCTM(context, 0, rect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, rect, imageRef);
CGImageRelease(imageRef);
}
Unfortunately this seems extremely slow with medium-sized images and a high setNeedsDisplay frequency. Playing with UIImageView‘s frame and clipToBounds produces better results (with less flexibility).
I guessed you are doing this to display part of an image on the screen, because you mentioned
UIImageView. And optimization problems always need defining specifically.Trust Apple for Regular UI stuff
Actually,
UIImageViewwithclipsToBoundsis one of the fastest/simplest ways to archive your goal if your goal is just clipping a rectangular region of an image (not too big). Also, you don’t need to sendsetNeedsDisplaymessage.Or you can try putting the
UIImageViewinside of an emptyUIViewand set clipping at the container view. With this technique, you can transform your image freely by settingtransformproperty in 2D (scaling, rotation, translation).If you need 3D transformation, you still can use
CALayerwithmasksToBoundsproperty, but usingCALayerwill give you very little extra performance usually not considerable.Anyway, you need to know all of the low-level details to use them properly for optimization.
Why is that one of the fastest ways?
UIViewis just a thin layer on top ofCALayerwhich is implemented on top of OpenGL which is a virtually direct interface to the GPU. This means UIKit is being accelerated by GPU.So if you use them properly (I mean, within designed limitations), it will perform as well as plain
OpenGLimplementation. If you use just a few images to display, you’ll get acceptable performance withUIViewimplementation because it can get full acceleration of underlying OpenGL (which means GPU acceleration).Anyway if you need extreme optimization for hundreds of animated sprites with finely tuned pixel shaders like in a game app, you should use OpenGL directly, because
CALayerlacks many options for optimization at lower levels. Anyway, at least for optimization of UI stuff, it’s incredibly hard to be better than Apple.Why your method is slower than
UIImageView?What you should know is all about GPU acceleration. In all of the recent computers, fast graphics performance is achieved only with GPU. Then, the point is whether the method you’re using is implemented on top of GPU or not.
IMO,
CGImagedrawing methods are not implemented with GPU.I think I read mentioning about this on Apple’s documentation, but I can’t remember where. So I’m not sure about this. Anyway I believe
CGImageis implemented in CPU because,CGImageshould be for CPU.So it seems to be done in CPU. Graphics operations done in CPU are a lot slower than in GPU.
Simply clipping an image and compositing the image layers are very simple and cheap operations for GPU (compared to CPU), so you can expect the UIKit library will utilize this because whole UIKit is implemented on top of OpenGL.
About Limitations
Because optimization is a kind of work about micro-management, specific numbers and small facts are very important. What’s the medium size? OpenGL on iOS usually limits maximum texture size to 1024×1024 pixels (maybe larger in recent releases). If your image is larger than this, it will not work, or performance will be degraded greatly (I think UIImageView is optimized for images within the limits).
If you need to display huge images with clipping, you have to use another optimization like
CATiledLayerand that’s a totally different story.And don’t go OpenGL unless you want to know every details of the OpenGL. It needs full understanding about low-level graphics and 100 times more code at least.
About Some Future
Though it is not very likely happen, but
CGImagestuffs (or anything else) doesn’t need to be stuck in CPU only. Don’t forget to check the base technology of the API which you’re using. Still, GPU stuffs are very different monster from CPU, then API guys usually explicitly and clearly mention them.