在iOS上,您可以将多个CIFilter添加到SpriteKit节点吗?

时间:2019-04-06 22:05:13

标签: ios sprite-kit core-image cifilter skeffectnode

在iOS上,您可以向CIFilter添加多个SKEffectsNode吗?

CIFilterGenerator似乎是我想要的,但在iOS上不可用。

我知道您可以通过将{的输出作为下一个的输入来使用multiple filters on an image,但是如果您想影响非图像节点,则无济于事。

这是否意味着我必须创建SKEffectNode的人工层次结构,并为每个过滤器添加一个过滤器,而我的实际内容才是最底部?有更好的方法吗?

2 个答案:

答案 0 :(得分:2)

在难以或不可能将多个CIFilter调用“链接”在一起以实现所需效果的情况下-可能由于具有单个属性的类,一种克服此问题的方法是执行以下操作:

  • 子类CIFilter,覆盖您需要的所有内容。这可能包括attributessetValue(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)])
    }
}

如果您希望公开更多属性,则可以简单地将它们添加到attributessetValue(forKey:)覆盖中,同时添加变量和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和/或拼写错误,此代码崩溃。我相信您可以使此操作更加安全-但请放心,我已经对此进行了测试并且可以正常工作。 (我创建了这个自定义过滤器,并在不会发生强制拆包的应用中将其替换。)

答案 1 :(得分:2)

遵循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") }
}