Search code examples
objective-ccocoaexifcgimage

add/pass (exif) thumbnail to CGImageDestinationRef


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.


Solution

  • 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);