UIImage categories (part 1 of 4)

My current iOS app gets images that I need to process. Basically, in my app, I get a UIImage with a white background and a CGRect, and I need to make the background of the image clear, automatically crop the image, scale image into the given CGRect preserving the aspect ratio, and then pad the scaled CGRect so that it appears centered inside of the original CGRect.

So in order to do this, I went through and found some UIImage categories to perform these tasks. Here is the first category that makes the white background into a clear background:

- (UIImage *)makeWhiteBackgroundTransparent
{
    return [UIImage replaceColor:[UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0] inImage:self withTolerance:0.0];
}
 
+ (UIImage *)replaceColor:(UIColor *)color inImage:(UIImage *)image withTolerance:(float)tolerance
{
    CGImageRef imageRef = [image CGImage];
 
    NSUInteger width = CGImageGetWidth(imageRef);
    NSUInteger height = CGImageGetHeight(imageRef);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
 
    NSUInteger bytesPerPixel = 4;
    NSUInteger bytesPerRow = bytesPerPixel * width;
    NSUInteger bitsPerComponent = 8;
    NSUInteger bitmapByteCount = bytesPerRow * height;
 
    unsigned char *rawData = (unsigned char*) calloc(bitmapByteCount, sizeof(unsigned char));
 
    CGContextRef context = CGBitmapContextCreate(rawData, width, height,
                                                 bitsPerComponent, bytesPerRow, colorSpace,
                                                 kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGColorSpaceRelease(colorSpace);
 
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
 
    CGColorRef cgColor = [color CGColor];
    const CGFloat *components = CGColorGetComponents(cgColor);
    float r = components[0];
    float g = components[1];
    float b = components[2];
    //float a = components[3]; // not needed
 
    r = r * 255.0;
    g = g * 255.0;
    b = b * 255.0;
 
    const float redRange[2] = {
        MAX(r - (tolerance / 2.0), 0.0),
        MIN(r + (tolerance / 2.0), 255.0)
    };
 
    const float greenRange[2] = {
        MAX(g - (tolerance / 2.0), 0.0),
        MIN(g + (tolerance / 2.0), 255.0)
    };
 
    const float blueRange[2] = {
        MAX(b - (tolerance / 2.0), 0.0),
        MIN(b + (tolerance / 2.0), 255.0)
    };
 
    int byteIndex = 0;
 
    while (byteIndex < bitmapByteCount)
    {
        unsigned char red   = rawData[byteIndex];
        unsigned char green = rawData[byteIndex + 1];
        unsigned char blue  = rawData[byteIndex + 2];
 
        if (((red >= redRange[0]) && (red <= redRange[1])) &&
            ((green >= greenRange[0]) && (green <= greenRange[1])) &&
            ((blue >= blueRange[0]) && (blue <= blueRange[1])))
        {
            // make the pixel transparent
            //
            rawData[byteIndex] = 0;
            rawData[byteIndex + 1] = 0;
            rawData[byteIndex + 2] = 0;
            rawData[byteIndex + 3] = 0;
        }
 
        byteIndex += 4;
    }
 
    CGImageRef imgRef = CGBitmapContextCreateImage(context);
    UIImage *result = [UIImage imageWithCGImage:imgRef];
    CGImageRelease(imgRef);
 
    CGContextRelease(context);
    free(rawData);
 
    return result;
}

I would attribute this code, but I can’t find where I found this particular replaceColor method. If anyone knows where this code originally came from, please let me know and I will make the necessary attribution.

BTW, Happy Anniversary to McDonalds, who opened their first restaurant in San Bernardino, California on this date back in 1940.

Leave a Reply