Last Updated: February 25, 2016
·
1.88K
· zwaldowski

Create UIImage Using Block, Thread-Safe

//
//  UIImageCreateUsingBlock.m
//
//  Copyright (c) 2013 Zachary Waldowski.
//  Licensed under MIT - Provided as-is.
//

#import <UIKit/UIKit.h>
#import <CoreGraphics/CoreGraphics.h>

static inline size_t aligned_size(size_t size, size_t alignment) {
    size_t r = size + --alignment + 2;
    return (r + 2 + alignment) & ~alignment;
}

UIImage *UIImageCreateUsingBlock(CGSize size, BOOL opaque, void(^drawingBlock)(void)) {
    BOOL isMain = [NSThread isMainThread];
    CGContextRef context = NULL;
    CGFloat scale;

    if (isMain) {
        UIGraphicsBeginImageContextWithOptions(size, opaque, 0.0);
        context = UIGraphicsGetCurrentContext();
    } else {
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        scale = [UIScreen mainScreen].scale;
        CGImageAlphaInfo alphaInfo;
        if (opaque) {
            alphaInfo = kCGImageAlphaNoneSkipFirst;
        } else {
            alphaInfo = kCGImageAlphaPremultipliedFirst;
        }

        // RGB - 32 bpp - 8 bpc - available on both OS X and iOS
        const size_t bitsPerPixel = 32;
        const size_t bitsPerComponent = 8;
        size_t widthPixels = (size_t)ceil(size.width * scale);
        size_t heightPixels = (size_t)ceil(size.height * scale);

        // Quartz 2D Programming Guide
        // "When you create a bitmap graphics context, you’ll get the best
        // performance if you make sure the data and bytesPerRow are 16-byte aligned."
        size_t bytesPerRow = widthPixels * bitsPerPixel;
        size_t alignedBytesPerRow = aligned_size(bytesPerRow, 16);

        context = CGBitmapContextCreate(NULL, widthPixels, heightPixels, bitsPerComponent, alignedBytesPerRow, colorSpace, alphaInfo);
        CGColorSpaceRelease(colorSpace);
        CGContextScaleCTM(context, scale, -1 * scale);
        CGContextTranslateCTM(context, 0, -1 * size.height);
        CGContextClipToRect(context, (CGRect){ CGPointZero, size });
        UIGraphicsPushContext(context);
    }

    if (drawingBlock) drawingBlock();

    UIImage *retImage = nil;

    if (isMain) {
        retImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    } else {
        UIGraphicsPopContext();
        CGImageRef cgImage = CGBitmapContextCreateImage(context);
        retImage = [UIImage imageWithCGImage:cgImage scale:scale orientation:UIImageOrientationUp];
        CGImageRelease(cgImage);
        CGContextRelease(context);
    }

    return retImage;
}