14

I found

Create new UIImage by adding shadow to existing UIImage

and

UIImage, is there an easy way to make it darker or all black

But the selected answers do not work for me.

I have an UIImage, which may have some transparent pixels in it, I need to create a new UIImage with non-transparent pixels darkened, is there any way to do this? I was thinking of using UIBezierPath but I don't know how to do it for only non-transparent pixels.

Community
  • 1
  • 1
hzxu
  • 5,753
  • 11
  • 60
  • 95

4 Answers4

33

This is the class I use to color images even if they are transparent.

+ (UIImage *)colorizeImage:(UIImage *)image withColor:(UIColor *)color {
    UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGRect area = CGRectMake(0, 0, image.size.width, image.size.height);

    CGContextScaleCTM(context, 1, -1);
    CGContextTranslateCTM(context, 0, -area.size.height);

    CGContextSaveGState(context);
    CGContextClipToMask(context, area, image.CGImage);

    [color set];
    CGContextFillRect(context, area);

    CGContextRestoreGState(context);

    CGContextSetBlendMode(context, kCGBlendModeMultiply);

    CGContextDrawImage(context, area, image.CGImage);

    UIImage *colorizedImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return colorizedImage;
}

To darken the image you would pass the method a black or gray UIColor with lowered transparency.

Benjohn
  • 13,228
  • 9
  • 65
  • 127
David Skrundz
  • 13,067
  • 6
  • 42
  • 66
  • 6
    Thank you! Suggested improvement: Use `UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale)` to retain the scale, otherwise the colorized image would be blurry on Retina displays. – Merott Jul 28 '13 at 12:13
  • 2
    @Merott I'm going to go ahead and edit the answer to include your suggestion. It may be moderator rejected, but if the OPs happy, hopefully they'll patch it back on. – Benjohn Jul 24 '15 at 10:12
  • This is very helpful. Thank you! – birdcage Sep 06 '16 at 18:00
16

How about trying a CoreImage Filter?

You could use the CIColorControls filter to adjust the input brightness and contrast to darken the image.

CIContext *context = [CIContext contextWithOptions:nil];
CIImage *inputImage = [[CIImage alloc] initWithImage:sourceImage]; //your input image

CIFilter *filter= [CIFilter filterWithName:@"CIColorControls"];
[filter setValue:inputImage forKey:@"inputImage"];
[filter setValue:[NSNumber numberWithFloat:0.5] forKey:@"inputBrightness"];

// Your output image
UIImage *outputImage = [UIImage imageWithCGImage:[context createCGImage:filter.outputImage fromRect:filter.outputImage.extent]];

Read more about the CIFilter parameters here:

http://developer.apple.com/library/mac/#documentation/graphicsimaging/reference/CoreImageFilterReference/Reference/reference.html%23//apple_ref/doc/filter/ci/CIColorControls

Tim Windsor Brown
  • 4,069
  • 5
  • 25
  • 33
brynbodayle
  • 6,546
  • 2
  • 33
  • 49
  • Nice solution, but note that it requires iOS 5 – Adam Dec 20 '12 at 18:31
  • Also, your code has a major memory leak (createCGImage must be released!), and (for me) doesn't work - where the accepted answer works perfectly. – Adam Dec 20 '12 at 19:04
10

Here's a quick Swift version, using a CIExposureAdjust CIFilter :)

  // Get the original image and set up the CIExposureAdjust filter
  guard let originalImage = UIImage(named: "myImage"),
    let inputImage = CIImage(image: originalImage),
    let filter = CIFilter(name: "CIExposureAdjust") else { return }

  // The inputEV value on the CIFilter adjusts exposure (negative values darken, positive values brighten)
  filter.setValue(inputImage, forKey: "inputImage")
  filter.setValue(-2.0, forKey: "inputEV")

  // Break early if the filter was not a success (.outputImage is optional in Swift)
  guard let filteredImage = filter.outputImage else { return }

  let context = CIContext(options: nil)
  let outputImage = UIImage(CGImage: context.createCGImage(filteredImage, fromRect: filteredImage.extent))

  myImageView.image = outputImage // use the filtered UIImage as required.
worthbak
  • 335
  • 3
  • 8
-2

Here's David's answer in Swift 5:

class func colorizeImage(_ image: UIImage?, with color: UIColor?) -> UIImage? {
    UIGraphicsBeginImageContextWithOptions(image?.size ?? CGSize.zero, _: false, _: image?.scale ?? 0.0)

    let context = UIGraphicsGetCurrentContext()
    let area = CGRect(x: 0, y: 0, width: image?.size.width ?? 0.0, height: image?.size.height ?? 0.0)

    context?.scaleBy(x: 1, y: -1)
    context?.translateBy(x: 0, y: -area.size.height)

    context?.saveGState()
    context?.clip(to: area, mask: (image?.cgImage)!)

    color?.set()
    context?.fill(area)

    context?.restoreGState()

    if let context = context {
        context.setBlendMode(.multiply)
    }

    context!.draw((image?.cgImage)!, in: area)

    let colorizedImage = UIGraphicsGetImageFromCurrentImageContext()

    UIGraphicsEndImageContext()

    return colorizedImage
}
Andy
  • 1,815
  • 2
  • 22
  • 49