user141302
user141302

Reputation:

UIImage color changing?

How can I change the UIImage's color through programming, any help please? If I send a UIImage, its color needs to change any help please? If I change the RGB color through bitmaphandling, it does not work.

Upvotes: 19

Views: 24811

Answers (11)

mixel
mixel

Reputation: 25876

For me this worked:

extension UIImage {
    class func image(image: UIImage, withColor color: UIColor) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(CGSizeMake(image.size.width, image.size.height), false, image.scale)
        let context = UIGraphicsGetCurrentContext()
        color.set()
        CGContextTranslateCTM(context, 0, image.size.height)
        CGContextScaleCTM(context, 1, -1)
        let rect = CGRectMake(0, 0, image.size.width, image.size.height)
        CGContextClipToMask(context, rect, image.CGImage)
        CGContextFillRect(context, rect)
        let coloredImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return coloredImage
    }
}

Upvotes: 0

GK100
GK100

Reputation: 3684

The great post mentioned by user576924 worked great for me: iPhone: How to Dynamically Color a UIImage

and in swift:

extension UIImage {

    func imageWithColor( color : UIColor ) -> UIImage {

       // begin a new image context, to draw our colored image onto
       UIGraphicsBeginImageContext(self.size)

       // get a reference to that context we created
       let context = UIGraphicsGetCurrentContext();

       // set the fill color
       color.setFill()

       // translate/flip the graphics context (for transforming from CG* coords to UI* coords
       CGContextTranslateCTM(context, 0, self.size.height)
       CGContextScaleCTM(context, 1.0, -1.0)

       // set the blend mode to color burn, and the original image
       CGContextSetBlendMode(context, kCGBlendModeColor)
       let rect = CGRect(origin: CGPointZero, size: self.size)
       CGContextDrawImage(context, rect, self.CGImage)

       // set a mask that matches the shape of the image, then draw (color burn) a colored rectangle
       CGContextClipToMask(context, rect, self.CGImage)
       CGContextAddRect(context, rect)
       CGContextDrawPath(context,kCGPathFill)

       // generate a new UIImage from the graphics context we drew onto
       let coloredImg = UIGraphicsGetImageFromCurrentImageContext()
       UIGraphicsEndImageContext()

       //return the color-burned image
       return coloredImg
   }

}

Note that I also changed "kCGBlendModeColorBurn" to "kCGBlendModeColor" as mentioned in the post's comments section.

Upvotes: 0

cbh2000
cbh2000

Reputation: 2193

If you only need it to look different, just use imageView.tintColor (iOS 7+). Catch is, setting tintColor doesn't do anything by default:

Why isn't my image blue?  I'm setting tintColor...

To make it work, use imageWithRenderingMode:

var image = UIImage(named: "stackoverflow")!
image = image.imageWithRenderingMode(.AlwaysTemplate)

let imageView = ...
imageView.tintColor = UIColor(red: 0.35, green: 0.85, blue: 0.91, alpha: 1)
imageView.image = image

And now it will work:

Now the image is orange!

Link to documentation.


Performance

Setting the image after configuring the UIImageView avoids repeating expensive operations:

// Good usage
let imageView = ...
imageView.tintColor = yourTintColor
var image = UIImage(named: "stackoverflow")!
image = image.imageWithRenderingMode(.AlwaysTemplate)
imageView.image = image        // Expensive

// Bad usage
var image = UIImage(named: "stackoverflow")!
image = image.imageWithRenderingMode(.AlwaysTemplate)
let imageView = ...
imageView.image = image        // Expensive
imageView.frame = ...          // Expensive
imageView.tintColor = yourTint // Expensive

Getting & setting the image asynchronously reduces scrolling and animation lag (especially when tinting an image inside of a UICollectionViewCell or UITableViewCell):

let imageView = cell.yourImageView
imageView.image = nil // Clear out old image
imageView.tintColor = UIColor(red: 0.35, green: 0.85, blue: 0.91, alpha: 1)

// Setting the image asynchronously reduces stuttering 
// while scrolling.  Remember, the image should be set as
// late as possible to avoid repeating expensive operations
// unnecessarily.
dispatch_async(dispatch_get_main_queue(), { () -> Void in
    var image = UIImage(named: "stackoverflow")!
    image = image.imageWithRenderingMode(.AlwaysTemplate)
    imageView.image = image
})

Upvotes: 36

Can Poyrazoğlu
Can Poyrazoğlu

Reputation: 34830

If you need high performance, I strongly recommend you to use GPUImage. You may download it at https://github.com/BradLarson/GPUImage

Upvotes: 2

Harsh Thakur
Harsh Thakur

Reputation: 348

try this

- (UIImage *)imageWithOverlayColor:(UIColor *)color
{
    CGRect rect = CGRectMake(0.0f, 0.0f, self.size.width, self.size.height);

    if (UIGraphicsBeginImageContextWithOptions) {
        CGFloat imageScale = 1.0f;
        if ([self respondsToSelector:@selector(scale)])  // The scale property is new with iOS4.
            imageScale = self.scale;
        UIGraphicsBeginImageContextWithOptions(self.size, NO, imageScale);
    }
    else {
        UIGraphicsBeginImageContext(self.size);
    }

    [self drawInRect:rect];

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetBlendMode(context, kCGBlendModeSourceIn);

    CGContextSetFillColorWithColor(context, color.CGColor);
    CGContextFillRect(context, rect);

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;
}

Upvotes: 0

Fernando Cervantes
Fernando Cervantes

Reputation: 2962

One way to accomplish this is to desaturate your image, and add a tint on top of that image with the color you desire.

Desaturate

-(UIImage *) getImageWithUnsaturatedPixelsOfImage:(UIImage *)image {
    const int RED = 1, GREEN = 2, BLUE = 3;

    CGRect imageRect = CGRectMake(0, 0, image.size.width*2, image.size.height*2);

    int width = imageRect.size.width, height = imageRect.size.height;

    uint32_t * pixels = (uint32_t *) malloc(width*height*sizeof(uint32_t));
    memset(pixels, 0, width * height * sizeof(uint32_t));

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(pixels, width, height, 8, width * sizeof(uint32_t), colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast);

    CGContextDrawImage(context, CGRectMake(0, 0, width, height), [image CGImage]);

    for(int y = 0; y < height; y++) {
        for(int x = 0; x < width; x++) {
            uint8_t * rgbaPixel = (uint8_t *) &pixels[y*width+x];
            uint32_t gray = (0.3*rgbaPixel[RED]+0.59*rgbaPixel[GREEN]+0.11*rgbaPixel[BLUE]);

            rgbaPixel[RED] = gray;
            rgbaPixel[GREEN] = gray;
            rgbaPixel[BLUE] = gray;
        }
    }

    CGImageRef newImage = CGBitmapContextCreateImage(context);

    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);
    free(pixels);

    UIImage * resultUIImage = [UIImage imageWithCGImage:newImage scale:2 orientation:0];
    CGImageRelease(newImage);

    return resultUIImage;
}

Overlay With Color

-(UIImage *) getImageWithTintedColor:(UIImage *)image withTint:(UIColor *)color withIntensity:(float)alpha {
    CGSize size = image.size;

    UIGraphicsBeginImageContextWithOptions(size, FALSE, 2);
    CGContextRef context = UIGraphicsGetCurrentContext();

    [image drawAtPoint:CGPointZero blendMode:kCGBlendModeNormal alpha:1.0];

    CGContextSetFillColorWithColor(context, color.CGColor);
    CGContextSetBlendMode(context, kCGBlendModeOverlay);
    CGContextSetAlpha(context, alpha);

    CGContextFillRect(UIGraphicsGetCurrentContext(), CGRectMake(CGPointZero.x, CGPointZero.y, image.size.width, image.size.height));

    UIImage * tintedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return tintedImage;
}

How-To

//For a UIImageView
yourImageView.image = [self getImageWithUnsaturatedPixelsOfImage:yourImageView.image];
yourImageView.image = [atom getImageWithTintedColor:yourImageView.image withTint:[UIColor redColor] withIntensity:0.7];

//For a UIImage
yourImage = [self getImageWithUnsaturatedPixelsOfImage:yourImage];
yourImage = [atom getImageWithTintedColor:yourImageView.image withTint:[UIColor redColor] withIntensity:0.7];

You can change the color of the tint to whatever you desire.

Upvotes: 7

Dan Rosenstark
Dan Rosenstark

Reputation: 69787

Check out my post (mostly just remixing code).

Edit: This code basically creates a new CGContext, draws a layer on it with the new color, and returns a new UIImage from that. I haven't gone in depth on this code in a while, but it seems to just draw a UIImage with the same shape as the original, so that's a limit (loses any detail in the image).

Upvotes: 2

user576924
user576924

Reputation:

There's a great post about this here: http://coffeeshopped.com/2010/09/iphone-how-to-dynamically-color-a-uiimage

The one caveat that I have with the current code is that using it on retina images will result in a loss of the higher 'resolution' for these images. I am currently looking for a solution for this...

Upvotes: 2

jstevenco
jstevenco

Reputation: 2953

Hmmm -- isn't the order of the bytes supposed to be RGBA? You are setting them as ARGB...

Upvotes: 0

mahboudz
mahboudz

Reputation: 39376

The RGB data you are operating on is just a copy. After you finish making changes, you need to turn that data back into an image.

I first make a new bitmap:

CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
ctx = CGBitmapContextCreate( malloc(dataSize), width, height,
                                8, //   CGImageGetBitsPerComponent(cgImage),
                                bytesPerRow, //CGImageGetBytesPerRow(cgImage),
                                space,
                                //kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big );
                                kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little);
                                //kCGImageAlphaPremultipliedFirst  | kCGBitmapByteOrder32Little);

    CGColorSpaceRelease( space );

// now draw the image into the context
CGRect rect = CGRectMake( 0, 0, CGImageGetWidth(cgImage), CGImageGetHeight(cgImage) );
CGContextDrawImage( ctx, rect, cgImage );

And get the pixels:

pixels = CGBitmapContextGetData( ctx );

Assuming that your pixel data came from pixels = CGBitmapContextGetData( ctx ); then take that context and build a new image from it:

CGImageRef newImg = CGBitmapContextCreateImage(ctx);
[[UIImage imageWithCGImage:newImg] drawInRect:rect];
CGImageRelease(newImg);

Upvotes: 1

Nava Carmon
Nava Carmon

Reputation: 4533

I think you can create another context with setting there context color to RGB you want to color your picture. Then draw your UIImage into that context and use that context instead of using directly your picture. This is a concept. This way you're creating offscreen buffer with a colored image. I didn't try this in cocoa, only in carbon, but i suppose it will work in the same way.

Upvotes: 0

Related Questions