首页 > 解决方案 > 在 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, 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.

我们不知道需要什么才能使用自定义CIFilter功能,CALayer,并且似乎找不到任何相关信息。

请注意,我们不是在寻找这种类型CIFilter或不同的方式来获得过滤器结果。我们需要一个习惯CIFilter来处理CALayer.

标签: swiftxcodemacoscalayercifilter

解决方案


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

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
        }
    }
}

编辑

奇怪的是,如果我添加一个计数器变量和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 上运行时,我在调试控制台中什么也outputImage得不到……从来没有请求过?


推荐阅读