Monday, December 13, 2010

Change the Colors of a UIImage Using a UIColor

Lots of googling led me here. Then it was pretty easy to mash this into that. When you combine this with the UIButton images provided by Apple, you can do cool stuff.

- (UIImage *) changeColorForImage:(UIImage *)image toColor:(UIColor*)color {
UIGraphicsBeginImageContext(image.size);
CGRect contextRect;
contextRect.origin.x = 0.0f;
contextRect.origin.y = 0.0f;
contextRect.size = [image size];
// Retrieve source image and begin image context
CGSize itemImageSize = [image size];
CGPoint itemImagePosition; 
itemImagePosition.x = ceilf((contextRect.size.width - itemImageSize.width) / 2);
itemImagePosition.y = ceilf((contextRect.size.height - itemImageSize.height) );
UIGraphicsBeginImageContext(contextRect.size);
CGContextRef c = UIGraphicsGetCurrentContext();
// Setup shadow
// Setup transparency layer and clip to mask
CGContextBeginTransparencyLayer(c, NULL);
CGContextScaleCTM(c, 1.0, -1.0);
CGContextClipToMask(c, CGRectMake(itemImagePosition.x, -itemImagePosition.y, itemImageSize.width, -itemImageSize.height), [image CGImage]);
// Fill and end the transparency layer
const float* colors = CGColorGetComponents( color.CGColor );
CGContextSetRGBFillColor(c, colors[0], colors[1], colors[2], .75);
contextRect.size.height = -contextRect.size.height;
contextRect.size.height -= 15;
CGContextFillRect(c, contextRect);
CGContextEndTransparencyLayer(c);
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}

Now that I know this simple code, I can even create idiotic methods like:


- (UIColor*) oppositeColor:(UIColor*)color {
const float* colors = CGColorGetComponents(color.CGColor);
return [UIColor colorWithRed:1-colors[0] green:1-colors[1] blue:1-colors[2] alpha:1];
}



7 comments:

Louis said...

thanks you so much man, it's exaclt what I was looking for.
Louis.

Daniel said...

Very much welcome, glad to help out :)

Ben Cunningham said...

Hey! This code doesn't properly account for different color spaces of the input UIColor. Specifically, it renders grayscale colors improperly. Here is a modified version that takes the input color space into account.

+ (UIImage*)changeColorForImage:(UIImage *)image toColor:(UIColor*)color
{
UIGraphicsBeginImageContext(image.size);

CGRect contextRect;
contextRect.origin.x = 0.0f;
contextRect.origin.y = 0.0f;
contextRect.size = [image size];

// Retrieve source image and begin image context
CGSize itemImageSize = [image size];
CGPoint itemImagePosition;
itemImagePosition.x = ceilf((contextRect.size.width - itemImageSize.width) / 2);
itemImagePosition.y = ceilf((contextRect.size.height - itemImageSize.height) );

UIGraphicsBeginImageContext(contextRect.size);

CGContextRef c = UIGraphicsGetCurrentContext();

// Setup shadow
// Setup transparency layer and clip to mask
CGContextBeginTransparencyLayer(c, NULL);
CGContextScaleCTM(c, 1.0, -1.0);
CGContextClipToMask(c, CGRectMake(itemImagePosition.x, -itemImagePosition.y, itemImageSize.width, -itemImageSize.height), [image CGImage]);
// Fill and end the transparency layer
CGColorSpaceRef colorSpace = CGColorGetColorSpace(color.CGColor);
CGColorSpaceModel model = CGColorSpaceGetModel(colorSpace);
const CGFloat* colors = CGColorGetComponents(color.CGColor);

if(model == kCGColorSpaceModelMonochrome)
{
CGContextSetRGBFillColor(c, colors[0], colors[0], colors[0], colors[1]);
}else{
CGContextSetRGBFillColor(c, colors[0], colors[1], colors[2], colors[3]);
}
contextRect.size.height = -contextRect.size.height;
contextRect.size.height -= 15;
CGContextFillRect(c, contextRect);
CGContextEndTransparencyLayer(c);

UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}

Dan Rosenstark said...

Hey Ben,

You are right. Under what condition would be you be in (using?) a different color space?

Thanks!
D

spynet said...

Hi Ben,

Code looks great and thanks for the code...

Is it possible to change one complete color [ARGB not transparent] to another color...?

any idea regarding this please share

Thanks,
Spynet

Daniel Ferreira said...

So, I wrote this gist at https://gist.github.com/4353039, with some fixes and removal of unneeded parts. If I broke some use case I'm unaware of, please let me know, but as far as I've tested it it works fine.

Unknown said...

Solved my problem where I wanted to have the possibility to change (unselected) icon colours in UITabBar without using different coluored images.

I also made this change to accommodate for a scaling issue with retina images (diff):
-UIGraphicsBeginImageContextWithOptions(contextRect.size);
+UIGraphicsBeginImageContextWithOptions(contextRect.size, NO, image.scale);