Core Image的API是弱类型的--我们通过键值编码(KVC)来配置图像滤镜. 在使用参数的类型或名字时,我们都使用字符串来进行表示,这就十分容易出错.
Q: 开发新API,利用类型来避免这些原因导致的运行时错误,得到一组类型安全而且高度模块化的API.
复制代码
第一步: 创建滤镜类型
1.CIFilter对象几乎都是通过kCIInputImageKey键提供输入对象 2.再通过kCIOutputImageKey键取回处理后的对象 3.取回的结果可以作为下一个滤镜的输入值.
/// 滤镜类型
typealias Filter = (CIImage) -> CIImage
复制代码
第二步: 设置各种类型滤镜
1.模糊滤镜
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
}
}
复制代码
2.颜色生成滤镜
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
}
}
复制代码
3.合成滤镜
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() }
// 设置输出的图像裁剪为输入图像一致的尺寸
let cropRect = image.extent
return outputImage.cropped(to: cropRect)
}
}
复制代码
4.颜色叠层滤镜
func colorOverlay(color: UIColor) -> Filter {
return { image in
// 获取由颜色滤镜生成的图片
let overlay = self.colorGenerator(color: color)(image)
// 再次对生成的图片通过合成滤镜合成
return self.compositeSourceOver(overlay: overlay)(image)
}
}
复制代码
第三步: 设置滤镜以及组合滤镜
组合滤镜(复合函数,可类比为数学上常用的f(g(x)))
首先将图像模糊,然后再覆盖上一层灰色叠层.
复制代码
let testImage = UIImage(named: "test.jpg")!
let image = CIImage(image: testImage)
let blurRadius = 5.0
let overlayColor = UIColor.lightGray.withAlphaComponent(0.1)
// 方案1:
let blurredImage = blur(radius: blurRadius)(image!)
let overlaidImage = colorOverlay(color: overlayColor)(blurredImage)
imageView1.image = UIImage(ciImage: overlaidImage)
// 方案2:
let result = colorOverlay(color: overlayColor)(blur(radius: blurRadius)(image!))
imageView2.image = UIImage(ciImage: result)
// 方案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)
复制代码
拓展
方案4:通过重载运算符达到组合滤镜效果
复制代码
/// 重载运算符
precedencegroup ComparisonPrecedence {
associativity: left
higherThan: LogicalConjunctionPrecedence
}
///定义运算符>>>为左结合
infix operator >>>: ComparisonPrecedence
/// 滤镜将以从左到右的顺序被应用到图像上
func >>> (filter1: @escaping Filter, filter2: @escaping Filter) -> Filter {
return { image in filter2(filter1(image)) }
}
// 方案4: 运算符重载
let myFilter2 = blur(radius: blurRadius) >>> colorOverlay(color: overlayColor)
let result2 = myFilter2(image!)
imageView4.image = UIImage(ciImage: result2)
复制代码
番外篇:柯里化
柯里化: 如何将一个接受多参数的函数变换为一系列只接受单个参数的函数的过程
// 1: 常用函数
func add1(_ x: Int, _ y: Int) -> Int {
return x + y
}
// 2: 柯里化后的函数
func add2(_ x: Int) -> (Int) -> Int {
return { y in x + y }
}
复制代码
总结
- 安全:新构筑的API免除了由未定义键或强制类型转换失败导致的运行时错误.
- 模块化:使用>>>运算符更容易将滤镜进行组合.各个滤镜都可以成为可复用的组件,复杂滤镜可通过搭配组合而成.
- 清晰易懂:使用新API不用文档便可开始编写代码.