在iOS上,你可以在CIFilter
上添加多个SKEffectsNode
吗?
CIFilterGenerator
看起来像我想要的,但它不适用于iOS。
我知道你可以通过传递一个的输出作为下一个的输入来使用multiple filters on an image,但是如果你想影响非图像节点则没有用。
这是否意味着我必须创建一个SKEffectNode
的人工层次结构,并为每个人添加一个过滤器,我的实际内容位于最底层?有没有更好的办法?
如果很难或不可能将多个CIFilter
调用“链接”在一起以达到预期的效果 - 可能是由于某个类具有单一属性,解决此问题的一种方法是执行以下操作:
CIFilter
,覆盖你需要的一切。这可能包括attributes
,setValue(forKey:)
,最重要的是,outputImage
。CIFilterConstructor
,并创建一个registerFilter()
方法。例如,假设您希望组合高斯模糊,然后为图像添加单色红色调。最简单的是你可以这样做:
class BlurThenColor:CIFilter {
let blurFilter = CIFilter(name: "CIGaussianBlur")
override public var attributes: [String : Any] {
return [
kCIAttributeFilterDisplayName: "Blur then Color",
"inputImage": [kCIAttributeIdentity: 0,
kCIAttributeClass: "CIImage",
kCIAttributeDisplayName: "Image",
kCIAttributeType: kCIAttributeTypeImage]
]
}
override init() {
super.init()
}
@available(*, unavailable) required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func setValue(_ value: Any?, forKey key: String) {
switch key {
case "inputImage":
blurFilter?.setValue(inputImage, forKey: "inputImage")
default:
break
}
}
override public var outputImage: CIImage {
return (blurFilter?.outputImage)! .applyingFilter("CIColorMonochrome", parameters: ["inputColor": CIColor(red: 1.0, green: 0.0, blue: 0.0)])
}
}
如果你想暴露更多的属性,你可以简单地将它们添加到attributes
和setValue(forKey:)
覆盖wist添加变量和setDefaults
。这里我只是使用默认值。
现在您已将效果链接到一个自定义过滤器中,您可以注册并使用它:
let CustomFilterCategory = "CustomFilter"
public class CustomFilterConstructor: NSObject, CIFilterConstructor {
static public func registerFilter() {
CIFilter.registerName(
"BlurThenColor",
constructor: CustomFilterConstructor(),
classAttributes: [
kCIAttributeFilterCategories: [CustomFilterCategory]
])
}
public func filter(withName name: String) -> CIFilter? {
switch name {
case "BlurThenColor":
return BlurThenColor()
default:
return nil
}
}
}
要使用它,只需确保注册过滤器(如果可能的话,我倾向于将其放入AppDelegate
):
CustomFilterConstructor.registerFilter()
从那里,你可以像任何其他BlurThenColor
一样使用CIFilter
。实例化它,使用setValue
,并调用outputImage
。
请注意,由于inputImage
和/或拼写错误的强制解包,此代码将崩溃。我相信你可以让它更安全 - 但请放心,我已经测试了这个并且它有效。 (我创建了这个自定义过滤器并将其替换为不会发生强制解包的应用程序。)
根据dfd的有用建议,我最终选择了这个简单的子类。我正在标记他的答案是正确的,因为a)他建议这种方法,我想给予他信任,并且b)它有更多关于使用CIFilterConstructor注册过滤器的一般用途信息。
有用的参考文献: - Apple Docs - Related Question - Free Core Image eBook
class MyChainFilter: CIFilter {
let chainedFilters: [CIFilter]
@objc dynamic var inputImage: CIImage?
init(filters: [CIFilter]) {
self.chainedFilters = filters
super.init()
}
// run filters in order on the specified source image
override var outputImage: CIImage? {
get {
let imageKey = "inputImage"
var workingImage = self.inputImage
for filter in chainedFilters {
assert(filter.inputKeys.contains(imageKey))
filter.setValue(workingImage, forKey: imageKey)
guard let result = filter.outputImage else {
assertionFailure("filter failed: \(filter.name)")
return nil
}
workingImage = result
}
return workingImage
}
}
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
}