Kingfisher 如何用 CIFilter 创建 CIImageProcessor 处理下载图片
【免费下载链接】KingfisherA lightweight, pure-Swift library for downloading and caching images from the web.项目地址: https://gitcode.com/GitHub_Trending/ki/Kingfisher
如果你手里已经有一个现成的CIFilter(CoreImage 滤镜),想在图片下载完成后自动应用它,Kingfisher 提供了CIImageProcessor协议和Filter包装类型来承接这个需求:你只需要把滤镜逻辑写成一个Transformer闭包,再让一个类型实现CIImageProcessor,通过options: [.processor(...)]传给setImage等加载方法,处理后的图片就会显示在视图上并写入缓存。
这一路径适用于 UIKit/AppKit 平台(即非 watchOS 目标):Filter.swift 整个文件被#if !os(watchOS)包裹,且 ImageProcessor.swift 的文档注释明确说明 watchOS 不支持包含 filter 的处理器,输入图片会原样返回。
Filter 和 CIImageProcessor 各自负责什么
两个类型的职责在 Filter.swift 中有明确定义:
Transformer是一个类型别名:(CIImage) -> CIImage?,定义「一张CIImage如何转成另一张」。Filter是对Transformer的包装结构体,通过Filter(transform:)初始化:
public struct Filter { let transform: Transformer /// Creates a ``Filter`` from a given ``Transformer``. /// /// - Parameter transform: The value defines how a `CIImage` can be converted to another one. public init(transform: @escaping Transformer) { self.transform = transform } // ... }CIImageProcessor是继承自ImageProcessor的协议,只要求提供一个filter属性:
public protocol CIImageProcessor: ImageProcessor { var filter: Filter { get } }协议扩展已经替你实现了process(item:options:),你不需要写处理逻辑本身:
extension CIImageProcessor { public func process(item: ImageProcessItem, options: KingfisherParsedOptionsInfo) -> KFCrossPlatformImage? { switch item { case .image(let image): return image.kf.apply(filter) case .data: return (DefaultImageProcessor.default |> self).process(item: item, options: options) } } }也就是说:输入已经是图片时直接应用滤镜;输入是原始数据时,先用DefaultImageProcessor解码成图片再应用滤镜。
第一步:用 Filter 包装 CIFilter
按照 CommonTasks_Processor.md 中 “Creating a processor from CIFilter” 一节,Filter闭包内接收一个CIImage,调用你的CIFilter并返回outputImage:
struct MyCIFilter: CIImageProcessor { let identifier = "com.yourdomain.myCIFilter" let filter = Filter { input in guard let filter = CIFilter(name: "xxx") else { return nil } filter.setValue(input, forKey: kCIInputBackgroundImageKey) return filter.outputImage } }代码中有两处占位内容,需要你替换后才能直接使用:
CIFilter(name: "xxx")中的"xxx"替换为你实际要使用的 CoreImage 滤镜名称,创建失败(返回nil)时闭包返回nil;identifier文档建议采用反向域名格式(见 ImageProcessor.swift 中对identifier的说明),并且不要用空字符串,因为空字符串已被DefaultImageProcessor保留。
除了自定义CIFilter(name:),Filter.swift 还内置了两个现成的Filter工厂,如果你的需求是上色或调整色彩,可以直接复用而不必手写滤镜:
// 用指定颜色给图片着色 let tintFilter: Filter = Filter.tint(.red) // 亮度 / 对比度 / 饱和度 / EV 调整 let colorFilter: Filter = Filter.colorControl( Filter.ColorElement(brightness: 0.0, contrast: 1.0, saturation: 1.1, inputEV: 0.0) )Filter.tint的实现展示了文档认可的滤镜写法:用CIConstantColorGenerator生成颜色图,再用CISourceOverCompositing叠加到输入图上,最后cropped(to: input.extent)。
第二步:把 processor 传给加载方法
processor 创建完成后,通过.processor选项传给 Kingfisher 的加载 API,写法与内置 processor 完全一致(引自 CommonTasks_Processor.md):
let processor = MyCIFilter() let url = URL(string: "https://example.com/my_image.png") imageView.kf.setImage(with: url, options: [.processor(processor)])其中https://example.com/my_image.png是文档中的示例 URL,替换为你自己的图片地址即可。
如果你想在这个滤镜之后再叠加其他处理(比如圆角),可以用|>运算符组合 processor:
// 先过 CIFilter,再裁圆角 let processor = MyCIFilter() |> RoundCornerImageProcessor(cornerRadius: 20) imageView.kf.setImage(with: url, options: [.processor(processor)])验证滤镜是否生效
CommonTasks_Processor.md 给出的成功标准是:图片设置流程会应用 processor,处理后的图片被发送到视图上,并以此存入缓存(“The processed image will then be sent to the image view and stored in the cache”)。具体到CIImageProcessor,Filter.swift 中KingfisherWrapper.apply(_:)展示了失败时的可观察行为:
public func apply(_ filter: Filter) -> KFCrossPlatformImage { guard let cgImage = cgImage else { assertionFailure("[Kingfisher] Tint image only works for CG-based image.") return base } let inputImage = CIImage(cgImage: cgImage) guard let outputImage = filter.transform(inputImage) else { return base } guard let result = ciContext.value.createCGImage(outputImage, from: outputImage.extent) else { assertionFailure("[Kingfisher] Can not make an tint image within context.") return base } // ... }据此可以判断三种情况:
- 滤镜正常生效:视图中显示的是滤镜处理后的图片,且处理结果以
identifier参与缓存键写入缓存。 CIFilter(name:)名字写错:闭包中guard不通过、返回nil,apply会原样返回base——视图显示未处理的原图,而不是报错中断。- 输入不是 CG-based 图片:会触发
assertionFailure("[Kingfisher] Tint image only works for CG-based image.")并返回原图。文档同时说明「Only CG-based images are supported」。
另外,identifier直接影响缓存命中:CommonTasks_Processor.md 强调 “It is your responsibility to keep it the same for processors with the same properties/functionality”——属性相同的 processor 必须返回相同identifier,否则同一张图会因键不同而无法复用缓存。
限制
- watchOS 不可用:
Filter相关代码在非 watchOS 平台才编译,watchOS 上含 filter 的处理器会直接返回输入图片。 - 滤镜只对 CG-based 图片生效,转换失败(滤镜返回
nil或CIContext渲染失败)时返回原图而不是抛出错误,调试时若发现“滤镜没生效”,优先检查滤镜名称和输入图片类型这两点。 identifier避免空字符串;组合 processor(|>)后的新 identifier 为"\(self.identifier)|>\(another.identifier)"(见 ImageProcessor.swift 中append(another:)的实现)。
深入阅读可以从 CommonTasks_Processor.md 的 “Creating your own processor” 一节继续,了解不依赖Filter、直接实现ImageProcessor的完整写法。
【免费下载链接】KingfisherA lightweight, pure-Swift library for downloading and caching images from the web.项目地址: https://gitcode.com/GitHub_Trending/ki/Kingfisher
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考