数百个 Core Image 过滤器包装为 CIImage 修饰符,以便与 SwiftUI 轻松集成
SwiftUICoreImage
帮助在 SwiftUI 的上下文中使用 Core Image。即使没有 SwiftUI 也很有用。
介绍
Core Image 是 macOS 和 iOS 中出色的图像处理工具包,但使用起来有点笨拙。即使在 Apple 将 Swift API 添加到许多过滤器 ( CoreImage.CIFilterBuiltins ) 之后,将过滤器链接到图像仍然非常乏味。
这个包的目的是提供一种更简单的方法来将多个过滤器链接到 CIImage 实例,然后将它们渲染到 SwiftUI(或任何其他上下文——不需要 SwiftUI)。
Image(ciImage: CIImage("Bernie.jpeg")
.sepiaTone(intensity: sepia)
.recropping { image in
image
.clampedToExtent(active: clamped)
.gaussianBlur(radius: gaussianBlurRadius)
}
)
.resizable()
.aspectRatio(contentMode: .fit)
显现
此包中包含:
- CIImage-Filters.swift
- 208 修饰符
CIImage
返回一个新的修改CIImage
(如果未修改则返回原始) - 返回新生成的 20 个静态函数
CIImage
- 208 修饰符
- CIImage-Extensions.swift
CIImage
来自资源名称和NSImage
/的便利初始值设定项UIImage
- 用于返回裁剪、缩放等的修饰符
CIImage
,以便更容易与 SwiftUI 一起使用 - 重载几个带有布尔参数
CIImage
的内置修饰函数active
- 图像扩展.swift
Image
从一个创建 SwiftUI 的便利初始化器CIImage
这是如何运作的
类似于 SwiftUI 视图修改器如何返回一个修改后的View
实例,这些修改器CIImage
通过创建相应的CIFilter
、为您连接inputImage
并返回结果来处理核心图像链接outputImage
。
在创建 SwiftUI 代码时,我认为您可以使用惰性修饰符,您可以在其中传入一些导致修饰符无效的参数,这一点很重要。(例如,为视图指定 1.0 的不透明度或 0.0 的填充。)
在这段代码中,我确保我们的每个图像修改器都带有惰性修改器:在某些情况下,它传递的参数显然没有效果(例如零强度、零半径);或者与另一张图片组合时为零背景图片;或布尔active
参数。如果指定的参数不会导致图像发生任何变化,则立即返回身份(自我)。
CIImage-Filters.swift 的内容是生成的源代码,使用我包含在此存储库中的代码(CIImage-Generation.swift
,不包含在包导入中)。这会循环遍历 Apple 提供的核心图像元数据 ( CIFilter.filterNames(inCategories: nil)
)。不幸的是,这个列表有些过时并且包含一些我已尽力克服的不一致之处。有一些 JSON 文件提供额外的元数据,例如实际上有在线文档的函数列表——56 个函数没有记录,因此需要一些猜测——或者修复丢失或过时的文档。您可能不需要运行此代码,除非您有一些特殊要求或列表已在未来(Ventura 后,iOS-16 后)操作系统版本中更新。
与 SwiftUI 一起使用
请记住,Core Image 操作实际上只是处理步骤的“配方”;直到需要将图像渲染为位图时才执行实际工作。
这段代码提供了Image
一个新的初始化程序来CGImage
从. 当 SwiftUI 需要渲染图像时,将 Core Image 渲染到屏幕上。NSImage
UIImage
Image
CIImage
那么,您的典型方法是创建一个Image
,使用内置初始化程序CIImage
之一或此处包含的便捷方法传入 created 以从资源名称或其他图像类型创建。
然后,只需将修饰符链接到它CIImage
以指示要修改的内容。
许多修饰符都很简单。例如:
Image(ciImage: CIImage("Halloween.jpeg")
.xRay()
)
如果您希望切换是否应用过滤器,请使用active
参数(默认值为true
):
Image(ciImage: CIImage("Halloween.jpeg")
.xRay(active: isMachineOn)
)
链接在中找到的任意数量的修饰符CIImage-Filters.swift
以构造所需的结果。
图像缩放
许多 Core Image 过滤器使用像素值作为参数。因此,可能需要在应用操作之前将图像缩放到适当的大小。例如,对 6000⨉4000 的图像应用 10 像素半径的模糊,然后将其缩小到 300⨉200 可能不会产生您想要的结果;也许您想先将图像缩放到 300⨉200,然后应用 10 像素半径模糊。
Core Image 提供了一个缩放操作(CILanczosScaleTransform
和lanczosScaleTransform()
),但这个包还包括更方便的替代方法:scaledToFill()
以及scaledToFit()
您在其中传递所需尺寸的地方。
这个的典型用法与GeometryReader
. 例如:
GeometryReader { geo in
let geoSize: CGSize = geo.frame(in: .local).integral.size
// Resize image to double the frame size, assuming we are on a retina display
let newSize: CGSize = CGSize(width: geoSize.width * 2,
height: geoSize.height * 2)
Image(ciImage: CIImage("M83.jpeg")
.scaledToFit(newSize)
.sharpenLuminance(sharpness: 1.0, radius: 5)
)
.resizable() // Make sure retina image is scaled to fit
.aspectRatio(contentMode: .fit)
}
在没有 SwiftUI 的情况下使用
根本不需要 SwiftUI。只需创建一个CIImage
并执行操作。然后,渲染为位图。
let tiledImage: CIImage = CIImage("HeyGoodMorning.png").
.triangleTile(center: .zero, angle: 0.356, width: 2.0)
imageView.image = UIImage(CIImage: tiledImage)
其他注意事项
如果您使用过 Core Image,您就会知道有时您需要调整图像的范围,例如在应用高斯模糊之前将图像限制为具有无限边缘,然后重新裁剪到图像的原始范围。为此,您可以使用recropping
后跟闭包的修饰符。该操作保存图像的范围,应用闭包中的任何内容,然后重新裁剪到该范围。在下面的示例中,图像 inciImage
被转换为沿其边缘的像素颜色在所有方向无限延伸的图像,然后它被模糊,然后在退出闭包时,返回的图像被重新裁剪。
ciImage
.recropping { image in
image
.clampedToExtent()
.gaussianBlur(radius: 10)
}
recropping
如果您发现过滤器(例如,comicEffect()
已经稍微增加了图像的范围并且您想将其限制为原始大小),修改器也很有用。
另一个有用的操作是replacing
。很像recropping
,只是它不会影响图像的范围。你传入一个闭包,它以你正在处理的图像开始;你的关闭返回一个新图像。这在需要传入背景图像的 Core Image 中进行合成操作时非常有用。如果您的操作链在背景图像上,并且您想在上面叠加一些东西怎么办?只需将您的操作包装起来.replacing
并返回合成图像。
ciImage
.replacing { backgroundImage in
ciImage2
.sourceAtopCompositing(backgroundImage: backgroundImage)
}
在这种情况下,中的图像ciImage2
是前景图像,放置在 之上backgroundImage
,然后返回到操作链。
使用包
在 Xcode 中,选择 File > Add Packages… 然后在搜索栏中输入这个存储库的 URL,然后从那里继续。
在你的代码中:
import SwiftUICoreImage
就是这样!
如果您能想到对生成的过滤器的代码或文档进行改进,或者找到任何其他有用的实用程序来在此工具包中操作 Core Images,请提交问题或请求请求!