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];
}



15 comments:

Unknown said...

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

Dan Rosenstark said...

Very much welcome, glad to help out :)

Unknown said...

Yo! This code doesn't properly take into account the color space of the input UIColor. Specifically, it fails to render grayscale colors properly. Here is a modified version that takes this 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;
}

Unknown 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,

thanks for that nice code...

Is it possible to one color to another color ....
not a single color alone like transparent...?

my task was change one entire color[ARGB] to another set of colors[ARGB]

have any suggestions and ideas...

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

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

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

bobstravels1 said...

So, I did a small number of fixes to the code which are on https://gist.github.com/4353039. I'd really like to know whether my changes are actually worthy or if I broke some weird case (works for me!).

bobstravels1 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);

Ayoub Amghari Apps said...

UIImage *image = [UIImage imageNamed:@"triangle.png"];

CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClipToMask(context, rect, image.CGImage);
CGContextSetFillColorWithColor(context, [[UIColor redColor] CGColor]);
CGContextFillRect(context, rect);
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

UIImage *flippedImage = [UIImage imageWithCGImage:img.CGImage
scale:1.0 orientation: UIImageOrientationDownMirrored];

yourUIImageView.image = flippedImage;

Unknown said...

hello, i have one question please.. what if i have a patterned image, so every specific color should be changed into other one.. how could i do this?
appreciate your help

Unknown said...

hello, i have one question please.. what if i have a patterned image so every specific color should be changed to another one.. appreciate your help