8

On CGImageDestinationRef Apple's reference it mentions that It can contain thumbnail images as well as properties for each image. The part with the properties works nice, by setting the properties parameter when adding a image to the CGImageDestinationRef, but I can't figure out how to add the thumbnail.

I want to add a thumbnail (or pass it directly from a CGImageSourceRef) to CGImageDestinationRef, such that when the resulting image is opened with CGImageSourceRef I can use CGImageSourceCreateThumbnailAtIndex to retrieve the original thumbnail.

NSDictionary* thumbOpts = [NSDictionary dictionaryWithObjectsAndKeys:
                                       (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform,
                                       (id)kCFBooleanFalse, (id)kCGImageSourceCreateThumbnailFromImageIfAbsent,
                                       [NSNumber numberWithInt:128], (id)kCGImageSourceThumbnailMaxPixelSize, 
                                       nil];
CGImageRef thumb = CGImageSourceCreateThumbnailAtIndex(iSource, 0, (CFDictionaryRef)thumbOpts);

The code above returns the thumbnail of the images that have it stored, but when I pass the image through CGImageDestinationRef, the thumbnail is lost.

Is there some way to keep the original thumbnail (or create a new one)? CGImageProperties Reference doesn't seem to have any keys where to store the thumbnail.

alex-i
  • 5,406
  • 2
  • 36
  • 56

3 Answers3

1

One way I've found to get a thumbnail back is to specify kCGImageSourceCreateThumbnailFromImageIfAbsent in the settings dictionary.

Specify the dictionary as follows

NSDictionary* thumbOpts = [NSDictionary dictionaryWithObjectsAndKeys:
                               (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageIfAbsent,
                               (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform,
                               [NSNumber numberWithInt:128], (id)kCGImageSourceThumbnailMaxPixelSize,
                               nil];

Update: In order to add a secondary (thumbnail) image to the original CGImageDestinationRef do the following after you've added the primary image

size_t thumbWidth = 640;
size_t thumbHeight = imageHeight / (imageWidth / thumbWidth);
size_t thumbBytesPerRow = thumbWidth * 4;
size_t thumbTotalBytes = thumbBytesPerRow * thumbHeight;
void *thumbData = malloc(thumbTotalBytes);
CGContextRef thumbContext = CGBitmapContextCreate(thumbData,
                                                  thumbWidth,
                                                  thumbHeight,
                                                  8,
                                                  thumbBytesPerRow,
                                                  CGColorSpaceCreateDeviceRGB(),
                                                  kCGImageAlphaNoneSkipFirst);

CGRect thumbRect = CGRectMake(0.f, 0.f, thumbWidth, thumbHeight);
CGContextDrawImage(thumbContext, thumbRect, fullImage);
CGImageRef thumbImage = CGBitmapContextCreateImage(thumbContext);
CGContextRelease(thumbContext);
CGImageDestinationAddImage(destination, thumbImage, nil);
CGImageRelease(thumbImage);

Then in order to get the thumbnail back out, do the following

CFURLRef imageFileURLRef = (__bridge CFURLRef)url;
NSDictionary* sourceOptions = @{(id)kCGImageSourceShouldCache: (id)kCFBooleanFalse,
                                    (id)kCGImageSourceTypeIdentifierHint: (id)kUTTypeTIFF};
CFDictionaryRef sourceOptionsRef = (__bridge CFDictionaryRef)sourceOptions;
CGImageSourceRef imageSource = CGImageSourceCreateWithURL(imageFileURLRef, sourceOptionsRef);
CGImageRef thumb = CGImageSourceCreateImageAtIndex(imageSource, 1, sourceOptionsRef);
UIImage *thumbImage = [[UIImage alloc] initWithCGImage:thumb];
CFRelease(imageSource);
CGImageRelease(thumb);
  • This is pretty much the same code I've added in the question, for getting the thumbnail. The question asks on how to set the thumbnail in `CGImageDestinationRef`. – alex-i Apr 29 '15 at 05:40
  • The options dictionary for CGImageSourceCreateThumbnailAtIndex is for getting a thumbnail back out of the CGImageDestination. Setting kCGImageSourceCreateThumbnailFromImageIfAbsent to kCFBooleanTrue will cause a thumbnail to be created if it is not present. If you are trying to add a thumbnail image to the CGImageDestination my code snippet and the snippet you posted in your original question are irrelevant. Please see my updated answer on how to add a thumbnail and retrieve it. – Nick Mcdonald May 05 '15 at 06:10
  • This looks like a decent workaround. However, this will only work for setting and loading the thumbnail from my own app. Other apps will most probably try to get the cached thumbnail, not read the image from index 1. This may also cause other issues if the image container contains more than 1 image. – alex-i May 09 '15 at 10:04
1

Ok, so since no one responded (even during bounty), plus after the hours I spent trying to pass the thumbnail, my guess is that the documentation for CGImageDestination is misleading. There doesn't seem to be any (documented) way to pass the image thumbnail to a CGImageDestinationRef (atleast not up to, and including, OSX 10.7.0 and iOS 5.0).

If anyone thinks otherwise, post an answer and I'll accept it (if it's correct).

alex-i
  • 5,406
  • 2
  • 36
  • 56
0

There is a half-documented property for embedding EXIF thumbnails on iOS 8.0+: kCGImageDestinationEmbedThumbnail.

Apple's developer doc is empty, but the header file (CGImageDestination.h) contains the following comment:

/* Enable or disable thumbnail embedding for JPEG and HEIF.
 * The value should be kCFBooleanTrue or kCFBooleanFalse. Defaults to kCFBooleanFalse */

IMAGEIO_EXTERN const CFStringRef kCGImageDestinationEmbedThumbnail  IMAGEIO_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0);

However you can't specify arbitrary thumbail data, it will get automatically created for you.

Jan Berkel
  • 3,373
  • 1
  • 30
  • 23