On my iPhone app I have a UIImage instance. I want to get a derived a UIImage that is the result of the first UIImage where one of its colors (e.g. magenta) is made transparent. How can I do this?
Select the picture for which you want to change the transparency of a color. On the Picture Format tab, select Color, and then select Set Transparent Color. Click the color in the picture or image that you want to make transparent. Note: You can't make more than one color in a picture transparent.
Click "Color" from the menu bar and select "Color to Alpha." The Color to Alpha dialog window opens and shows a small preview of your image. The Color to Alpha feature allows you to choose a color in your image and make it transparent.
OK. Having tried I don't know how many versions of these solutions, I have my own customised version. What I've found is that the solution from @yubenyi works quite well, but if you want to take the output from his changeWhiteColorTransparent() function and pass it back in, it doesn't work.
My first step was to change his function to accept a specific colour and tolerance so that the caller could specify a range of colours to make transparent. This worked fine, almost unchanged, but I found that the output was not a valid image for passing through the same code with a second colour range.
After a lot of trial and error, I got this working by doing the colour replacement myself. I resisted this because it seemed like too much hard work when there are API's to do this stuff, but they don't always behave the way you want. Specifically, the output from CGImageCreateWithMaskingColors() can't be used as an input into another call to the same function. I haven't been able to work out why, but I think it's something to do with the alpha channel.
In any case, my solution is:
- (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; }
This is a tweak of yubenyi's code that will work with multiple passes. It strips the alpha channel before processing by converting the image to an uncompressed jpeg. Also added some comments on how the color range selection works.
-(UIImage *)changeWhiteColorTransparent: (UIImage *)image { //convert to uncompressed jpg to remove any alpha channels //this is a necessary first step when processing images that already have transparency image = [UIImage imageWithData:UIImageJPEGRepresentation(image, 1.0)]; CGImageRef rawImageRef=image.CGImage; //RGB color range to mask (make transparent) R-Low, R-High, G-Low, G-High, B-Low, B-High const double colorMasking[6] = {222, 255, 222, 255, 222, 255}; UIGraphicsBeginImageContext(image.size); CGImageRef maskedImageRef=CGImageCreateWithMaskingColors(rawImageRef, colorMasking); //iPhone translation CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 0.0, image.size.height); CGContextScaleCTM(UIGraphicsGetCurrentContext(), 1.0, -1.0); CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, image.size.width, image.size.height), maskedImageRef); UIImage *result = UIGraphicsGetImageFromCurrentImageContext(); CGImageRelease(maskedImageRef); UIGraphicsEndImageContext(); return result; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With