Huge images in iOS app without CATiledLayer? - objective-c

I have an image at about 7000x6000px. I need this to be in a scrollview/imageView in my app, however this is way to huge for display. It is supposed to be a kind of map. I was hoping to keep the size of the app to the minimum, and the image is just about 13mb in .jpg. In .png it is over 100mb, which is unacceptable. Many have suggested CATiledLayer as an option, but I believe this would result in even bigger file sizes. Anyway, I tried to do it with CATiledLayer, and create my own tiles in TileCutter, (tiles in .jpg), and the size wasn't too bad. But I am having errors all over the place. The iOS version of CATiledLayer is a mystery to me, and I can't find a way to solve this. I get an error saying something about the java-equivalent "index out of bounds of array", even though the array has content at that specific index..
It has a method which returns an array. The array contains data of a .plist. Before the return I log out the content of the array, giving me good data. The call is trying to access
[array objectAtIndex:0]
and put it in a dictionary, but throws OutOfBounds. When logged out the whole array, I can clearly see the content, but when logged out
NSLog("%#",[method objectAtIndex]); I get the same exception.
Anyway, CATiledLayer has given me nothing but problems. I have been reverse-engineering the PhotoScroller project with no luck. Anyone have any other solutions?
Thanks.

Apple has this really neat project, PhotoScroller, that uses CATiledLayer and lets you scroll through several images and zoom them. This seemed really neat until I found out that Apple "cheated" and pre-tiled the images (about 800 tiles saved as file in the bundle!)
I had need for a similar capability, but had to download the images from the network. Thus came about PhotoScrollerNetwork. With the TiledImageBuilder you can download (or read from disk) massive images - I even tested a 18000x18000 image - and it works.
What this class does is start tiling the image as it downloads (when using libjpegturbo) or can save the file then tile (takes longer). The class figures out how many levels of detail are needed to show the image at full resolution and sized to fit in the containing view (a scrollview).
The class uses the disk cache to hold the tiles, but uses and old Unix trick of creating a file, opening it, then unlinking it so that the tiles never really get saved - once the class is dealloced (and the file descriptor closed) the tiles are freed (or if your app crashes they get freed too.
Someone had problems on an iPad 1 with its quite limited memory, so the class now throttles its use of the file system when concurrently loading multiple images. I had a discussion with the iOS kernel manager at WWDC this year, and after explaining the throttling technique to him, he said the algorithm (on managing the amount of disk cache usage) was probably the best technique that could be used.

I think those who suggested CATiledLayer are right. You should really use it! If you need a sample project that displays a huge bitmap using that technology, look here: http://www.cimgf.com/2011/03/01/subduing-catiledlayer/
Many technologies we use as Cocoa/Cocoa Touch developers stand
untouched by the faint of heart because often we simply don’t
understand them and employing them can seem a daunting task. One of
those technologies is found in Core Animation and is referred to as
the CATiledLayer. It seems like a magical sort of technology because
so much of its implementation is a bit of a black box and this fact
contributes to it being misunderstood. CATiledLayer simply provides a
way to draw very large images without incurring a severe memory hit.
This is important no matter where you’re deploying, but it especially
matters on iOS devices as memory is precious and when the OS tells you
to free up memory, you better be able to do so or your app will be
brought down. This blog post is intended to demonstrate that
CATiledLayer works as advertised and implementing it is not as hard as
it may have once seemed.

Related

Does CGBitmapContextCreate() have a size limit?

I want to make an extremely large bitmap (250,000 pixels on each side, to be eventually written out as BigTIFF). I don't see a memory size or dimensional limit anywhere in the docs, can Core Graphics handle it?
CG is not designed for that kind of workload.
(I'd be surprised if you found any general-purpose graphics framework that is, frankly. If you're pushing images that big, you're going to have to write your own code to get anything done in a reasonable amount of time.)
In my experience, images started to fail once dimensions got over 32767 or so. Not in any organized way, just crashes and hard-to-repro failures; certain parts of the API would work, others wouldn't. Things may be better in 64-bit but I wouldn't count on it.

Resizable image resource with embedded cap insets

This is by far not a showstopper problem just something I've been curious about for some time.
There is this well-known -[UIImage resizableImageWithCapInsets:] API for creating resizable images, which comes really handy when texturing variable size buttons and frames, especially on the retina iPad and especially if you have lots of those and you want to avoid bloating the app bundle with image resources.
The cap insets are typically constant for a given image, no matter what size we want to stretch it to. We can also put that this way: the cap insets are characteristic for a given image. So here is the thing: if they logically belong to the image, why don't we store them together with the image (as some kind of metadata), instead of having to specify them everywhere where we got to create a new instance?
In the daily practice, this could have serious benefits, mainly by means of eliminating the possibility of human error in the process. If the designer who creates the images could embed the appropriate cap values upon exporting in the image file itself then the developers would no longer have to write magic numbers in the code and maintain them updated each time the image changes. The resizableImage API could read and apply the caps automatically. Heck, even a category on UIImage would make do.
Thus my question is: is there any reliable way of embedding metadata in images?
I'd like to emphasize these two words:
reliable: I have already seen some entries on the optional PNG chunks but I'm afraid those are wiped out of existence once the iOS PNG optimizer kicks in. Or is there a way to prevent that? (along with letting the optimizer do its job)
embedding: I have thought of including the metadata in the filename similarly to what Apple does, i.e. "#2x", "~ipad" etc. but having kilometer-long names like "image-20.0-20.0-40.0-20.0#2x.png" just doesn't seem to be the right way.
Can anyone come up with smart solution to this?
Android has a filetype called nine-patch that is basically the pieces of the image and metadata to construct it. Perhaps a class could be made to replicate it. http://developer.android.com/reference/android/graphics/NinePatch.html

Is there a nice way to share a CCTextureAtlas among multiple CCTMXTiledMaps?

Good day, I'm new to cocos2d, objective-c and stack overflow.
I would like to know if it's possible to share a texture atlas instance among multiple tiled maps. I'm working on a map system, which is supposed to be able to use a really, really huge map but since it needs to run on an iPhone, I have to slice that map into many small ones to be able to cull them so I have multiple CCTMXTiledMaps in my scene which get constantly allocated and deallocated.
This works fine but on every allocation of a tiled map there is a CCTextureAtlas generated which freezes the app during the loading time and uses up a lot of memory even if the tile graphics are everywhere the same.
I looked around but all tutorials just cover the case with only one tiled map. I also tried some awful hacking, but that just caused crashes. I think, I have to setup a tiled map instance manually (not with the loadFromFile function) so I have more control of the initialization but I have no clue of what I have to consider during that.
If you have loaded the textures before, the tilemaps shouldn't freeze the game significantly.

Is there anyway to load a bunch of images from Resources?

Hey guys, im getting a little problem here. I am getting a Level 1 Warning from my debug.
So, i think the best solution for it is loading every images of resource, to prevent crash the app like it are doing.
So, whats the best way to do it?
Thanks!
There are several strategies to reduce memory usage if you're working with lots of images. The warning you're getting doesn't necessarily mean that main memory is running out, but you could be running low on video ram.
Reduce the image size before adding them to your project, both by scaling them down and/or compressing image data.
Load only the images you need at a particular time - avoid trying to keep all images in memory.
Load images using (UIImage)imageWithContentsOfFile: rather than (UIImage)imageNamed: (the latter reads an caches images immediately, the former is more "lazy").
Be aggressive in 'release'ing images - don't wait for autorelease to kick in, but send [image release];
Simplify other aspects of your code that use video ram (e.g. remove layer transparency, remove views that aren't currently visible, etc.)
Your question needs more details I think to get a clear answer.
Anyway, the best solution not to get a memory warning while loading "a bunch of images"...
is...
Not loading these images! Only load required images "on demand".
For example if you have images in a table view, make cells loads images only when they are visible.

OutOfMemoryException When Creating a Large Bitmap in CF.NET

My compact framework application creates a smooth-scrolling list by rendering all the items to a large bitmap surface, then copying that bitmap to an offset position on the screen so that only the appropriate items show. Older versions only rendered the items that should appear on screen at the time, but this approach was too slow for a smooth scrolling interface.
It occasionally generates an OutOfMemoryException when initially creating the large bitmap. If the user performs a soft-reset of the device and runs the application again, it is able to perform the creation without issue.
It doesn't look like this bitmap is being generated in program memory, since the application uses approximately the same amount of program memory as it did before the new smooth-scrolling methods.
Is there some way I can prevent this exception? Is there any way I can free up the memory I need (wherever it is) before the exception is thrown?
I'd suggest going back to the old mechanism of rendering only part of the data, as the size of the fully-rendered data is obviously an issue. To help prevent rendering problems I would probably pre-render a few rows above and below the current view so they can be "scrolled" in with limited impact.
And just as soon as I posted I thought of something you can do to fix your problem with the new version. The problem you have is one of CF trying to find one block of contiguous memory available for the huge bitmap, and this is occasionally a problem.
Instead of creating one big bitmap, you can instead create a collection of smaller bitmaps, one for each item, and render each item onto its own little bitmap. During display, you then just copy over the bitmaps you need. CF will have a much easier time creating a bunch of little bitmaps than one big one, and you shouldn't have any memory problems unless this is a truly enormous bunch of items.
I should avoid expressions like "there is no fix".
One other important point: make sure you call Dispose() on each bitmap when you're finished with it.
Your bitmap definitely is being created in program memory. How much memory the bitmap needs depends on how big it is, and whether or not this required size will generate the OutOfMemoryException depends on how much is available to the PDA (which makes this a randomly-occuring error).
Sorry, but this is generally an inadvisable control rendering technique (especially on the Compact Framework) for which there is no fix short of increasing the physical memory on the PDA, which isn't usually possible (and often won't fix the problem anyway, since a CF process is limited to 32MB no matter how much the device has available).
Your best bet is to go back to the old version and improve its rendering speed. There is also a simple technique available on CF for making a control double-buffered to eliminate flicker.
Since it appears you've run into a device limitation that is restricting the total size of Bitmap space you can create (these are apparently created in video RAM rather than general program memory), one alternative is to replace the big Bitmap object used here with a plain-old block of Windows memory, accessing it for reading and writing by PInvoking the BitBlt API function.
Initially creating the memory block is tricky, and you'd probably want to ask another SO question about that (GCHandle.Alloc can be used here to create a "pinned" object, which means .NET isn't allowed to move it around in memory, which is critical here). I know how to do it, but I'm not sure I do it correctly and I'd rather have an expert's input.
Once you've created the big block, you'd iterate through your items, render each to one small bitmap that you keep re-using (using your existing .NET code), and BitBlt it to the appropriate spot in your memory block.
After creating the entire cache, your rendering code should work just like before, with the difference that instead of copying from the big bitmap to your rendering surface, you BitBlt from your cache block. The arguments for BitBlt are essentially the same as for DrawImage (destination, source, coordinates and sizes etc.).
Since you're creating the cache out of regular memory this way instead of specialized video RAM, I don't think you'll run into the same problem. However, I would definitely get the block creation code working first and test to make sure it can create a big enough block every time.
Update: actually, the ideal approach would be to have a collection of smaller memory blocks rather than one big one (like I thought was the problem with the Bitmap approach), but you already have enough to do. I've worked with CF apps that deal with 5 and 10MB objects and it's not a huge problem anyway (although it might be a bigger problem when that chunk is pinned - I dunno). BTW, I've always been surprised by the OOMEs on BitMap creation because I knew the bitmaps were much smaller than the available memory, as did you - now I know why. Sorry I thought this was an easy solve at first.