2

In my app, I have to draw some images on a view, in real-time, in positions and scaling that frequently change over time. These images are a subset of those contained in a dictionary. Here is the code, a bit summarized:

-(void)drawObjects:(NSArray*)objects withImages:(NSDictionary*)images <etc> { // Get graphic context and save it CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSaveGState(context); // Display objects in reverse order for (int i = [objects count] - 1; i >= 0; i--) { MyObject *object = [objects objectAtIndex:i]; // Check if object shall be visible if (<test to check whether the image shall be visible or not>) { // Get object image UIImage* image = <retrieve image from "images", based on a key stored in "object">; // Draw image float x = <calculate image destination x>; float y = <calculate image destination y>; float imageWidth = <calculate image destination width>; float imageHeight = <calculate image destination height>; CGRect imageRect = CGRectMake(x - imageWidth / 2, y - imageHeight / 2, imageWidth, imageHeight); [image drawInRect:imageRect]; } } // Restore graphic context CGContextRestoreGState(context); } 

My problem is that this code is slow; it takes about 700 to 800 ms to perform the loop, when the number of images is about 15 (iPhone 4, iOS 4.3.5). I have experimented a bit, and it seems that the bottleneck is the drawInRect call, since if I exclude it, everything speeds up drammatically.

Does anyone have suggestions to offer?

7
  • Have you tried using a UIImageView and setting its frame? I don't have detailed insight into the iOS rendering pipeline, but that kind of recomposition might be faster than rendering the image anew every time. Commented Jan 18, 2012 at 16:11
  • Just an idea. Have you tried creating a context, off-screen, draw into it, and then bring it back on-screen, (UIGraphicsPopContext and UIGraphicsPushContext) and doing this whole drawing in background, using GCD? Commented Jan 18, 2012 at 16:29
  • Can drawing even occur in a background thread? UIKit can't draw in background. Commented Jan 18, 2012 at 16:32
  • The problem is (I think) that you are actually drawing the image in every frame. But you don't need that. You only want to draw it once, and then transform it afterwards. UIKit makes this easy for you: Just set the frame for a UIImageView, and the image will be displayed where and how large you want it, (probably) without needing to re-render the image data. Commented Jan 18, 2012 at 16:57
  • If using UIImageViews alleviates the problem, you can probably optimize this further by storing the UIImageViews themselves, so that they don't have to be recreated every time they need to be displayed. Commented Jan 18, 2012 at 16:58

2 Answers 2

2

The problem is (I think) that you are actually drawing (creating the screen representation) the image in every frame, because drawInRect: has no idea that you want to reuse the image that you displayed in the last frame. But you don't need that. You only want to draw it once, and then transform it afterwards to change its position and size.

UIKit makes this easy for you: Just set the frame for a UIImageView, and the image will be displayed where and how large you want it, without needing to re-render the image data.

You can probably optimize this further by storing the UIImageViews themselves, so that they don't have to be recreated every time they need to be displayed. Istead just set their hidden property accordingly.

Sign up to request clarification or add additional context in comments.

Comments

0

Your number one answer: run Instruments and see what part of this is actually taking time. (Yes, chances are it's the drawInRect: but it could be the "get the image" you didn't show).

3 Comments

I did make some measurements prior to posting the question; so I can confirm my statements. Thank you for suggesting, anyway. Now I'm using UIImageView instead of UIImage, and pre-storing them, as suggested by fzwo, and it is working great.
Ah, maybe I missed that part of your question. UIImageView is definitely optimized for quick image drawing (I imagine it uses CALayer directly). BTW, you can draw in a thread, but it would need to be into a bitmap context. I don't think that would save you anything here, because you would still have to draw it.
@GiorgioBarchiesi, Glad to have been of help :)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.