add/pass (exif) thumbnail to CGImageDestinationRef - objective-c

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.

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

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).

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.

Related

How to get webp image size from URL in iOS?

I tried to use CGImageSourceCreateWithURL method like below, but it seems both height and width are equal 0. Is there any other way to get webp image from url?
CGImageSourceRef source = CGImageSourceCreateWithURL((CFURLRef)imageUrl, NULL);
NSDictionary* imageHeader = (__bridge NSDictionary*) CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
NSLog(#"Image header %#",imageHeader);
NSLog(#"PixelHeight %#",[imageHeader objectForKey:#"PixelHeight"]);
NSNumber* height = [imageHeader objectForKey:#"PixelHeight"];
NSNumber* width = [imageHeader objectForKey:#"PixelWidth"];
The webp format image is not supported in iOS. You can use URLSession to fetch image data, then use it to create NSImage with SDWebImage.

Saving NSBitmapImageRep as image

I've done an exhaustive search on this and I know similar questions have been posted before about NSBitmapImageRep, but none of them seem specific to what I'm trying to do which is simply:
Read in an image from the desktop (but NOT display it)
Create an NSBitmap representation of that image
Iterate through the pixels to change some colours
Save the modified bitmap representation as a separate file
Since I've never worked with bitmaps before I thought I'd just try to create and save one first, and worry about modifying pixels later. That seemed really straightforward, but I just can't get it to work. Apart from the file saving aspect, most of the code is borrowed from another answer found on StackOverflow and shown below:
-(void)processBitmapImage:(NSString*)aFilepath
{
NSImage *theImage = [[NSImage alloc] initWithContentsOfFile:aFilepath];
if (theImage)
{
CGImageRef CGImage = [theImage CGImageForProposedRect:nil context:nil hints:nil];
NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:CGImage];
NSInteger width = [imageRep pixelsWide];
NSInteger height = [imageRep pixelsHigh];
long rowBytes = [imageRep bytesPerRow];
// above matches the original size indicating NSBitmapImageRep was created successfully
printf("WIDE pix = %ld\n", width);
printf("HIGH pix = %ld\n", height);
printf("Row bytes = %ld\n", rowBytes);
// We'll worry about this part later...
/*
unsigned char* pixels = [imageRep bitmapData];
int row, col;
for (row=0; row < height; row++)
{
// etc ...
for (col=0; col < width; col++)
{
// etc...
}
}
*/
// So, let's see if we can just SAVE the (unmodified) bitmap first ...
NSData *pngData = [imageRep representationUsingType: NSPNGFileType properties: nil];
NSString *destinationStr = [self pathForDataFile];
BOOL returnVal = [pngData writeToFile:destinationStr atomically: NO];
NSLog(#"did we succeed?:%#", (returnVal ? #"YES": #"NO")); // the writeToFile call FAILS!
[imageRep release];
}
[theImage release];
}
While I like this code for its simplicity, another potential issue down the road might be that Apple docs advise us treat bitmaps returned with 'initWithCGImage' as read-only objects…
Can anyone please tell me where I'm going wrong with this code, and how I could modify it to work. While the overall concept looks okay to my non-expert eye, I suspect I'm making a dumb mistake and overlooking something quite basic. Thanks in advance :-)
That's a fairly roundabout way to create the NSBitmapImageRep. Try creating it like this:
NSBitmapImageRep* imageRep = [NSBitmapImageRep imageRepWithContentsOfFile:aFilepath];
Of course, the above does not give you ownership of the image rep object, so don't release it at the end.

CGImageSourceCreateWithURL returns always NULL

I need to read the properties of an image without load or download it. In fact i have implemented a simple method that use the CGImageSourceCreateWithUrl for accomplish this.
My problem is that it is returning always error because seems that the imageSource is null. So what can i do for fix it?
In the NSURL object i pass urls like:
"http://www.example.com/wp-content/uploads/image.jpg" but also ALAssets library Id used to retrieve images inside the phone like "assets-library://asset/asset.JPG?id=E5F41458-962D-47DD-B5EF-E606E2A8AC7A&ext=JPG".
This is my method:
-(NSString *) getPhotoInfo:(NSString *)paths{
NSString *xmlList = #“test”;
NSURL * imageFileURL = [NSURL fileURLWithPath:paths];
NSLog(#"imageFileURL %#", imageFileURL);
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((__bridge CFURLRef)(imageFileURL), NULL);
if (imageSource == NULL) {
// Error loading image
NSLog(#"Error loading image");
}
CGFloat width = 0.0f, height = 0.0f;
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);
NSLog(#"image source %#", imageSource);
return xmlList;
}
I have saw this posts for try to fix it but nothing seems working:
CGImageSourceRef imageSource = CGImageSourceCreateWithURL returns NULL
CGImageSourceCreateWithURL with authentication
accessing UIImage properties without loading in memory the image
In my project ARC is enabled.
Thanks
If you are passing the string "http://www.example.com/wp-content/uploads/image.jpg” to -fileURLWithPath: it’s going to return nil, because that string is sure not a file path, it’s a URL string.
Think of -fileURLWithPath: as just prepending the string you pass in with “file://localhost/“...so you’d end up with a URL that looks like "file://localhost/http://www.example.com/wp-content/uploads/image.jpg”. That’s not good.
You need to call [NSURL URLWithString:paths] if you’re going to be passing in entire URL strings, not just a filesystem path strings.
Working with "assets-library://asset/asset.JPG?id.........., try this code
-(UIImage*)resizeImageToMaxSize:(CGFloat)max anImage:(UIImage*)anImage
{
NSData * imgData = UIImageJPEGRepresentation(anImage, 1);
CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)imgData, NULL);
if (!imageSource)
return nil;
CFDictionaryRef options = (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:
(id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform,
(id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageIfAbsent,
(id)[NSNumber numberWithFloat:max], (id)kCGImageSourceThumbnailMaxPixelSize,
nil];
CGImageRef imgRef = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options);
UIImage* scaled = [UIImage imageWithCGImage:imgRef];
//these lines might be skipped for ARC enabled
CGImageRelease(imgRef);
CFRelease(imageSource);
return scaled; }

Writing image metadata (EXIF/TIFF/IPTC) to image file in OS X

I am creating a photo editing app, and so far I've managed to read the metadata from image files successfully (after getting an answer to this question: Reading Camera data from EXIF while opening NSImage on OS X).
source = CGImageSourceCreateWithURL((__bridge CFURLRef)url, NULL);
NSDictionary *props = (__bridge_transfer NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
This copies all the metadata of the image file to a dictionary, and it works faily well. However, I couldn't find out how to write this metadata back to a newly created NSImage (or to an image file). Here is how I save my file (where img is an NSImage instance without metadata and self.cachedMetadata is the dictionary read from the initial image):
NSBitmapImageRep *rep = [NSBitmapImageRep imageRepWithData:[img TIFFRepresentation]];
[rep setProperty:NSImageEXIFData withValue:self.cachedMetadata];
NSData *data;
if([[fileName lowercaseString] rangeOfString:#".png"].location != NSNotFound){
data = [rep representationUsingType:NSPNGFileType properties:nil];
}else if([[fileName lowercaseString] rangeOfString:#".tif"].location != NSNotFound){
data = [rep representationUsingType:NSTIFFFileType properties:nil];
}else{ //assume jpeg
data = [rep representationUsingType:NSJPEGFileType properties:#{NSImageCompressionFactor: [NSNumber numberWithFloat:1], NSImageEXIFData: self.cachedMetadata}];
}
[data writeToFile:fileName atomically:YES];
How can I write the metadata? I used to write just EXIF for JPEG (the dictionary was EXIF-only previously) successfully but because EXIF lacked some of the fields that the initial images had (IPTC and TIFF tags) I needed to change my reading method. Now I have all the data, but I don't know how to write it to the newly-created image file.
Thanks,
Can.
Found an answer from another StackOverflow question: How do you overwrite image metadata?:
(code taken from that question itself and modified for my needs, which contains the answer to my question)
//assuming I've already loaded the image source and read the meta
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef) data, NULL);
CFStringRef imageType = CGImageSourceGetType(source);
//new empty data to write the final image data to
NSMutableData *resultData = [NSMutableData data];
CGImageDestinationRef imgDest = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)(resultData), imageType, 1, NULL);
//copy image data
CGImageDestinationAddImageFromSource(imgDest, source, 0, (__bridge CFDictionaryRef)(window.cachedMetadata));
BOOL success = CGImageDestinationFinalize(imgDest);
This worked perfectly for writing back all the metadata including EXIF, TIFF, and IPTC.
You could try either using, or looking at the code in, the "exiv2" tool maybe.

ios- pdf generation in uiscrollview

in my ios app i am listing out some data in scroll view. When a button is clicked the datas in the page are been generated in pdf file format. Following is the bit of code.
- (void)createPDFfromUIView:(UIView*)aView saveToDocumentsWithFileName:(NSString*)aFilename{
// Creates a mutable data object for updating with binary data, like a byte array
NSMutableData *pdfData = [NSMutableData data];
// Points the pdf converter to the mutable data object and to the UIView to be converted
UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil);
UIGraphicsBeginPDFPage();
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
// draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData
[aView.layer renderInContext:pdfContext];
// remove PDF rendering context
UIGraphicsEndPDFContext();
// Retrieves the document directories from the iOS device
NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString* documentDirectory = [documentDirectories objectAtIndex:0];
NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:aFilename];
// instructs the mutable data object to write its context to a file on disk
[pdfData writeToFile:documentDirectoryFilename atomically:YES];
NSLog(#"documentDirectoryFileName: %#",documentDirectoryFilename);
}
From the above code, i am able to generate a pdf file, but i am facing the following issue.
If i have more than 50 data, in my app i am able to see only the first 10 data and to see the others i need to scroll them. In this case when the pdf file gets generated it is taking only the data of first 10 and not the others. How to get the other data too
renderInContext: will only render what you currently see - UIScrollView is smart and will only have the views in the hierarchy that you need right now. You need a for-loop to manually scroll and redraw. This might be tricky to get right, maybe call [aView layoutSubviews] manually after scrolling to force the scrollView to rearrange the subviews instantly.
as steipete said, you can use loop to renderIncontext
- You need to calculate num of loop
numOfLoop = webview.ScrollView.ContentSize.Height/weview.frame.size.height
use loop to renderInContext
for(int i = 0; i < numOfLopp; i ++)
1.UIGraphicsBeginPDFPage()
get current context
renderIncontext
This way run correct in my app but I am getting with BIG issue when the numOfLoop lager
- you will get slow to renderInContext
- Memory will be increase -> Close app
In this way, How can I remove/release context when I am in once loop ?
Other way, you can use https://github.com/iclems/iOS-htmltopdf/blob/master/NDHTMLtoPDF.h
to generate to PDF
#steipete, #all Hi i tried the below code it generate multipage PDF, but the visible area in view is repeated for all the pages. please add any further ideas to improve the result.
-(void)createPDFfromUIView:(UIWebView*)aView saveToDocumentsWithFileName:(NSString*)aFilename
{
NSString *oldFilePath= [[NSBundle mainBundle] pathForResource:#"ABC" ofType:#"pdf"];
NSMutableData *pdfData = [NSMutableData data];
UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil);
CFURLRef url = CFURLCreateWithFileSystemPath (NULL, (CFStringRef)oldFilePath, kCFURLPOSIXPathStyle, 0);
CGPDFDocumentRef templateDocument = CGPDFDocumentCreateWithURL(url);
CFRelease(url); size_t count = CGPDFDocumentGetNumberOfPages(templateDocument);
for (size_t pageNumber = 1; pageNumber <= count; pageNumber++)
{
UIGraphicsBeginPDFPage();
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
[aView.layer renderInContext:pdfContext];
}
CGPDFDocumentRelease(templateDocument);
// remove PDF rendering context UIGraphicsEndPDFContext();
// Retrieves the document directories from the iOS device
NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString* documentDirectory = [documentDirectories objectAtIndex:0];
NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:aFilename];
[pdfData writeToFile:documentDirectoryFilename atomically:YES];
NSLog(#"documentDirectoryFileName: %#",documentDirectoryFilename);
}
Check ScrollViewToPDF example
It uses same scrollview's layer renderInContext but here PDF is created according to your requirement such as one page PDF or multiple page PDF
Note : It captures all visible as well as invisible part of scrollView