dimanche 9 juin 2019

How to reduce duplicate code when sublcassing CIFilter?

My app requires CIFilters, each one with a custom CIKernel which compiles from Metal. Swift doesn't allow instantiating CIFilter from a custom kernel, instead, I have to subclass base CIFilter for every custom filter I need and use the kernel inside the computed outputImage property. All these sublcasses I have scoped inside a class which conforms to CIFilterConstructor, and serves an instance of the required filter via the method

filter(withName name: String) -> CIFilter?

I can't seem to be able to make anything available from the outer scope inside the CIFilter sublcass. Which means that every single filter looks like this:

class deuteranopiaFilter: CIFilter {
        var deuteranopiaKernel: CIKernel?

        override var name: String {
            get {
                return "deuteranopia"
            }
            set {}
        }

        override init() {
            super.init()
            guard let url = Bundle.main.url(forResource: "default", withExtension: "metallib"),
                let data = try? Data(contentsOf: url)
                else { fatalError("Unable to get metallib") }

            guard let deuteranopiaKernel: CIColorKernel = try? CIColorKernel(functionName: "deuteranopia",
                                                                             fromMetalLibraryData: data)
                else { fatalError("Couldn't create kernel \(self.name)") }
            self.deuteranopiaKernel = deuteranopiaKernel
        }

        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
        }

        override class func registerName(_ name: String,
                                         constructor anObject: CIFilterConstructor,
                                         classAttributes attributes: [String : Any] = [:]) {
            CIFilter.registerName("Deuteranopia", constructor: FilterManager(), classAttributes: attributes)
        }

        @objc dynamic var inputImage: CIImage?
        override var outputImage: CIImage? {
            if let input = inputImage {
                let src = CISampler(image: input)
                return self.deuteranopiaKernel?.apply(extent: input.extent, roiCallback: {return $1}, arguments: [src])
            } else {
                return nil
            }
        }
    }

There's a lot of reused code here. Creating the Metal library and the Kernel, supplying the name property, the register class function, the output image property. Is there any way for me to avoid copy-pasting all of this for every single filter I'm going to have?

Aucun commentaire:

Enregistrer un commentaire