Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is UIImageJPEGRepresentation() thread safe?

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 sendNSDataBack: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?

like image 671
rickharrison Avatar asked Apr 27 '11 17:04

rickharrison


1 Answers

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 );
}
like image 192
Kendall Helmstetter Gelner Avatar answered Sep 19 '22 13:09

Kendall Helmstetter Gelner