Another Programming Blog

(mostly iOS and Ruby on Rails)

iOS: How to Resize and Rotate an UIImage - Thread Safe Version

Here are two code snippets for rotating and resizing UIImage’s without using UIGraphicsBeginImageContext(), because it is not thread-safe and can lead to weird behavior.

In my case the camera-preview when taking a picture in an app I was writing, turned black from time to time. The problem was that I was rotating and resizing the images using UIGraphicsBeginImageContext().

Here’s some code I’ve found and tweaked to resize images:

- (UIImage *)imageByScalingToSize:(CGSize)targetSize
{
  UIImage *newImage = nil;
  
  CGFloat targetWidth = targetSize.width;
  CGFloat targetHeight = targetSize.height;

  CGContextRef bitmap = CGBitmapContextCreate(NULL,
                                              targetWidth,
                                              targetHeight,
                                              CGImageGetBitsPerComponent(self.CGImage),
                                              4 * targetWidth, CGImageGetColorSpace(self.CGImage),
                                              (CGBitmapInfo)kCGImageAlphaNoneSkipLast);
  
  CGContextDrawImage(bitmap, CGRectMake(0, 0, targetWidth, targetHeight), self.CGImage);
  
  CGImageRef ref = CGBitmapContextCreateImage(bitmap);
  newImage = [UIImage imageWithCGImage:ref];
  
  if(newImage == nil) NSLog(@"could not scale image");
  CGContextRelease(bitmap);
  
  return newImage ;
}

And here’s how to rotate them:

- (UIImage*)rotateInRadians:(CGFloat)radians
{
        CGImageRef cgImage = self.CGImage;
        const CGFloat originalWidth = CGImageGetWidth(cgImage);
        const CGFloat originalHeight = CGImageGetHeight(cgImage);

        const CGRect imgRect = (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, 
                .size.width = originalWidth, .size.height = originalHeight};
        const CGRect rotatedRect = CGRectApplyAffineTransform(imgRect, CGAffineTransformMakeRotation(radians));

        CGContextRef bmContext = NYXImageCreateARGBBitmapContext(rotatedRect.size.width, rotatedRect.size.height, 0);
        if (!bmContext)
                return nil;

        CGContextSetShouldAntialias(bmContext, true);
        CGContextSetAllowsAntialiasing(bmContext, true);
        CGContextSetInterpolationQuality(bmContext, kCGInterpolationHigh);

        CGContextTranslateCTM(bmContext, +(rotatedRect.size.width * 0.5f), +(rotatedRect.size.height * 0.5f));
        CGContextRotateCTM(bmContext, radians);

        CGContextDrawImage(bmContext, (CGRect){.origin.x = -originalWidth * 0.5f,  .origin.y = -originalHeight * 0.5f, 
                .size.width = originalWidth, .size.height = originalHeight}, cgImage);

        CGImageRef rotatedImageRef = CGBitmapContextCreateImage(bmContext);
        UIImage* rotated = [UIImage imageWithCGImage:rotatedImageRef];

        CGImageRelease(rotatedImageRef);
        CGContextRelease(bmContext);

        return rotated;
}

Because the above takes radians, convert using something like this:

CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;};

Just put these methods into a category on UIImage.

P.S.: You can follow me on Twitter.

Comments

Webmentions