在 CALayer 上使用自定义 CIFilter 显示 CALayer 没有变化

问题描述

我们正在尝试创建一个自定义 CIFilter添加到我们的 CALayer's. 之上,但是似乎只有认 CIFilters 可以在 CALayer. 上运行

我们在添加ViewController.swift 上创建了一个小型新项目:

import Cocoa
import CoreImage

class ViewController: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Create some layers to work with! (square with gradient color)
        let mainLayer = CALayer()
        let shapeLayer = CAShapeLayer()
        let gradientLayer = CAGradientLayer()
        gradientLayer.colors = [NSColor.red.cgColor,NSColor.white.cgColor,NSColor.yellow.cgColor,NSColor.black.cgColor]
        
        shapeLayer.path = CGPath(rect: CGRect(x: 0,y: 0,width: 500,height: 500),transform: nil)
        shapeLayer.fillColor = CGColor.black
        
        gradientLayer.frame = CGRect(x: 0,height: 500)
        gradientLayer.mask = shapeLayer
        
        gradientLayer.setAffineTransform(CGAffineTransform(translationX: 50,y: 50))
        mainLayer.addSublayer(gradientLayer)
        
        mainLayer.filters = []
        self.view.layer?.addSublayer(mainLayer)

        // Register the custom filter
        CustomFilterRegister.register()
        
        // Test with a normal image file,WORKS!
//      if let image = NSImage(named: "test"),let cgImage = image.cgImage(forProposedRect: nil,context: nil,hints: nil) {
//          if let filter = CIFilter(name: "CustomFilter") {
//              filter.setValue(CIImage(cgImage: cgImage),forKey: kCIInputimageKey)
//              let output = filter.outputimage
//              // WORKS! Image filtered as expected!
//          }
//      }
        
        // Does NOT work. No change in color of the layer!
        if let filter = CIFilter(name: "CustomFilter") {
            filter.name = "custom"
            mainLayer.filters?.append(filter)
        }

        // This works: mainLayer and sublayers are blurred!
//      if let filter = CIFilter(name: "CIGaussianBlur") {
//          filter.name = "blur"
//          mainLayer.filters?.append(filter)
//      }


    }
}
}

在开始构建自定义 CIFilter 之前,我们创建了一个简单的自定义 CIFilter. 以进行第一次尝试

class CustomFilter: CIFilter {
    
    // Error in xcode if you don't add this in!
    override class var supportsSecureCoding: Bool {
        return true
    }
        
    @objc dynamic var inputimage: CIImage?
    @objc dynamic var inputSaturation: CGFloat = 1
    @objc dynamic var inputBrightness: CGFloat = 0
    @objc dynamic var inputContrast: CGFloat = 1
    override func setDefaults() {
        inputSaturation = 1
        inputBrightness = 0
        inputContrast = 2
    }
    
    override public var outputimage: CIImage? {
        guard let image = inputimage else {
            return nil
        }
        return image.applyingFilter("CIPhotoEffectProcess")
            .applyingFilter("CIColorControls",parameters: [
                kCIInputSaturationKey: inputSaturation,kCIInputBrightnessKey: inputBrightness,kCIInputContrastKey: inputContrast
            ])
    }
}

class CustomFilterRegister: CIFilterConstructor {
    static func register() {
        CIFilter.registerName(
            "CustomFilter",constructor: CustomFilterRegister(),classAttributes: [
                kCIAttributeFilterCategories: [kCICategoryBlur,kCICategoryVideo,kCICategoryStillImage]
            ])
    }
    func filter(withName name: String) -> CIFilter? {
        switch name {
        case "CustomFilter":
            return CustomFilter()
        default:
            return nil
        }
    }
}

在 ViewController 中,我们添加代码以使用普通图像进行测试。这确实有效,因此过滤器似乎没问题。我们还尝试了一个认的 CIGaussianBlur,它确实适用于 CALayer.

我们不知道让自定义 CIFilterCALayer, 一起工作需要什么,而且似乎找不到任何有关它的信息。

请注意,我们不是在寻找这种类型的 CIFilter 或其他获取过滤器结果的方法。我们需要一个自定义CIFilter 来处理 CALayer.

解决方法

我假设您在代码中未显示的地方执行了此操作:

self.view.wantsLayer = true

但你也想这样做:

self.view.layerUsesCoreImageFilters = true
    

结果没有那一行:

enter image description here

结果那一行:

enter image description here

(不要问我为什么“CIGaussianBlur”仍然有效...)


编辑 - 我用来产生上述输出的确切代码:

import CoreImage

class ViewController: NSViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.view.wantsLayer = true
        self.view.layerUsesCoreImageFilters = true
        
        // Create some layers to work with! (square with gradient color)
        let mainLayer = CALayer()
        let shapeLayer = CAShapeLayer()
        let gradientLayer = CAGradientLayer()
        gradientLayer.colors = [NSColor.red.cgColor,NSColor.white.cgColor,NSColor.yellow.cgColor,NSColor.black.cgColor]
        
        shapeLayer.path = CGPath(rect: CGRect(x: 0,y: 0,width: 500,height: 500),transform: nil)
        shapeLayer.fillColor = CGColor.black
        
        gradientLayer.frame = CGRect(x: 0,height: 500)
        gradientLayer.mask = shapeLayer
        
        gradientLayer.setAffineTransform(CGAffineTransform(translationX: 50,y: 50))
        mainLayer.addSublayer(gradientLayer)
        
        mainLayer.filters = []
        self.view.layer?.addSublayer(mainLayer)

        // Register the custom filter
        CustomFilterRegister.register()
        
        let t = 2
        
        if t == 1 {
            // Test with a normal image file,WORKS!
            if let image = NSImage(named: "test"),let cgImage = image.cgImage(forProposedRect: nil,context: nil,hints: nil) {
                if let filter = CIFilter(name: "CustomFilter") {
                    filter.setValue(CIImage(cgImage: cgImage),forKey: kCIInputImageKey)
                    let output = filter.outputImage
                    // WORKS! Image filtered as expected!
                    print()
                }
            }
        }
        
        else if t == 2 {
            // 
            // Does NOT work. No change in color of the layer!
            // 
            // This NOW works for me
            // 
            if let filter = CIFilter(name: "CustomFilter") {
                filter.name = "custom"
                mainLayer.filters?.append(filter)
            }
            
        } else {
            
            // This works: mainLayer and sublayers are blurred!
            if let filter = CIFilter(name: "CIGaussianBlur") {
                filter.name = "blur"
                mainLayer.filters?.append(filter)
            }
            
        }
        
    }
}

class CustomFilter: CIFilter {
    
    // Error in xcode if you don't add this in!
    override class var supportsSecureCoding: Bool {
        return true
    }
    
    @objc dynamic var inputImage: CIImage?
    @objc dynamic var inputSaturation: CGFloat = 1
    @objc dynamic var inputBrightness: CGFloat = 0
    @objc dynamic var inputContrast: CGFloat = 1
    override func setDefaults() {
        inputSaturation = 1
        inputBrightness = 0
        inputContrast = 2
    }
    
    override public var outputImage: CIImage? {
        guard let image = inputImage else {
            return nil
        }
        return image.applyingFilter("CIPhotoEffectProcess")
            .applyingFilter("CIColorControls",parameters: [
                kCIInputSaturationKey: inputSaturation,kCIInputBrightnessKey: inputBrightness,kCIInputContrastKey: inputContrast
            ])
    }
}

class CustomFilterRegister: CIFilterConstructor {
    static func register() {
        CIFilter.registerName(
            "CustomFilter",constructor: CustomFilterRegister(),classAttributes: [
                kCIAttributeFilterCategories: [kCICategoryBlur,kCICategoryVideo,kCICategoryStillImage]
            ])
    }
    func filter(withName name: String) -> CIFilter? {
        switch name {
        case "CustomFilter":
            return CustomFilter()
        default:
            return nil
        }
    }
}

编辑

奇怪的是,如果我添加一个像这样的计数器 var 和 print() 语句:

var applyCount: Int = 0

override public var outputImage: CIImage? {
    print("getting outputImage...")
    guard let image = inputImage else {
        return nil
    }
    applyCount += 1
    print("apply",applyCount)
    return image.applyingFilter("CIPhotoEffectProcess")
        .applyingFilter("CIColorControls",parameters: [
            kCIInputSaturationKey: inputSaturation,kCIInputContrastKey: inputContrast
        ])
}

在 macOS 10.15.4 / Xcode 12.4 上,我在调试控制台中得到这个:

getting outputImage...
apply 1
getting outputImage...
apply 2
getting outputImage...
apply 3
getting outputImage...
apply 4
getting outputImage...
apply 5
getting outputImage...
apply 6
getting outputImage...
apply 7
getting outputImage...
apply 8
getting outputImage...
apply 9
getting outputImage...
apply 10
getting outputImage...
apply 11
getting outputImage...
apply 12
getting outputImage...
apply 13
getting outputImage...
apply 14
getting outputImage...
apply 15
getting outputImage...
apply 16

(例如,当窗口改变大小时,它会继续被重复调用)。

但是,在 macOS 11.4 / Xcode 12.5.1 上运行时,我在调试控制台中什么都没有...从不请求 outputImage? >

,

正如@DonMag 指出的那样,它应该适用于他描述的更改。不幸的是,我们今天收到了 Apple 的回复;

此时,有一个错误阻止 CALayer 上的自定义 CIFilters 工作。目前没有针对此错误的解决方法。

当我们提交错误时,我会在此处为感兴趣的人添加链接。但此时您无法在 macOS 11 上向 CALayer 添加自定义 CIFilter。

希望他们为所有阅读本文的人修复它以寻求解决方案。