【问题标题】:On iOS, can you add multiple CIFilters to a SpriteKit node?在 iOS 上,你可以将多个 CIFilters 添加到一个 SpriteKit 节点吗?
【发布时间】:2019-08-28 10:45:40
【问题描述】:

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

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

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

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

【问题讨论】:

  • 我不使用 SpriteKit,但 SKEffectNode 的文档说您可以对其应用 CIFilter。所以我想如果你可以only应用一个过滤器,你应该能够(1)子类CIFilter和(2)链接你想要的过滤器,然后(3)可选地设置属性它们的动态变化。接下来,您可以 (4) 子类化 CIFilterConstructor,以便您可以 (5) 注册新的 CIFilter,并且应该像使用任何其他 CIFilter 一样使用它。如果您认为这是一条不错的路线,我可以发布一些示例代码。
  • 很好的建议。这种方法奏效了。我想发布我的代码,但我也想感谢您的回答。如果您想将此文本作为答案发布,我会将我的代码添加到其中(如有必要,您可以随时改进。)

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


【解决方案1】:

按照 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") }
}

【讨论】:

  • 很高兴我能帮上忙。
【解决方案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()

从那里,您可以像使用任何其他 CIFilter 一样使用 BlurThenColor。实例化它,使用setValue,然后调用outputImage

请注意,由于inputImage 的强制解包和/或拼写错误,此代码崩溃。我相信你可以让它更安全 - 但请放心,我已经测试过它并且它有效。 (我创建了这个自定义过滤器并将其替换为不会发生强制展开的应用程序。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多