From 122f698e7c9daf5f69d96c1794d34cf11a3fc7b9 Mon Sep 17 00:00:00 2001 From: "Michael J. Rubinsky" Date: Sat, 8 Aug 2009 22:51:02 -0400 Subject: [PATCH] First round of changes to refactor export code to use Quartz and CoreImage Use Quartz and CI routines to get the image metadata out of the image before scaling the image (which nukes the metadata). Use Quartz/CI to add select IPTC fields to the image such as keywords/description etc... Still need to refactor the scaling code to use CoreImage routines (maybe the Lanczos transform filter?) instead of the current (wasteful) quicktime solution. This change not only allows us to embed keywords/description etc.. into the IPTC data sent to Ansel, but it also will preserve much more of the metadata the the QT routine did. --- iPhoto2Ansel/AnselExportController.m | 87 ++++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 9 deletions(-) diff --git a/iPhoto2Ansel/AnselExportController.m b/iPhoto2Ansel/AnselExportController.m index cc9db658f..73fec5559 100644 --- a/iPhoto2Ansel/AnselExportController.m +++ b/iPhoto2Ansel/AnselExportController.m @@ -546,8 +546,57 @@ NSString * const TURAnselServerPasswordKey = @"password"; withObject: [NSNumber numberWithDouble: progressPercent] waitUntilDone: NO]; - // Prepare the image data + /*** Pull out (and generate) all desired metadata before rescaling the image ***/ + // The CGImageSource for getting the image INTO Quartz + CGImageSourceRef source; + + // Dictionary to hold all metadata + NSMutableDictionary *metadata; + + // Read the image data into Quartz + NSURL *url = [NSURL fileURLWithPath: [mExportMgr imagePathAtIndex:i]]; + source = CGImageSourceCreateWithURL((CFURLRef)url, NULL); + + // Prepare to get the data OUT of Quartz + NSData *data = [NSMutableData data]; + CGImageDestinationRef destination = CGImageDestinationCreateWithData((CFMutableDataRef)data, (CFStringRef)@"public.jpeg", 1, NULL); + + // Get the metadata dictionary, cast it to NSDictionary the get a mutable copy of it + CFDictionaryRef metadataRef = CGImageSourceCopyPropertiesAtIndex(source, 0, NULL); + NSDictionary *immutableMetadata = (NSDictionary *)metadataRef; + metadata = [immutableMetadata mutableCopy]; + CFRelease(metadataRef); + + // Get a mutable copy of the IPTC Dictionary for the image...create a + // new one if one doesn't exist in the image. + NSDictionary *iptcData = [metadata objectForKey:(NSString *)kCGImagePropertyIPTCDictionary]; + NSMutableDictionary *iptcDict = [iptcData mutableCopy]; + if (!iptcDict) { + iptcDict = [[NSMutableDictionary alloc] init]; + } + + // Get the keywords from the image and put it into the dictionary...should we check for them first? + NSArray *keywords = [mExportMgr imageKeywordsAtIndex: i]; + [iptcDict setObject:keywords forKey:(NSString *)kCGImagePropertyIPTCKeywords]; + + // Put the IPTC Dictionary (back?) into the metadata dictionary + [metadata setObject:iptcDict forKey:(NSString *)kCGImagePropertyIPTCDictionary]; + + // Get the data out of quartz (image data is in *data now. + CGImageDestinationAddImageFromSource(destination, source, 0, (CFDictionaryRef)metadata); + BOOL success = CGImageDestinationFinalize(destination); + + CFRelease(source); + CFRelease(destination); + + if (!success) { + // ?? + } + + /*** TODO: This is wasteful, but do it this way for now to just test if this works as expected ***/ + // Need to resize the image, but all metadata is lost...ideally we should only read the image in once though... NSData *theImage = [[NSData alloc] initWithContentsOfFile: [mExportMgr imagePathAtIndex:i]]; + //NSData *theImage = (NSData *)data; CGFloat imageSize; switch([mSizePopUp selectedTag]) @@ -571,20 +620,39 @@ NSString * const TURAnselServerPasswordKey = @"password"; [self postProgressStatus: [NSString stringWithFormat: @"Resizing image %d out of %d", (i+1), count]]; - NSData *scaledData = [ImageResizer getScaledImageFromData: theImage - toSize: NSMakeSize(imageSize, imageSize)]; - + + // Don't resize if we want original image...it will lose some metadata needlessly. + NSData *scaledData; + if ([mSizePopUp selectedTag] != 3) { + scaledData = [ImageResizer getScaledImageFromData: theImage + toSize: NSMakeSize(imageSize, imageSize)]; + } else { + scaledData = theImage; + } + + // Now we have resized image data, put back the metadata... + source = CGImageSourceCreateWithData((CFDataRef)scaledData, NULL); + + // Should we release, or clear or use a new data object? + NSData *newData = [NSMutableData data]; + destination = CGImageDestinationCreateWithData((CFMutableDataRef)newData, (CFStringRef)@"public.jpeg", 1, NULL); + + // Get the data out of quartz (image data is in the NSData *data object now. + CGImageDestinationAddImageFromSource(destination, source, 0, (CFDictionaryRef)metadata); + success = CGImageDestinationFinalize(destination); // write metadata into the data object + [self postProgressStatus: [NSString stringWithFormat: @"Encoding image %d out of %d", (i+1), count]]; - NSString *base64ImageData = [NSString base64StringFromData: scaledData - length: [scaledData length]]; +// NSString *base64ImageData = [NSString base64StringFromData: scaledData +// length: [scaledData length]]; + NSString *base64ImageData = [NSString base64StringFromData: newData + length: [newData length]]; // Get the filename/path for this image. This returns either the most // recent version of the image, the original, or (if RAW) the jpeg - // version of the original. Still need to figure out how to modify - // the image size/quality etc... when not doing a file export. + // version of the original. NSString *filename = [mExportMgr imageFileNameAtIndex:i]; NSString *imageDescription = [mExportMgr imageTitleAtIndex:i]; - NSArray *keywords = [mExportMgr imageKeywordsAtIndex: i]; + // NSArray *keywords = [mExportMgr imageKeywordsAtIndex: i]; NSArray *keys = [[NSArray alloc] initWithObjects: @"filename", @"description", @"data", @"type", @"tags", nil]; @@ -612,6 +680,7 @@ NSString * const TURAnselServerPasswordKey = @"password"; [values release]; [imageData release]; [params release]; + [iptcDict release]; [pool release]; i++; } -- 2.11.0