How to avoid malloc while using CGImageDestinationFinalize - ios

I am trying to programmatically create GIF in iOS, using the following stack's question:
Create and and export an animated gif via iOS?
My code looks like this:
// File Parameters
const void *keys[] = { kCGImagePropertyGIFLoopCount };
const void *values[] = { (CFNumberRef) 0 };
CFDictionaryRef params = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
const void *keys2[] = { kCGImagePropertyGIFDictionary };
const void *values2[] = { (CFDictionaryRef) params };
CFDictionaryRef fileProperties = CFDictionaryCreate(NULL, keys2 , values2, 1, NULL, NULL);
// URL to the documents directory
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil];
NSURL *fileURL = [documentsDirectoryURL URLByAppendingPathComponent:fileName];
// Object that writes GIF to the specified URL
CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)fileURL, kUTTypeGIF, [arrayOfAllFrames count], NULL);
CGImageDestinationSetProperties(destination, fileProperties);
for (NSUInteger i = 0; i < [arrayOfAllFrames count]; i++) {
#autoreleasepool {
float delayTime = [[gifFramesDuration objectAtIndex:i] floatValue];
NSDictionary *frameProperties = #{
(__bridge id)kCGImagePropertyGIFDictionary: #{
(__bridge id)kCGImagePropertyGIFDelayTime: [NSNumber numberWithFloat:delayTime] // a float (not double!) in seconds, rounded to centiseconds in the GIF data
}
};
UIImage *myImage = [arrayOfAllFrames objectAtIndex:i];
CGImageDestinationAddImage(destination, myImage.CGImage, (__bridge CFDictionaryRef)frameProperties);
}
}
if (!CGImageDestinationFinalize(destination)) {
NSLog(#"failed to finalize image destination");
}
CFRelease(destination);
CFRelease(fileProperties);
CFRelease(params);
However once I try to add around 240 frames to the GIF file, debugger throws the following error once the CGImageDestinationFinalize gets called:
(923,0xb0115000) malloc: *** error for object 0xd1e7204: incorrect checksum for freed object - object was probably modified after being freed.
Could you please provide me with some workaround, or with a suggestion on how to avoid malloc?

First of all, try debugging your app using Instruments. You probably will notice that the problem is caused by the method:
Generatefromrgbimagewu
I have been wondering whether the cause was in my threads implementation, but it turns out, that once you have that kind of error, you should focus on resizing the Image.
Once the image had been resized, the code published above, will generate your own GIF.

Related

Objective C: Gif Conversion with with image array

For gif conversion with ImageArray . But I'm facing memory Issue when array contains more then 420 images (approximate).
Here is my code:
-(void) createGifFromImages:(NSArray*)imageArray :(NSString*)filename
{
NSDictionary *fileProperties = #{
(id)kCGImagePropertyGIFDictionary: #{
(id)kCGImagePropertyGIFLoopCount: #0, // 0 means loop forever
}
};
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil];
filename=[filename stringByAppendingString:#".gif"];
fileURL = [documentsDirectoryURL URLByAppendingPathComponent:filename];
NSLog(#"%#",fileURL);
CGImageDestinationRef destination = CGImageDestinationCreateWithURL((CFURLRef)fileURL, kUTTypeGIF,imageArray.count, NULL);
CGImageDestinationSetProperties(destination, (CFDictionaryRef)fileProperties);
UIImage *image;
for (int i=0;i<imageArray.count;i++) {
image=imageArray[i];
NSDictionary *frameDurationProperties = #{
(id)kCGImagePropertyGIFDictionary: #{
(id)kCGImagePropertyGIFDelayTime: _durationArray[i % _durationArray.count], // a float (not double!) in seconds, rounded to centiseconds in the GIF data
}
};
CGImageDestinationAddImage(destination, image.CGImage, (CFDictionaryRef)frameDurationProperties);
}
if (!CGImageDestinationFinalize(destination)) {
}
CFRelease(destination);
}
In this code i'm using default size image.
How do I reduce my memory allocation?
& Is there any way to reduce size of images without changing its quality?

How many images Array to make GIF Images in ios

I'm still making 60 images array to GIF images Size 320*320. Normally I used third party Library NSGIF on github but still getting app memory warning and crash when used 80 images in to make GIF images process.
NSDictionary *fileProperties = #{(__bridge id)kCGImagePropertyGIFDictionary: #{
(__bridge id)kCGImagePropertyGIFLoopCount: #0, // 0 means loop forever
}
};
NSDictionary *frameProperties = #{(__bridge id)kCGImagePropertyGIFDictionary: #{
//(__bridge id)kCGImagePropertyGIFDelayTime: #0.02f, // a float (not double!) in seconds, rounded to centiseconds in the GIF data
(__bridge id)kCGImagePropertyGIFDelayTime: #0.06f,
}
};
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil];
NSString *savePath = [documentsDirectoryURL URLByAppendingPathComponent:#"animated.gif"];
CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)savePath, kUTTypeGIF, FrameArr.count, NULL);
CGImageDestinationSetProperties(destination, (__bridge CFDictionaryRef)fileProperties);
for (NSUInteger i = 0; i < ImageArray.count; i++) {
#autoreleasepool {
UIImage *CaptureImage = [ImageArray objectAtIndex:i];
CGImageDestinationAddImage(destination, CaptureImage.CGImage, (__bridge CFDictionaryRef)frameProperties);
}
}
if (!CGImageDestinationFinalize(destination)) {
}
else
{
//[shareBtn setHidden:NO];
}
CFRelease(destination);
i want to make 80 plus images to GIF..
That actually depends on the duration and the framerate of the gif that your are creating. With a GIF you can probably bring down the framerate to 8-10 fps and still see something decent out of it.
Otherwise you can always opt for a different library and hope it has a better performance

Custom Certificate in iOS App

I created a certificate (of p12 type) that I installed onto an iPad using Apple Configurator. I now want to access that certificate in an app I've written but I can't seem to find a source example for it. There are lots of examples on how to use certificates but I've been unable to find anything on how to import it into an app.
Can someone point me in the right direction? Thanks in advance.
* NEW *
So I tried following the tutorial here https://developer.apple.com/library/ios/documentation/Security/Conceptual/CertKeyTrustProgGuide/iPhone_Tasks/iPhone_Tasks.html#//apple_ref/doc/uid/TP40001358-CH208-SW13 using Listing 2-1 and Listing 2-2. For now I gave up on getting the certificate from the keychain and instead I load it from the main Bundle.
NSData *certData = [[NSData alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"myfile.com" ofType:#"pfx"]];
CFDataRef myCertData = (__bridge_retained CFDataRef)(certData);
I'm not certain whether my loading is correct but when I debug certData is not NULL and contains bytes.
Next I modified the code in the two listings since the method signatures don't really match an Objective C method signature. Someone help me out here why Apple would show it this way? No code inside the function was modified.
- (OSStatus) extractIdentityAndTrust: (CFDataRef) inPKCS12Data withIdentity:(SecIdentityRef *) outIdentity withTrust:(SecTrustRef *) outTrust withPassword:(CFStringRef) keyPassword
{
OSStatus securityError = errSecSuccess;
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { keyPassword };
CFDictionaryRef optionsDictionary = NULL;
optionsDictionary = CFDictionaryCreate(NULL, keys, values, (keyPassword ? 1 : 0), NULL, NULL);
CFArrayRef items = NULL;
securityError = SecPKCS12Import(inPKCS12Data, optionsDictionary, &items);
if (securityError == 0) {
CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (items, 0);
const void *tempIdentity = NULL;
tempIdentity = CFDictionaryGetValue (myIdentityAndTrust,
kSecImportItemIdentity);
CFRetain(tempIdentity);
*outIdentity = (SecIdentityRef)tempIdentity;
const void *tempTrust = NULL;
tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust);
CFRetain(tempTrust);
*outTrust = (SecTrustRef)tempTrust;
}
if (optionsDictionary)
CFRelease(optionsDictionary);
if (items)
CFRelease(items);
return securityError;
}
Next I modified the method signature for copySummaryString. I also had to add a deference inside the function for the 'identity' variable.
- (NSString *)copySummaryString:(SecIdentityRef *) identity
{
// Get the certificate from the identity.
SecCertificateRef myReturnedCertificate = NULL;
OSStatus status = SecIdentityCopyCertificate (*identity, &myReturnedCertificate);
if (status) {
NSLog(#"SecIdentityCopyCertificate failed.\n");
return NULL;
}
CFStringRef certSummary = SecCertificateCopySubjectSummary
(myReturnedCertificate);
NSString* summaryString = [[NSString alloc] initWithString:(__bridge NSString *)certSummary];
CFRelease(certSummary);
return summaryString;
}
Finally in my calling function I created a identity and trust variable that I pass around:
[self extractIdentityAndTrust:myCertData withIdentity:identity withTrust:trust withPassword:CFSTR("supersecret")];
[self copySummaryString:identity];
When I try to run this I get a Thread 1: EXC_BAD_ACCESS(code=1, adress=0x2b) error and XCode pauses on the following line:
CFStringRef certSummary = SecCertificateCopySubjectSummary
This code doesn't reference any variable I created so I'm really surprised it's crashing here. The ultimate goal with loading this certificate is to use it for HTTPS. I hope that I'm at least on the right path here.
Here is the documentation and example about finding and using of a pre-installed certificate and identity through your mobile app.
https://developer.apple.com/library/ios/documentation/Security/Conceptual/CertKeyTrustProgGuide/iPhone_Tasks/iPhone_Tasks.html#//apple_ref/doc/uid/TP40001358-CH208-DontLinkElementID_10
*NEW
I checked your update.
Both you methods works like charm.
I used below source to call your methods, and was able to extract the SummaryString properly.
SecIdentityRef identity = nil;
SecTrustRef trust = nil;
NSData *certData = [[NSData alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"[Dev] InHouse_Certificates" ofType:#"p12"]];
CFDataRef myCertData = (__bridge_retained CFDataRef)(certData);
[self extractIdentityAndTrust:myCertData withIdentity:&identity withTrust:&trust withPassword:CFSTR("1234")];
NSString* summaryString = [self copySummaryString:&identity];

There is no effect when setting quality of JPEG compression in CGImageDestination in Apple's Core Graphics/ ImageIO framework

I am writing and IOS app where I need to save a CGImage as a JPEG file. It is important that I control the quality of the compression.
I've written the function provided below. It works, in that I get a JPEG file. But no matter what I set for the compression, I am always getting the same result. i.e. the file size is always the same.
Can anyone tell me what I am doing wrong?
void CGImageWriteJPEG(CGImageRef image, NSString *path) {
NSMutableData * data = [[NSMutableData alloc] init];
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)data, kUTTypeJPEG, 1, NULL);
float compression = .8; // What I put here does not seem to matter...
CFStringRef myKeys[1];
CFTypeRef myValues[1];
CFDictionaryRef myOptions = NULL;
myKeys[0] = kCGImageDestinationLossyCompressionQuality;
myValues[0] = CFNumberCreate(NULL, kCFNumberFloatType, &compression);
myOptions = CFDictionaryCreate( NULL, (const void **)myKeys, (const void **)myValues, 1,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CGImageDestinationSetProperties(destination, myOptions);
CGImageDestinationAddImage(destination, image, nil);
CGImageDestinationFinalize(destination);
[data writeToURL:[NSURL fileURLWithPath:path] atomically:NO];
CFRelease(destination);
CFRelease(myOptions);
CFRelease(myValues[0]);
}
Try passing myOptions to CGImageDestinationAddImage() instead
CGImageDestinationAddImage(destination, image, myOptions);
It's what I had to do in swift, so I assume it's the same. e.g.
let imageProperties = [kCGImageDestinationLossyCompressionQuality as String: 0.8]
CGImageDestinationAddImage(destination, image, imageProperties)
Updated answer for swift 3
let imageProperties = [kCGImageDestinationLossyCompressionQuality as String: 0.8] as CFDictionary
CGImageDestinationAddImage(destination, image, imageProperties)

How to write custom metadata to PNG images in iOS

My application should be able to write custom metadata entries to PNG images for export to the UIPasteboard.
By piecing together various posts on the subject, I've been able to come up with the class given below as source.
Triggering the copyPressed method with a button, I'm able to set custom metadata with JPG images (EXIF):
Image[6101:907] found jpg exif dictionary
Image[6101:907] checking image metadata on clipboard
Image[6101:907] {
ColorModel = RGB;
Depth = 8;
Orientation = 1;
PixelHeight = 224;
PixelWidth = 240;
"{Exif}" = {
ColorSpace = 1;
PixelXDimension = 240;
PixelYDimension = 224;
UserComment = "Here is a comment";
};
"{JFIF}" = {
DensityUnit = 0;
JFIFVersion = (
1,
1
);
XDensity = 1;
YDensity = 1;
};
"{TIFF}" = {
Orientation = 1;
};
}
Although I'm able to read the PNG metadata just fine, I can't seem to write to it:
Image[6116:907] found png property dictionary
Image[6116:907] checking image metadata on clipboard
Image[6116:907] {
ColorModel = RGB;
Depth = 8;
PixelHeight = 224;
PixelWidth = 240;
"{PNG}" = {
InterlaceType = 0;
};
}
However, nothing in the documentation suggests this should fail and the presence of many PNG-specific metadata constants suggests it should succeed.
My application should use PNG to avoid JPG's lossy compression.
Why can I not set custom metadata on an in-memory PNG image in iOS?
Note: I've seen this SO question, but it doesn't address the problem here, which is how to write metadata to PNG images specifically.
IMViewController.m
#import "IMViewController.h"
#import <ImageIO/ImageIO.h>
#interface IMViewController ()
#end
#implementation IMViewController
- (IBAction)copyPressed:(id)sender
{
// [self copyJPG];
[self copyPNG];
}
-(void)copyPNG
{
NSData *pngData = UIImagePNGRepresentation([UIImage imageNamed:#"wow.png"]);
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)pngData, NULL);
NSDictionary *metadata = (__bridge NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
NSMutableDictionary *mutableMetadata = [metadata mutableCopy];
NSMutableDictionary *dict = [[mutableMetadata objectForKey:(NSString *) kCGImagePropertyPNGDictionary] mutableCopy];
if (dict) {
NSLog(#"found png property dictionary");
} else {
NSLog(#"creating png property dictionary");
dict = [NSMutableDictionary dictionary];
}
// set values on the root dictionary
[mutableMetadata setObject:#"Name of Software" forKey:(NSString *)kCGImagePropertyPNGDescription];
[mutableMetadata setObject:dict forKey:(NSString *)kCGImagePropertyPNGDictionary];
// set values on the internal dictionary
[dict setObject:#"works" forKey:(NSString *)kCGImagePropertyPNGDescription];
CFStringRef UTI = CGImageSourceGetType(source);
NSMutableData *data = [NSMutableData data];
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef) data, UTI, 1, NULL);
if (!destination) {
NSLog(#">>> Could not create image destination <<<");
return;
}
CGImageDestinationAddImageFromSource(destination, source, 0, (__bridge CFDictionaryRef) mutableMetadata);
BOOL success = CGImageDestinationFinalize(destination);
if (!success) {
NSLog(#">>> Error Writing Data <<<");
}
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
[pasteboard setData:data forPasteboardType:#"public.png"];
[self showPNGMetadata];
}
-(void)copyJPG
{
NSData *jpgData = UIImageJPEGRepresentation([UIImage imageNamed:#"wow.jpg"], 1);
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef) jpgData, NULL);
NSDictionary *metadata = (__bridge NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
NSMutableDictionary *mutableMetadata = [metadata mutableCopy];
NSMutableDictionary *exif = [[mutableMetadata objectForKey:(NSString *)kCGImagePropertyExifDictionary] mutableCopy];
if (exif) {
NSLog(#"found jpg exif dictionary");
} else {
NSLog(#"creating jpg exif dictionary");
}
// set values on the exif dictionary
[exif setObject:#"Here is a comment" forKey:(NSString *)kCGImagePropertyExifUserComment];
[mutableMetadata setObject:exif forKey:(NSString *)kCGImagePropertyExifDictionary];
CFStringRef UTI = CGImageSourceGetType(source);
NSMutableData *data = [NSMutableData data];
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef) data, UTI, 1, NULL);
if(!destination) {
NSLog(#">>> Could not create image destination <<<");
return;
}
CGImageDestinationAddImageFromSource(destination,source, 0, (__bridge CFDictionaryRef) mutableMetadata);
BOOL success = CGImageDestinationFinalize(destination);
if (!success) {
NSLog(#">>> Could not create data from image destination <<<");
}
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
[pasteboard setData:data forPasteboardType:#"public.jpeg"];
[self showJPGMetadata];
}
-(void)showJPGMetadata
{
NSLog(#"checking image metadata on clipboard");
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
NSData *data = [pasteboard dataForPasteboardType:#"public.jpeg"];
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
NSDictionary *metadata = (__bridge NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source,0,NULL);
NSLog(#"%#", metadata);
}
-(void)showPNGMetadata
{
NSLog(#"checking image metadata on clipboard");
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
NSData *data = [pasteboard dataForPasteboardType:#"public.png"];
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
NSDictionary *metadata = (__bridge NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source,0,NULL);
NSLog(#"%#", metadata);
}
#end
If you will try to save your image with modified metadata
[data writeToFile:[NSTemporaryDirectory() stringByAppendingPathComponent:#"test.png"]
atomically:YES];
And than view it properties in Finder. You will see that kCGImagePropertyPNGDescription field was setted up successfully.
But if you will try read metadata of this new file, kCGImagePropertyPNGDescription will be lost.
ColorModel = RGB;
Depth = 8;
PixelHeight = 1136;
PixelWidth = 640;
"{PNG}" = {
InterlaceType = 0;
};
After some research I found that PNG doesn't contain metadata. But it may contain XMP metadata. However seems like ImageIO didn't work with XMP.
Maybe you can try to use ImageMagic or libexif.
Useful links:
PNG Specification
Reading/Writing image XMP on iPhone / Objective-c
Does PNG support metadata fields like Author, Camera Model, etc?
Does PNG contain EXIF data like JPG?
libexif.sourceforge.net

Resources