嘿,我正在构建一个裁剪文档应用程序。 我正在从相机获取从 CIImage 转换为 UIImage 的图像。 一旦它到达我的 SwiftUI 视图,我就会在上面绘制一个裁剪视图。 然后我使用 UIBezierPath 裁剪视图。
裁剪中的某些内容有些错误,因为形状是正确的,但图片的内容却不正确。
这是持有我观点的结构:
struct PathPoint {
var topLeft: CGPoint
var topRight: CGPoint
var bottomLeft: CGPoint
var bottomRight: CGPoint
init(topLeft: CGPoint = .zero, topRight: CGPoint = .zero, bottomLeft: CGPoint = .zero, bottomRight: CGPoint = .zero) {
self.topLeft = topLeft
self.topRight = topRight
self.bottomLeft = bottomLeft
self.bottomRight = bottomRight
}
func createBezierPath() -> UIBezierPath {
let path = UIBezierPath()
path.move(to: topLeft)
path.addLine(to: topRight)
path.addLine(to: bottomRight)
path.addLine(to: bottomLeft)
path.close() // Close the path to form a rectangle
return path
}
func createCGPath() -> CGPath {
createBezierPath().cgPath
}
}
这里是裁剪代码的地方:
func imageByApplyingClippingBezierPath(_ path: UIBezierPath) -> UIImage? {
guard let maskedImage = imageByApplyingMaskingBezierPath(path),
let cgImage = maskedImage.cgImage else { return nil }
guard let cropped = cgImage.cropping(to: path.bounds) else {
print(#function, "could not crop for bounds \(path.bounds)")
return nil
}
let croppedImage = UIImage(cgImage: cropped)
return croppedImage
}
func imageByApplyingMaskingBezierPath(_ path: UIBezierPath) -> UIImage? {
UIGraphicsBeginImageContext(size)
let context = UIGraphicsGetCurrentContext()!
context.saveGState()
path.addClip()
draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
guard let maskedImage = UIGraphicsGetImageFromCurrentImageContext() else { return nil }
context.restoreGState()
UIGraphicsEndImageContext()
return maskedImage
}
正如@DonMag 注意到我缺少缩放部分,其中 UIImage 大小得到适合我们应用裁剪边界/绘制的屏幕的大小。
为了像这样转换我的 CIImage:
func convertCIImageToUIImage(ciImage: CIImage) -> UIImage? {
let context = MetalDevice.shared.context
if let cgImage = context.createCGImage(ciImage, from: ciImage.extent) {
return UIImage(cgImage: cgImage).scale(newSize: self.view.bounds.size)
}
return nil
}
其中 UIImage.scale(newSize:) 扩展名是:
func scale(newSize: CGSize) -> UIImage {
let scaleFactorWidth = newSize.width / self.size.width
let scaleFactorHeight = newSize.height / self.size.height
let scale = min(scaleFactorWidth, scaleFactorHeight)
let newHeight = self.size.height * scale
let newWidth = self.size.width * scale
UIGraphicsBeginImageContextWithOptions(.init(width: newWidth, height: newHeight), true, 0.0)
self.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
let newImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage ?? self
}
请注意,可以使用 CILanczosScaleTransform 过滤器直接缩放 CIImage。
并且它会被应用在扩展中:
func lanczosScale(to bounds: CGSize) -> UIImage? {
let targetWidth = bounds.width
let targetHeight = bounds.height
let originalWidth = extent.width
let originalHeight = extent.height
let scaleX = targetWidth / originalWidth
let scaleY = targetHeight / originalHeight
let scale = min(scaleX, scaleY)
let scaleFilter = CIFilter(name: "CILanczosScaleTransform")!
scaleFilter.setValue(self, forKey: kCIInputImageKey)
scaleFilter.setValue(scale, forKey: kCIInputScaleKey)
scaleFilter.setValue(1.0, forKey: kCIInputAspectRatioKey)
if let outputImage = scaleFilter.outputImage {
let context = MetalDevice.shared.context
if let cgImage = context.createCGImage(outputImage, from: outputImage.extent) {
let resizedImage = UIImage(cgImage: cgImage)
return resizedImage
}
return nil
}
return nil
}
现在的问题是,为了保持分辨率,我应该直接对缓冲区应用一些大小调整吗?但这还需要进一步研究。