PrEV
Thoughts from a NeXTStep Guy on Cocoa Development

TiledLayer on the iPhone

Dec 08, 2008 by Bill Dudney

I finally got the tiled layer example ported to the phone. The SFMuni map was killing performance and takes forever to draw so I did a simple keynote slide with some text and a few shapes so it would not be too simple. This slide PDF draws very fast on my 3G iPhone. You can grab the code here. If you want to go back to the SFMuni map, just change the code in the sfMuni method of TiledDelegate class.

As the comments in the code say this is not an attempt to teach you how to handle events. I just hacked some stuff together enough to get zooming and panning working well enough to experiment with the pdf drawing so use it as that.

I hope it helps!



Comments:

Bill,

Sorry, but what is this TiledLayer example you are referring to? Looks like I'm missing some context here as to what this is related to.

--Joao

Posted by Joao Prado Maia on December 08, 2008 at 04:37 PM MST #

Sorry Joao,

I've updated the entry now to have a link to the old entry.

HTH!

Posted by bill Dudney on December 08, 2008 at 04:42 PM MST #

Hi Bill,

While the layer works fine when using [UIImage imageNamed:...].CGImage in the TiledDelegate, if that is replaced with a CGImageRef made by calling CGImageCreateUsingJPEGDataProvider, the tiles load painfully slow.

The obvious choice is just to use .CGImage, but in my case I need to be able to release the CGImage from memory when I'm not using it but then bring it back again when I need it. CGImageRelease will cause it to release permanently. Any ideas?

Thanks,
Dan

Posted by Dan on February 03, 2009 at 04:02 AM MST #

Hi Dan,

Loading a JPEG requires a conversion to PNG behind the scenes so that could be a big part of the slow down. Also make sure you are loading only what you need and not a whole huge image.

Good Luck!

Posted by Bill Dudney on February 03, 2009 at 04:07 AM MST #

Bill Dudney said:
Also make sure you are loading only what you need and not a whole huge image.

How do you load a part of a huge image? I haven't seen a method in UIImage that would do anything like that. What method would you be referring to that can load only portions of an image?

Posted by Issac on April 10, 2009 at 01:44 PM MDT #

Hi Issac,

You have to use Core Graphics routines, google 'loading large images with core graphics' and you should see some stuff that will help.

Good luck!

Posted by Bill Dudney on April 11, 2009 at 02:35 AM MDT #

Bill,

so you have a conversion to the iPhone of the CATiled example in the book drawn with slices?

Thanks

Michele

Posted by Michele on April 22, 2009 at 02:42 AM MDT #

Bill,

After going through your code I started wondering why you made the tiled layer a sublayer of the UIView's layer versus making the UIView backing layer tiled (by using +layerClass)? In my simple explorations, it looks like you can proceed down the +layerClass path and not incur the overhead of two fully overlapping layers.

Do you have any thoughts or comments on why I should avoid using +layerClass?

Andrew

Posted by Andrew on June 29, 2009 at 05:56 AM MDT #

Hi Andrew,

All UIViews are layer backed on the phone, wether you implement +layerClass or not. Also the overhead for adding an additional layer is almost negligible. That said, you should more or less leave the layer that is owned by the view alone. There is a special relationship between a view and its layer and if you start manipulating the layer you can introduces some strange behavior.

Much better to add a sublayer.

Posted by Bill Dudney on June 29, 2009 at 06:01 AM MDT #

Bill,

Thank you for your reply and I have another question.

You say the resource cost of a second sublayer is negligible. Yet, layer image allocations do not appear to show up in Instruments. Is there a way to actually find out how much a layer is costing me? I'm not doubting your statement. I just want to know how to figure these things out myself?

Andrew

Posted by Andrew on June 30, 2009 at 12:23 PM MDT #

You could try using the object allocation instrument and just make a tight loop to create 1000 layers, that should give you a nice bump in memory usage that would help you see the memory each layer takes.

Have you used/looked at the core animation instrument it has lots of nobs and dials, perhaps one of them will let you see the resources an individual layer is using.

Good luck!

Posted by Bill Dudney on June 30, 2009 at 12:26 PM MDT #

Hey Bill,

I've read what I can find about CATiledLayer, but I still have some questions. Here are some assumptions, which I think are correct (but I hope you'll correct me if they aren't), and some questions.

CALayer is useful for two very distinct purposes:
1) For apps where you actually need to load tiles asynchronously (e.g. Google Maps)
2) For showing content that won't fit into a single texture (maximum texture size is GPU dependent), e.g. a high-res image or large pdf rendering.

The way CATilesLayer works is that it calls drawLayer:inContext: with a context that has clip bounds and CTM set. You can figure out which tile ({x,y}) is requested, if you need to, from the clip rect. CTM takes care of automatic scaling, so you always draw in 1:1 size.

In the most trivial implementation drawLayer:inContext: can just draw the entire image onto the layer and only the relevant part of it will actually be drawn, since the clip box is in place.

CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image);

If you can't fit the entire image in memory (size is ~ w*h*4 bytes) or you want better performance*, you can draw only the rect that fits into the clip box.

(* during my testing with a png image that _does_ fit into memory, cutting it with CGImageCreateWithImageInRect resulted in performance a bit slower than just drawing it all. I obtained an image with [UIImage imageNamed:...].CGImage. It was a png image. Maybe it was getting decoded again and again, but if that was the case, I would expect cutting it to be much much slower)

[question #1]
When drawing PDFs with CGContextDrawPDFPage (like in your example), you don't specify the rectangle. Does that mean that:
a) the entire pdf is drawn for each tile and what's outside of it is just clipped and thrown away, or
b) CG is smart enough to only draw a part of a PDF?

[question #2]
Is levelsOfDetail/levelsOfDetailBias only relevant for caching/performance, or does it actually affect the image displayed? E.g. if you have levels of detail for zoom levels 25%, 50% and 100%, does this mean that the image can still be drawn at 40% (in which case it will downsize from 50%) and 70% (in which case it will downsize from 100%)?

If not, how are we supposed to set lod/lodBias for CATiledLayers which we want the user to be able to zoom to an arbitrary level (e.g. using pinching on the iPhone)?

Jaka

Posted by Jaka Jan?ar on July 09, 2009 at 09:26 AM MDT #

Hi Jaka,

Your understanding sounds good.

Q1 - CG is very smart :)

Q2 - it downsizes and upsizes depending on the situation

Hope this helps!

Posted by Bill Dudney on July 09, 2009 at 09:34 AM MDT #

Hi
thanks for the great sample. I just used it in a TableView. Sometimes you have the problem of slow drawing in TableCells, in my case a part of a big PDF. By using a TiledLayer für the whole drawing part you can push drawing to the background easily. I think UIImage does it for images loaded from a web-url.

Posted by daniel9000 on November 04, 2009 at 07:43 PM MST #

Hi,

Do you know how to implement a CATiledLayer with a UIWebVIew?

Thanks in advance,
Frankai

Posted by frankai on August 15, 2010 at 09:41 PM MDT #

Hi

I am trying to use CATiledLayer with a huge image for iPhone similar to what you have done for MacOSX. Your example for iPhone works with a PDF. I am using the same code as a template and trying to modify it to make it work with images. When I try to do this, it only loads a part of the huge image on the screen and then crashes. Can you please post a tutorial on how to do it with images for iPhone.

Thank you

Posted by Kayvee on August 15, 2010 at 09:41 PM MDT #

Post a Comment:
  • HTML Syntax: Allowed