8

I'm trying to use CGImageSourceCreateThumbnailAtIndex to efficiently create a resized version of an image. I have some existing code that does this with images from disk, and now I'm trying to use an image that comes from ALAssetsLibrary.

Here's my code:

ALAsset *asset;
ALAssetRepresentation *representation = [asset defaultRepresentation];
CGImageRef imageRef = [representation fullResolutionImage];

CGDataProviderRef provider = CGImageGetDataProvider(imageRef);
CGImageSourceRef sourceRef = CGImageSourceCreateWithDataProvider(provider, NULL);

NSDictionary *resizeOptions = @{
  kCGImageSourceCreateThumbnailWithTransform : @YES,
  kCGImageSourceCreateThumbnailFromImageAlways : @YES,
  kCGImageSourceThumbnailMaxPixelSize : @(2100) 
};

CGImageRef resizedImage = CGImageSourceCreateThumbnailAtIndex(source, 0, resizeOptions);

The problem is that resizedImage is null, and CGImageSourceGetCount(sourceRef) returns 0. The data provider does have quite a bit of data in it, though, and the data does appear to be valid image data. The ALAsset comes from an iPhone 4S camera roll.

What am I missing? Why does CGImageSourceCreateWithDataProvider() create an image source with 0 images?

BJ Homer
  • 48,806
  • 11
  • 116
  • 129
  • By the way, putting the non-object literal `YES` into your dictionary literal is going to go badly. You want `@YES`. – Peter Hosey Jun 27 '13 at 00:27
  • Oh, true. It's actually kCFBooleanTrue in my code, but I didn't want to show all the casting here. I'll fix that. – BJ Homer Jun 27 '13 at 04:08

4 Answers4

4

CGImageSource is for deserializing serialized images, such as JPEGs, PNGs, and whatnot.

CGImageGetDataProvider returns (the provider of) the raw pixel data of the image. It does not return serialized bytes in some external format. CGImageSource has no way to know what pixel format (color space, bits-per-component, alpha layout, etc.) any given raw pixel data is in.

You could try getting the URL of the asset rep and giving that to CGImageSourceCreateWithURL. If that doesn't work (e.g., not a file URL), you'll have to run the image through a CGImageDestination and create a CGImageSource with wherever you put the output.

(The one other thing to try would be to see whether the rep's filename is actually a full path, the way Cocoa often misuses the term. But you probably shouldn't count on that.)

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • Ah. This makes sense. And indeed, if I get the serialized data from the `ALAssetRepresentation` via `-getBytes:length:offset:error:` and make an image source out of that, it all works fine. – BJ Homer Jun 27 '13 at 15:54
  • @BJHomer: Actually, I'd assumed that that returns pixels; if it returns some external format (which makes sense given the existence of `representationForUTI:`), then you probably should just use that. – Peter Hosey Jun 27 '13 at 16:52
  • @HeathBorders: My guess would be for CGImages you created with a data provider, in case you want to retrieve the data provider you created the image with. But even then, I don't know why you would do that. – Peter Hosey Jun 10 '16 at 06:05
2

One thing you might try is the asset rep's CGImageWithOptions: method.

The documentation claims that:

This method returns the biggest, best representation available, unadjusted in any way.

But it says that about fullResolutionImage, too, and I'm not sure why this class would have both methods if they both do the same thing. I wonder if it's a copy-and-paste error.

Try CGImageWithOptions: with a bunch of thumbnail-creating options and see what happens.

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • This would probably work; in my case, I have existing code that applies the correct options and handles some other processing, so I really just wanted to pass a `CGImageSourceRef` off to that existing code rather than duplicating it. But in another case, this would likely make sense. – BJ Homer Jun 27 '13 at 15:55
1

Option #3 would be the rep's fullScreenImage. Depending on what sort of “thumbnail” you need, it may be cheaper and/or simpler to just use this, which will be no bigger than (approximately) the size of the device's screen.

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
0

This can also help...

ALAssetRepresentation* rep = [asset defaultRepresentation];

NSDictionary* options = [[NSDictionary alloc] initWithObjectsAndKeys:
                     (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform,
                     (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageAlways, 
                     (id)[NSNumber numberWithDouble:400], (id)kCGImageSourceThumbnailMaxPixelSize, 
                     nil];

CGImageRef image = [rep CGImageWithOptions:options];
Axel Guilmin
  • 11,454
  • 9
  • 54
  • 64