harinsa
harinsa

Reputation: 3196

Drawing Gradient with Core Graphic on UIImage result not consistent

Objective

I am trying to draw a linear gradient on top of an image.

Setup

I have an function call imageWithGradient which takes the colors and the imageView to put the image with gradient drawn on in. I added the imageView to the view using storyboard.

Problem

When I try the code on the simulator, sometimes the gradient wouldn't appear, sometimes the alpha wouldn't appear (solid color), sometimes the gradient would be flipped. Since I am running the same code with the same color it should apply the same gradient to all the images, but it rarely does.

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    var c1 = UIColor(red: 1.0, green: 0, blue: 0, alpha: 0.9).CGColor
    var c2 = UIColor(red: 0, green: 0, blue: 0, alpha: 0).CGColor

    imageWithGradient(self.imageView1, startColor: c1, endColor: c2)
    imageWithGradient(self.imageView2, startColor: c1, endColor: c2)
}

func imageWithGradient(imgView:UIImageView, startColor: CGColor, endColor: CGColor){

    var img = UIImage(named:"sample.jpg")!

    UIGraphicsBeginImageContext(img.size)
    var context = UIGraphicsGetCurrentContext()

    img.drawAtPoint(CGPointMake(0, 0))

    var colorSpace = CGColorSpaceCreateDeviceRGB()

    var startColorComp = CGColorGetComponents(startColor)
    var endColorComp = CGColorGetComponents(endColor)
    var colorComps = [startColorComp[0], endColorComp[0]]

    var locations:[CGFloat] = [0.0, 1.0]

    var gradient = CGGradientCreateWithColorComponents(colorSpace, &colorComps, &locations, 2)

    var startPoint = CGPointMake(img.size.width/2, img.size.height/2)
    var endPoint = CGPointMake(img.size.width/2, img.size.height)

    CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0)

    imgView.image = UIGraphicsGetImageFromCurrentImageContext()

    UIGraphicsEndImageContext()
}

Result

Gradient Flipped Gradient doesn't appear on the right I changed the color and add another imageView with this one

Upvotes: 0

Views: 688

Answers (1)

David Berry
David Berry

Reputation: 41226

You are essentially passing in an uninitialized block of memory for the color components.

var colorComps = [startColorComp[0], endColorComp[0]]

creates an array consisting of the first component of each color, so 2 components (not even the components you want)

Further on, you pass that array of 2 elements in for:

let gradient = CGGradientCreateWithColorComponents(colorSpace, colorComps, locations, 2)

where colorComps expects to be receiving an array of 8 floats, so the other 6 floats are basically random memory contents, hence the results vary widely.

You can use this function to extract the color components into an array of the actual color components. This builds a swift array of the 4 components of the color (or however many there were, but it should be 4)

func CGColorComponents(color:CGColorRef) -> [CGFloat] {
    let count = CGColorGetNumberOfComponents(color)
    var comps = CGColorGetComponents(color)

    var array = [CGFloat]()

    for i in 0 ..< count {
        array.append(comps.memory)
        comps = comps.successor()
    }

    return array
}

You can build the appropriate array of components using:

let startColorComp = CGColorComponents(startColor)
let endColorComp = CGColorComponents(endColor)
let colorComps =  startColorComp + endColorComp

Put it all together and you can create a function that'll overlay the gradient on your image:

func imageWithGradient(startColor: CGColor, endColor: CGColor) -> UIImage {
    var img = UIImage(named:"img_0002.png")!

    UIGraphicsBeginImageContext(img.size)
    var context = UIGraphicsGetCurrentContext()

    img.drawAtPoint(CGPointMake(0, 0))

    let colorSpace = CGColorSpaceCreateDeviceRGB()
    let componentCount = CGColorSpaceGetNumberOfComponents(colorSpace)
    let startColorComp = CGColorComponents(startColor)
    let endColorComp = CGColorComponents(endColor)
    let colorComps =  startColorComp + endColorComp
    let locations:[CGFloat] = [0.0, 1.0]

    let gradient = CGGradientCreateWithColorComponents(colorSpace, colorComps, locations, 2)

    let startPoint = CGPointMake(img.size.width/2, img.size.height/2)
    let endPoint = CGPointMake(img.size.width/2, img.size.height)

    CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0)

    let image = UIGraphicsGetImageFromCurrentImageContext()

    UIGraphicsEndImageContext()

    return image
}

Not quite how you were using it, but you should be able to dig the functionality out if you really want to maintain your semantics.

Better yet, just use CGGradientCreateWithColors instead:

let gradient = CGGradientCreateWithColors(colorSpace, [startColor, endColor], locations)

Upvotes: 3

Related Questions