views:

986

answers:

7

Hi all,

When a PNG is added to an XCode iPhone project, the compiler optimizes it using pngcrush. Once on the device, the image's rendering performance is very fast.

My problem is that my application downloads its PNGs from an external source at runtime (from Picasa Web albums, using the Google Data APIs). Unfortunately, these images' performance is quite bad. When I do custom rendering on top of the image, it seems 100x slower than its internally stored counterparts. I strongly suspect this is because the downloaded images haven't been optimized.

Does anyone know how I can optimize an externally downloaded PNG at runtime on the iPhone? I'm hoping for a class that does this. I even considered adding pngcrush's source code to my app, which seems drastic. I haven't been able to find an decent answer myself. I'd be very grateful for any help.

Thanks!

Update: Some folks have suggested that it may be due to the file's size, but it isn't. During my tests, I added a toggle button to switch between using the embedded version and the downloaded version of exactly the same PNG. The only difference is that the embedded one was optimized by 'pngcrush' during compilation. This does some byte-swapping (from RGBA to BRGA) and pre-multiplication of alpha. (http://iphonedevelopment.blogspot.com/2008/10/iphone-optimized-pngs.html)

Also, the performance I'm referring to isn't the downloading, but the rendering. I superimpose custom painting on top of the image (overriding the drawRect method of the UIView), and it's very choppy when the background is the downloaded version, and very smooth when it's the embedded (and therefore optimized) version. Again, it's exactly the same file. The only difference is the optimization, which I'm hoping I can perform on the image at runtime, on the device, after downloading it.

Thanks again for everyone's help!

+1  A: 

Are you storing the png at the original downloaded size? If it's a large image it'll take significantly longer to render.

Andrew Grant
Good thought, but the downloaded image is exactly the same size and resolution as the embedded one. As a matter of fact, it's exactly the same image.
Dan Bourque
+1  A: 

On the surface, it sounds like something else is at play here. Any additional image manipulation should only add time until it's displayed onscreen...

Would it be at all possible to get the server to gzip the images by sending the appropriate HTTP header? (If it even helps file size much, that is.)

Temporarily using the pngcrush source might be a good test as well, just to get some measurements.

Collin Allen
Thanks for your advice; I think I will try using the pngcrush just to test the concept.Downloading the file from the server and displaying it is pretty fast, and only needs to occur once. The performance problems are when I superimpose custom renderings on top of that UIImage.
Dan Bourque
Hmm, it must be all that additional layering that's killing performance. Is it at all possible to merge the superimposed images into one overlay? Currently, I'm doing something similar with a JPG with a single overlaid "cover" PNG, and performance is quite good.
Collin Allen
Well, the thing is that performance if very good when using the optimized version of the graphic, and very poor when using the downloaded version of exactly the same file. This is why I'm convinced layering has nothing to do with it.
Dan Bourque
Ah, good point! I don't suppose you have control over the server side (to pre-crush the PNGs)...
Collin Allen
+1  A: 

The fact that you say it "seems" 100x slower indicates that you have not performed any experimentation, but made a guess (it must be the PNG optimization), and are now going down a path based on a hunch.

You should spend time to confirm what the problem is before you try to solve it. My gut says that PNG optimization shouldn't be the issue: that mostly affects the loading of images, but once they are in memory it doesn't matter what file format they were originally in.

Anyway, you should try an A-B comparison, either get your code to load an optimized PNG from somewhere else and see how it compares, or make a test app that just does some drawing on the two PNG types. Once you've confirmed what the problem is, then you can figure out if you need to compile pngcrush into your app.

benzado
Heh, I have done that very comparison. I have a toggle button that allows me to swap the on-screen image between the downloaded version and the embedded version of the same PNG, and the difference is huge. The optimization does the following: http://tinyurl.com/6fnsmt
Dan Bourque
+1  A: 

Well it seems that a good way to do it (since you can't run pngcrush on the iPhone and expect that to speed it up) would be to make your requests through a proxy that runs pngcrush. The proxy would have nice horse power to actually give you some gain over the 100x pain you feel.

John Fricker
+2  A: 

That link you posted pretty much answers your question.

During the build process XCode pre-processes your png so it's in a format that's more friendly to the graphics chip in the iPhone.

Png's that have not been processed like this will likely use a slower rendering path, one that deals with the non-native format and the fact that the alpha must be computed separately for each color.

So you have two options;

  1. Perform the same work that pngcrush does and swap ordering/pre-multiply alpha. The speed up may be due to one or both of these.

  2. After you have loaded your image, you can "create" a new image from it. This new image should be in the iPhone's native format and so should perform faster. The downside is it could potentially take up a bit more memory.

E.g.

CGRect area = CGRectMake(0, 0, width, height);
CGSize size = area.size;
UIGraphicsBeginImageContext(size);

[oldImage drawInRect:area];

UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Andrew Grant
A: 

You say you are drawing on top of the image by overriding a UIView's drawRect: method. Are you trying to do some animation by repeatedly drawing the whole image with your custom stuff on top of it?

You might get better results if you put your custom stuff in a separate view or layer, and let the OS deal with compositing the result over the background. The OS will only update the parts of the screen that you actually change, and won't be repainting the entire image as often.

benzado
A: 

try pincrush to trans the normal png file to the crushed png file

randomdot