问题
I am trying to make a gradient and use it as an alpha mask. Right now, I am able to make an image similar to this (from black to transparent):
This is the code which I use to make all this:
private func createImage(width: CGFloat, height: CGFloat) -> CGImageRef?{
if let ciFilter = CIFilter(name: "CILinearGradient"){
let ciContext = CIContext()
ciFilter.setDefaults()
let startColor = CIColor(red: 0, green: 0, blue: 0, alpha: 0)
let endColor = CIColor(red: 0, green: 0, blue: 0, alpha: 1)
let startVector = CIVector(x: 0, y: height-10)
let endVector = CIVector(x: 0, y: height-22)
ciFilter.setValue(startColor, forKey: "inputColor0")
ciFilter.setValue(endColor, forKey: "inputColor1")
ciFilter.setValue(startVector, forKey: "inputPoint0")
ciFilter.setValue(endVector, forKey: "inputPoint1")
if let outputImage = ciFilter.outputImage {
let cgImage:CGImageRef = ciContext.createCGImage(outputImage, fromRect: CGRect(x: 0, y: 0, width: width, height: height))
return cgImage
}
}
return nil
}
For me, this way works pretty much perfect because I create a mask only once in my code (when GUI element is created) so performance is not affected at all.
The thing is that I actually need a resulting image (gradient texture) to look like this (gradients should have fixed height, but size of black area may vary):
Because CILinearGradient
filter doesn't have an inputImage
parameter, I can't chain two filters one after another. But rather I have to create an image, and apply a filter, then to create another image, and apply a new filter.
I solved this just like described above. Also I had to extend a process in three steps (create upper gradient, create middle, black area and create lower gradient) and then to combine all that into one image.
I wonder is there anything about CILinearGradient filter I am not aware of ? Maybe there is an easier way to attack this problem and make a complex gradient?
Note that I am using SpriteKit and solving this with CAGradientLayer is not a way I want to go.
回答1:
I'm not sure Core Image is the right tool for the job here — you'd probably be better off using CGGradient
functions to draw your image.
Aside: What you're asking would be possible but cumbersome in CoreImage. Think of it by analogy to what you'd do to create these images as a user of Photoshop or other common graphics software... the CoreImage way would be something like this:
- Create a layer for the upper gradient, fill the entire canvas with your gradient, and transform the layer to the proper position and size
- Create a layer for the middle black portion, fill the entire canvas with black, and transform the layer to the proper position and size
- Ditto #1, but for the bottom gradient.
You could do this with a combination of generator filters, transform filters, and compositing filters...
gradient -> transform -\
}-> composite -\
solid color -> transform -/ \
}-> composite
gradient -> transform ————————————————--/
But setting that up would be ugly.
Likewise, in Photoshop you could use selection tools and fill/gradient tools to create your gradient-fill-gradient design completely within a single layer... that's what drawing a single image using CoreGraphics is analogous to.
So, how to do that single-pass CG drawing? Something like this...
func createImage(width: CGFloat, _ height: CGFloat) -> CGImage {
let gradientOffset: CGFloat = 10 // white at bottom and top before/after gradient
let gradientLength: CGFloat = 12 // height of gradient region
// create colors and gradient for drawing with
let space = CGColorSpaceCreateDeviceRGB()
let startColor = UIColor.whiteColor().CGColor
let endColor = UIColor.blackColor().CGColor
let colors: CFArray = [startColor, endColor]
let gradient = CGGradientCreateWithColors(space, colors, [0,1])
// start an image context
UIGraphicsBeginImageContext(CGSize(width: width, height: height))
defer { UIGraphicsEndImageContext() }
let context = UIGraphicsGetCurrentContext()
// fill the whole thing with white (that'll show through at the ends when done)
CGContextSetFillColorWithColor(context, startColor)
CGContextFillRect(context, CGRect(x: 0, y: 0, width: width, height: height))
// draw top gradient
CGContextDrawLinearGradient(context, gradient, CGPoint(x: 0, y: gradientOffset), CGPoint(x: 0, y: gradientOffset + gradientLength), [])
// fill solid black middle
CGContextSetFillColorWithColor(context, endColor)
CGContextFillRect(context, CGRect(x: 0, y: gradientOffset + gradientLength, width: width, height: height - 2 * gradientOffset - 2 * gradientLength))
// draw bottom gradient
CGContextDrawLinearGradient(context, gradient, CGPoint(x: 0, y: height - gradientOffset), CGPoint(x: 0, y: height - gradientOffset - gradientLength), [])
return CGBitmapContextCreateImage(context)!
}
来源:https://stackoverflow.com/questions/34422374/creating-transparent-gradient-and-use-it-as-an-alpha-mask-in-spritekit