【问题标题】:Using custom CIFilter on CALayer shows no change to CALayer在 CALayer 上使用自定义 CIFilter 显示 CALayer 没有变化
【发布时间】:2021-08-16 19:26:59
【问题描述】:

我们正在尝试创建一个自定义 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, y: 0, width: 500, 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.

【问题讨论】:

  • 可能的愚蠢问题,但只是想提供帮助。您已标记此 [macos]。 iOS 上的 CoreImage 和 macOS 之间存在许多差异——不,我没有资格说明它们。你的代码有可能在 iOS 中运行吗?
  • @dfd 你不能在 iOS 中将过滤器附加到层,所以 OP 的代码在 iOS 设备上肯定会失败。
  • 正如我所说,一个可能很愚蠢的问题
  • 确实标记了 macOS 是有原因的 ????。正如代码所述,内置 CIFilter 在直接调用时确实有效。

标签: swift xcode macos calayer cifilter


【解决方案1】:

我假设您已经在代码中未显示的某个地方完成了此操作:

self.view.wantsLayer = true

但您也需要这样做:

self.view.layerUsesCoreImageFilters = true
    

结果没有那一行:

结果那一行:

(不要问我为什么“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, y: 0, width: 500, 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
        }
    }
}

编辑

奇怪的是,如果我添加一个 counter 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,
            kCIInputBrightnessKey: inputBrightness,
            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 上运行时,我在调试控制台中得到 nothing...outputImage 从未被请求?

【讨论】:

  • 我不确定我是否真的将 CI 过滤器附加到 Mac OS 上的层(这些天我主要在 iOS 上工作。)为什么你必须明确设置标志layerUsesCoreImageFilters,当图层有一个 Optional filters 属性默认为 nil 时,您必须显式设置为过滤器数组?看起来不必要的复杂。根据将过滤器属性设置为非零值,框架中需要 3 行代码来设置该属性。
  • @DuncanC - 我同意,虽然它可能有一个原因......我什至不做任何 MacOS 开发 - 它只是看起来像一个有趣的问题,所以我快速破解了它:)
  • @JorydeKort - 不知道该告诉你什么...我在 Intel Mac mini (2018) Xcode 12.4 上运行 10.15.4
  • @JorydeKort - 好的 - 我将不同的系统更新为 macOS 11.4 / Xcode 12.5.1,我看到的结果与您相同(即自定义过滤器无法修改图层)。 有些东西变了……
  • @JorydeKort - 我用另一个观察结果编辑了我的答案。但它并没有给我任何帮助。
【解决方案2】:

正如@DonMag 指出的那样,它应该与他描述的变化一起工作。不幸的是,我们今天收到了 Apple 的回复;

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

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

希望他们为所有阅读本文的人解决此问题。


编辑

真是个坏消息……目前在 macOS 12.2.1 上,它仍然有同样的问题,根据我们的票证没有发生任何事情。苹果似乎不想解决这个问题。对于那些在那里寻找的人:即使使用其他答案中描述的所有选项,这仍然不适用于CALayer。内置的 CIFilter 按预期工作。

请注意,在 CALayer 上使用相同的自定义 CIFilter 进行使用 AVVideoCompositionCoreAnimationTool 的导出确实有效!

【讨论】:

  • 绝对失望。首先,我们在天文馆圆顶中为我们的项目在辅助显示器上尝试了一些东西,花费了时间和资源来组织旅行以及进行测试,hdmi 连接、光学显示器连接和其他我们尝试的各种方法都失败了。然后我们从 reddit 线程中发现 mac os monterey 破坏了外部显示功能,老实说这非常重要。现在,在使用各种方法和默认过滤器进行了 3 小时或更长时间的测试之后,我认为我编写的着色器有问题。认真的吗?
猜你喜欢
  • 1970-01-01
  • 2021-12-17
  • 2012-03-30
  • 1970-01-01
  • 2011-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多