I am scaling and cropping a UIImage
and I want to be able to do it in a block that is thread safe. I could not find in the docs whether UIImageJPEGRepresentation
is thread safe.
In the following code, I crop and scale a CGImage
, then I create a UIImage
from that and get the UIImageJPEGRepresentation
. The end goal of this block is to get the NSData*
from the scaled/cropped version.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
CGImageRef imageRef = photo.CGImage;
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
CGColorSpaceRef colorSpaceInfo = CGImageGetColorSpace(imageRef);
CGContextRef bitmap;
if (photo.imageOrientation == UIImageOrientationUp || photo.imageOrientation == UIImageOrientationDown) {
bitmap = CGBitmapContextCreate(NULL, kFINAL_WIDTH, kFINAL_HEIGHT, CGImageGetBitsPerComponent(imageRef), 0, colorSpaceInfo, bitmapInfo);
} else {
bitmap = CGBitmapContextCreate(NULL, kFINAL_HEIGHT, kFINAL_WIDTH, CGImageGetBitsPerComponent(imageRef), 0, colorSpaceInfo, bitmapInfo);
}
CGContextSetInterpolationQuality(bitmap, kCGInterpolationHigh);
CGContextDrawImage(bitmap, drawRect, imageRef);
CGImageRef ref = CGBitmapContextCreateImage(bitmap);
NSData *finalData = UIImageJPEGRepresentation([UIImage imageWithCGImage:ref], 1.0);
CGContextRelease(bitmap);
CGImageRelease(ref);
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate sendNS开发者_如何学GoDataBack:finalData];
});
});
I tried getting the NSData using a CGDataProviderRef, but when I did finally get the NSData, putting it in a UIImage into a UIImageView displayed nothing.
So bottomline question is. Can I do [UIImage imageWithData:]
and UIImageJPEGRepresentation
in another thread in a block using GCD?
You can use UIImageJPEGRepresentation() in the background (I'm using it this way in a current project).
However what you can't do is create a UIImage the way you are doing in the background, the [UIImage imagewithCGImage]
call must be doing in the main thread (as a rule of thumb all UIKit calls should be done on the main thread).
This seems like a case where you might need nested blocks.
Edit: My own code I have found does call [UIImage imagewithCGImage]
while in a background thread, but I am still suspicious that might cause issues in some cases. But my code does work.
Edit2: I just noticed you are resizing the image, UIImage+Resize. There's a very nice class linked to in this post, that has been built to do that in a robust way:
http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/
You should really read that whole page to understand the nuances of resizing images. As I said, I do use that from a background thread even though part of what it does inside is what you were doing.
Edit3: If you are running on iOS4 or later, you may want to look into using the ImageIO framework to output images, which is more likely to be thread safe:
http://developer.apple.com/graphicsimaging/workingwithimageio.html
Example code for that is hard to find, here's a method that saves a PNG image using ImageIO (based on the code in "Programming With Quartz:2D and PDF graphics in Mac OS X):
// You'll need both ImageIO and MobileCoreServices frameworks to have this compile
#import <ImageIO/ImageIO.h>
#import <MobileCoreServices/MobileCoreServices.h>
void exportCGImageToPNGFileWithDestination( CGImageRef image, CFURLRef url)
{
float resolution = 144;
CFTypeRef keys[2];
CFTypeRef values[2];
CFDictionaryRef options = NULL;
// Create image destination to go into URL, using PNG
CGImageDestinationRef imageDestination = CGImageDestinationCreateWithURL( url, kUTTypePNG, 1, NULL);
if ( imageDestination == NULL )
{
fprintf( stderr, "Error creating image destination\n");
return;
}
// Set the keys to be the X and Y resolution of the image
keys[0] = kCGImagePropertyDPIWidth;
keys[1] = kCGImagePropertyDPIHeight;
// Create a number for the DPI value for the image
values[0] = CFNumberCreate( NULL, kCFNumberFloatType, &resolution );
values[1] = values[0];
// Options dictionary for output
options = CFDictionaryCreate(NULL,
(const void **)keys,
(const void **)values,
2,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFRelease(values[0]);
// Adding the image to the destination
CGImageDestinationAddImage( imageDestination, image, options );
CFRelease( options );
// Finalizing writes out the image to the destination
CGImageDestinationFinalize( imageDestination );
CFRelease( imageDestination );
}
Apple's official position is that no part of UIKit is thread-safe. However, the rest of your code appears to be Quartz-based, which is thread-safe when used in the manner you use it.
You can do everything on a background thread, then do the call to UIImageJPEGRepresentation()
back on main:
// ...
CGImageRef ref = CGBitmapContextCreateImage(bitmap);
dispatch_async(dispatch_get_main_queue(), ^ {
NSData *finalData = UIImageJPEGRepresentation([UIImage imageWithCGImage:ref], 1.0);
[self.delegate sendNSDataBack:finalData];
});
CGContextRelease(bitmap);
CGImageRelease(ref);
I think it is thread-safe because I do the similar things to resize an UIImage, or store image data to database in the background thread. And, the main-thread sometimes is named to UI thread. Anything about updating a screen should be executed on the UI thread. But, the UIImage is an object to store image data. It is not sub-classed from UIView. So, it is thread-safe.
精彩评论