CGContext .normal 和 .color 混合模式的 CIFilter 等价物
CIFilter equivalents for CGContext .normal and .color blend modes
我一直在尝试使用 CIFilter 复制 SKSpriteNode 着色方法,以便在另一种类型的 SKNode(例如 SKEffectNode)上使用它。
Colorizing a Sprite Node 下的 This reference 提供了一种简单地为 sprite 着色的方法。我怎样才能对任何 SKEffectSprite 做同样的事情。
我在 CoreGraphics 中为这种着色重新创建了一个仿真,向 UIImage 添加了一个 tinted
函数(见下文)。它工作正常并给出类似的结果。唯一的问题是 CIImage 和 CGImage 在 iOS 的许多版本上表现不佳(有问题!)。
我现在转向使用合成过滤器设置标准 CIFilter,但没有成功。
This page 提供了多个选项,但似乎很难将我在 CoreGraphics 中所做的事情与 CoreImage 中的任何特定过滤器相匹配,并且不知道公式。有没有办法让 CIFilter 现有的复合过滤器链获得与 SKSpriteNode 中的着色算法相似的结果?
extension UIImage {
public func tinted(color: UIColor, colorBlendFactor: CGFloat) -> UIImage {
UIGraphicsBeginImageContextWithOptions(size, false, scale)
defer { UIGraphicsEndImageContext() }
guard let context = UIGraphicsGetCurrentContext() else { return self }
context.translateBy(x: 0, y: size.height)
context.scaleBy(x: 1.0, y: -1.0)
let rect = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
context.setBlendMode(.normal)
UIColor.black.setFill()
context.fill(rect)
context.setBlendMode(.normal)
let cgImage = self.cgImage
context.draw(cgImage!, in: rect)
context.setBlendMode(.color)
color.withAlphaComponent(colorBlendFactor).setFill()
context.fill(rect)
context.setBlendMode(.destinationIn)
context.draw(cgImage!, in: rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
return image!
}
}
我能够弄清楚如何构建一个 CIFilter 来以 SKSpriteNode 混合颜色的方式为图像着色。从下图中,左边是苹果的混合算法。在右侧,我的 CIFilter 算法:
下面是用 Swift4 编写的代码:
import CoreImage
import UIKit
class TintFilter: CIFilter {
@objc dynamic var inputImage: CIImage?
var color = UIColor.black
var colorBlendFactor: CGFloat = 1.0
public init(color: UIColor, colorBlendFactor: CGFloat) {
super.init()
self.color = color
self.colorBlendFactor = colorBlendFactor
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func computeBlendComponent(_ component: CGFloat) -> CGFloat {
return 1.0 - (colorBlendFactor * (1.0 - component))
}
override open var outputImage: CIImage? {
guard let inputImage = self.inputImage else {
return nil
}
let colorGenerator = CIFilter(name:"CIConstantColorGenerator")
var hue: CGFloat = 0.0, saturation: CGFloat = 0.0, brightness: CGFloat = 0.0, alpha: CGFloat = 0.0
color.getHue(&hue, saturation: &saturation, brightness:&brightness, alpha:&alpha)
brightness += colorBlendFactor - 1.0
brightness = max(min(brightness, 1.0), 0.0)
let newColor = UIColor(hue: hue, saturation: saturation, brightness:brightness, alpha:alpha)
let inputColor = CIColor(cgColor: newColor.cgColor)
colorGenerator?.setValue(inputColor, forKey: "inputColor")
guard var colorOutputImage = colorGenerator?.outputImage else {
return nil
}
colorOutputImage = CIImage(color: CIColor(color: self.color.withAlphaComponent(self.colorBlendFactor)))
return colorOutputImage.applyingFilter("CIMultiplyCompositing", parameters: ["inputBackgroundImage": inputImage]).applyingFilter("CIMultiplyBlendMode", parameters: ["inputBackgroundImage": inputImage])
}
}
我一直在尝试使用 CIFilter 复制 SKSpriteNode 着色方法,以便在另一种类型的 SKNode(例如 SKEffectNode)上使用它。 Colorizing a Sprite Node 下的 This reference 提供了一种简单地为 sprite 着色的方法。我怎样才能对任何 SKEffectSprite 做同样的事情。
我在 CoreGraphics 中为这种着色重新创建了一个仿真,向 UIImage 添加了一个 tinted
函数(见下文)。它工作正常并给出类似的结果。唯一的问题是 CIImage 和 CGImage 在 iOS 的许多版本上表现不佳(有问题!)。
我现在转向使用合成过滤器设置标准 CIFilter,但没有成功。
This page 提供了多个选项,但似乎很难将我在 CoreGraphics 中所做的事情与 CoreImage 中的任何特定过滤器相匹配,并且不知道公式。有没有办法让 CIFilter 现有的复合过滤器链获得与 SKSpriteNode 中的着色算法相似的结果?
extension UIImage {
public func tinted(color: UIColor, colorBlendFactor: CGFloat) -> UIImage {
UIGraphicsBeginImageContextWithOptions(size, false, scale)
defer { UIGraphicsEndImageContext() }
guard let context = UIGraphicsGetCurrentContext() else { return self }
context.translateBy(x: 0, y: size.height)
context.scaleBy(x: 1.0, y: -1.0)
let rect = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
context.setBlendMode(.normal)
UIColor.black.setFill()
context.fill(rect)
context.setBlendMode(.normal)
let cgImage = self.cgImage
context.draw(cgImage!, in: rect)
context.setBlendMode(.color)
color.withAlphaComponent(colorBlendFactor).setFill()
context.fill(rect)
context.setBlendMode(.destinationIn)
context.draw(cgImage!, in: rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
return image!
}
}
我能够弄清楚如何构建一个 CIFilter 来以 SKSpriteNode 混合颜色的方式为图像着色。从下图中,左边是苹果的混合算法。在右侧,我的 CIFilter 算法:
下面是用 Swift4 编写的代码:
import CoreImage
import UIKit
class TintFilter: CIFilter {
@objc dynamic var inputImage: CIImage?
var color = UIColor.black
var colorBlendFactor: CGFloat = 1.0
public init(color: UIColor, colorBlendFactor: CGFloat) {
super.init()
self.color = color
self.colorBlendFactor = colorBlendFactor
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func computeBlendComponent(_ component: CGFloat) -> CGFloat {
return 1.0 - (colorBlendFactor * (1.0 - component))
}
override open var outputImage: CIImage? {
guard let inputImage = self.inputImage else {
return nil
}
let colorGenerator = CIFilter(name:"CIConstantColorGenerator")
var hue: CGFloat = 0.0, saturation: CGFloat = 0.0, brightness: CGFloat = 0.0, alpha: CGFloat = 0.0
color.getHue(&hue, saturation: &saturation, brightness:&brightness, alpha:&alpha)
brightness += colorBlendFactor - 1.0
brightness = max(min(brightness, 1.0), 0.0)
let newColor = UIColor(hue: hue, saturation: saturation, brightness:brightness, alpha:alpha)
let inputColor = CIColor(cgColor: newColor.cgColor)
colorGenerator?.setValue(inputColor, forKey: "inputColor")
guard var colorOutputImage = colorGenerator?.outputImage else {
return nil
}
colorOutputImage = CIImage(color: CIColor(color: self.color.withAlphaComponent(self.colorBlendFactor)))
return colorOutputImage.applyingFilter("CIMultiplyCompositing", parameters: ["inputBackgroundImage": inputImage]).applyingFilter("CIMultiplyBlendMode", parameters: ["inputBackgroundImage": inputImage])
}
}