7

I am using ALAsset to retrieve images like that:

[[asset defaultRepresentation] fullResolutionImage]]

This return CGImageRef which I want to save to disk as fast as possible...

Solution 1:

UIImage *currentImage = [UIImage imageWithCGImage:[[asset defaultRepresentation] fullResolutionImage]];
NSData *currentImageData = UIImagePNGRepresentation(currentImage);
[currentImageData writeToFile:filePath atomically:YES];

Solution 2:

CFURLRef url = (__bridge CFURLRef)[NSURL fileURLWithPath:filePath];
CGImageDestinationRef destination = CGImageDestinationCreateWithURL(url, kUTTypePNG, 1, NULL);
CGImageDestinationAddImage(destination, [[asset defaultRepresentation] fullResolutionImage], nil);
CGImageDestinationFinalize(destination);

The problem is that both methods are very slow performing on a device. I takes about 2 seconds per image to perform this. And this is absolutely to long.

Question: How can I speed up this image saving process? Or perhaps is there a better solution for this?

UPDATE: The best performance improvements in both solutions is to save images to JPEG format instead of PNG. So for solution 1 have replaced UIImagePNGRepresentation with UIImageJPEGRepresentation. For solution 2 have replaced kUTTypePNG with kUTTypeJPEG.

Also worth noting that second solution is way more memory efficient that first one.

Borut Tomazin
  • 8,041
  • 11
  • 78
  • 91

3 Answers3

9

You could just copy the raw data instead.
This has the advantages of not re-encoding the file, not making the file bigger, not losing quality through additional compression and preserving any meta data in the file. Should also be the fastest possible way.
Assuming you have theAsset and a filepath to save it to.
Should add error handling as well.

long long sizeOfRawDataInBytes = [[theAsset defaultRepresentation] size];
NSMutableData* rawData = [NSMutableData dataWithLength:(NSUInteger) sizeOfRawDataInBytes];
void* bufferPointer = [rawData mutableBytes];
NSError* error=nil;
[[theAsset defaultRepresentation] getBytes:bufferPointer 
                                fromOffset:0
                                    length:sizeOfRawDataInBytes
                                     error:&error];
if (error) 
{
    NSLog(@"Getting bytes failed with error: %@",error);
}
else 
{
    [rawData writeToFile:filepath 
              atomically:YES];
}
Brian Lee
  • 3
  • 2
GusOst
  • 4,105
  • 3
  • 19
  • 28
2

That's because of PNG compression is slow process, and takes a while on iPhone's processor, especially for full-size photography.

Nickolay Olshevsky
  • 13,706
  • 1
  • 34
  • 48
  • I understand that. But is there any other, more efficient way to do this? I don't need PNG. Actually any format is ok. I just need to copy original image from ALAsset to my custom folder... ? Thanks – Borut Tomazin Feb 13 '12 at 12:54
  • I just tried with JPEG representation - it's about one half faster than PNG representation (still about 1 second). This is much better. But still not what I've expected. Is there anything else? What about the difference between both solutions I posted in question. Has either any advantages over another? – Borut Tomazin Feb 13 '12 at 13:04
  • The most time-consuming operation is compression of the image, so both approaches should take approximately the same time. If you need only one image, you can save CGImage's bytes, and load them after so compression will be omited. – Nickolay Olshevsky Feb 13 '12 at 13:13
  • No, I will be saving more than one image. Do you have any example? Also, I don't need compression. Original images is ok... – Borut Tomazin Feb 13 '12 at 13:16
  • I do not have an example, look at this question for instance: http://stackoverflow.com/questions/1579631/converting-rgb-data-into-a-bitmap-in-objective-c-cocoa – Nickolay Olshevsky Feb 13 '12 at 13:24
  • Ok, I will look at this later. But now I have just run some test between Solution 1 and Solution 2. Second one is about one half more memory efficient than first one. Over 30 images the first one consumes about 25 MB of ram where second uses only 12 MB of ram... – Borut Tomazin Feb 13 '12 at 13:58
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/7625/discussion-between-borut-tomazin-and-nickolay-o) – Borut Tomazin Feb 13 '12 at 14:03
1

There is no such a way to speed the process up, but a possible solution would be to use threads, this way you will not block the mainthread and you will offer the user a better experience:

dispatch_queue_t dispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(dispatchQueue, ^(void) {
    // Your code here
});
Xabier Arrabal
  • 180
  • 2
  • 8