Core Image’s API is weakly typed — we configure Image filters through key-value encoding (KVC). When we use a parameter’s type or name, we use a string representation, which is very error-prone.

Q: Develop new apis that use types to avoid runtime errors for these reasons, resulting in a set of type-safe and highly modular apis.Copy the code

Step 1: Create the filter type

1.CIFilter objects almost always use the kCIInputImageKey to provide input object 2. The processed object 3 is retrieved using the kCIOutputImageKey. The result retrieved can be used as the input value for the next filter.

// filter type
typealias Filter = (CIImage) - >CIImage
Copy the code

Step 2: Set up various types of filters

1. Blur filters

func blur(radius: Double) -> Filter {
        return { image in
            let parameters = [
                kCIInputRadiusKey : radius,
                kCIInputImageKey : image
                ] as [String : Any]
            guard let filter = CIFilter(name: "CIGaussianBlur", withInputParameters: parameters) else { fatalError()}guard let outputImage = filter.outputImage else { fatalError()}return outputImage
        }
    }
Copy the code

2. Color generation filters

func colorGenerator(color: UIColor) -> Filter {
        return {_ in
            let c = CIColor(color: color)
            let paramters = [kCIInputColorKey : c]
            guard let filter = CIFilter(name: "CIConstantColorGenerator", withInputParameters: paramters) else { fatalError()}guard let outputImage = filter.outputImage else { fatalError()}return outputImage
        }
    }
Copy the code

3. Synthetic filters

func compositeSourceOver(overlay: CIImage) -> Filter {
        return { image in
            let paramters = [
                kCIInputBackgroundImageKey : image,
                kCIInputImageKey : overlay
            ]
            guard let filter = CIFilter(name: "CISourceOverCompositing", withInputParameters: paramters) else { fatalError()}guard let outputImage = filter.outputImage else { fatalError()}// Set the output image to crop to the same size as the input image
            let cropRect = image.extent
            return outputImage.cropped(to: cropRect)
        }
    }
Copy the code

4. Color overlay filter

func colorOverlay(color: UIColor) -> Filter {
        return { image in
            // Get the image generated by the color filter
            let overlay = self.colorGenerator(color: color)(image)
            // Apply a composition filter to the generated image again
            return self.compositeSourceOver(overlay: overlay)(image)
        }
    }
Copy the code

Step 3: Set up filters and combination filters

Combined filters (compound functions, analogous to the mathematically common f(g(x))) blur the image first and then overlay it with a layer of grey.Copy the code
let testImage = UIImage(named: "test.jpg")!
let image = CIImage(image: testImage)
let blurRadius = 5.0
let overlayColor = UIColor.lightGray.withAlphaComponent(0.1)
        
/ / plan 1:
let blurredImage = blur(radius: blurRadius)(image!)
let overlaidImage = colorOverlay(color: overlayColor)(blurredImage)
imageView1.image = UIImage(ciImage: overlaidImage)
        
/ / solution 2:
letresult = colorOverlay(color: overlayColor)(blur(radius: blurRadius)(image!) ) imageView2.image =UIImage(ciImage: result)
        
/ / solution 3:
func composeFilters(filter1: @escaping Filter, _ filter2: @escaping Filter) -> Filter {
    return { image in filter2(filter1(image)) }
}
        
let myFilter1 = composeFilters(filter1: blur(radius: blurRadius), colorOverlay(color: overlayColor))
let result1 = myFilter1(image!)
imageView3.image = UIImage(ciImage: result1)
Copy the code

expand

Scheme 4: Use overloaded operators to achieve the combined filter effectCopy the code

Operator overload rule SE-0077

/// override the operator
precedencegroup  ComparisonPrecedence {
    associativity: left
    higherThan: LogicalConjunctionPrecedence
}

/// define operator >>> to be left associative
infix operator> > > :ComparisonPrecedence

/// Filters will be applied to the image from left to right
func >>> (filter1: @escaping Filter, filter2: @escaping Filter) -> Filter {
    return { image in filter2(filter1(image)) }
}

// Option 4: operator overload
let myFilter2 = blur(radius: blurRadius) >>> colorOverlay(color: overlayColor)
let result2 = myFilter2(image!)
imageView4.image = UIImage(ciImage: result2)
Copy the code

Extras: Currification

Corrification: The process of transforming a function that takes many parameters into a series of functions that take only one parameter

// 1: common functions
func add1(_ x: Int, _ y: Int) -> Int {
    return x + y
}
    
// 2: Currified function
func add2(_ x: Int)- > (Int) - >Int {
    return { y in x + y }
}
Copy the code

conclusion

  • Security: The newly constructed API eliminates runtime errors caused by undefined keys or cast failures.
  • Modularity: It is easier to combine filters using the >>> operator. Each filter can become a reusable component, and complex filters can be combined by collocation.
  • Clear and easy to understand: With the new API, you can start writing code without documentation.