CIAffineClamp 过滤器不夹紧
CIAffineClamp filter does not clamp
我正在尝试做简单的事情。
我有图像视图,里面有图像。我想将图像平移图像大小的一半宽度,并在变换图像的边缘夹住像素,在 CIAffineClamp
的帮助下将它们向外扩展。但这是行不通的。它什么都不做。我附上了示例项目。
我的代码是:
func translateAndClamp(image: UIImage) -> UIImage {
guard let cgImage = image.cgImage else {
return image
}
let ciImage = CIImage(cgImage: cgImage)
var transform = CGAffineTransform.identity
transform = transform.translatedBy(x: image.size.width / 2, y: 0)
let filter = CIFilter(name: "CIAffineClamp")!
filter.setValue(ciImage, forKey: kCIInputImageKey)
filter.setValue(NSValue(cgAffineTransform: transform), forKey: "inputTransform")
let context = CIContext(options: [CIContextOption.useSoftwareRenderer: true])
guard let outputImage = filter.outputImage else {
return image
}
let extent = ciImage.extent.applying(transform)
guard let result = context.createCGImage(outputImage, from: extent) else {
return image
}
return UIImage(cgImage: result, scale: image.scale, orientation: image.imageOrientation)
}
更新
private func makeResultImageCroppedAndRotated(from image: UIImage) -> UIImage {
let angle = CGFloat(gestureHandler.rotation(from: imgView.transform))
guard let cgImage = image.cgImage else {
return image
}
let rotatedCGImage = rotateImage(image: cgImage, angle: angle)
let rotated = UIImage(cgImage: rotatedCGImage, scale: image.scale, orientation: image.imageOrientation)
let cropArea = makeCropArea(for: rotated, in: imgView, anchor: layoutContentView)
let cropped = cropImage(image: rotatedCGImage, to: cropArea)
return UIImage(cgImage: cropped, scale: image.scale, orientation: image.imageOrientation)
}
private func makeCropArea(for image: UIImage, in imageView: UIImageView, anchor: UIView) -> CGRect {
let boundingRect = LayoutCorrectionUtility.boundingAspectRect(for: image, inside: imageView)
let factor = LayoutCorrectionUtility.factor(for: image, insideBoundingRect: boundingRect)
let x = (anchor.bounds.origin.x - boundingRect.origin.x) * factor
let y = (anchor.bounds.origin.y - boundingRect.origin.y) * factor
let width = anchor.frame.width * factor
let height = anchor.frame.height * factor
return CGRect(x: x, y: y, width: width, height: height)
}
func rotateImage(image: CGImage, angle: CGFloat) -> CGImage {
let ciImage = CIImage(cgImage: image)
let newAngle = angle * CGFloat(-1)
var transform = CATransform3DIdentity
transform = CATransform3DRotate(transform, CGFloat(newAngle), 0, 0, 1)
let affineTransform = CATransform3DGetAffineTransform(transform)
let filter = CIFilter(name: "CIAffineClamp")
filter?.setValue(ciImage, forKey: kCIInputImageKey)
filter?.setDefaults()
filter?.setValue(NSValue(cgAffineTransform: affineTransform), forKey: "inputTransform")
let contex = CIContext(options: [CIContextOption.useSoftwareRenderer: true])
guard let outputImage = filter?.outputImage else {
return image
}
let extent = ciImage.extent.applying(affineTransform)
guard let result = contex.createCGImage(outputImage, from: extent) else {
return image
}
return result
}
func cropImage(image: CGImage, to rect: CGRect) -> CGImage {
let ciImage = CIImage(cgImage: image).clampedToExtent()
var transform = CGAffineTransform(scaleX: 1, y: -1)
transform = transform.translatedBy(x: 0, y: -CGFloat(image.height))
let contex = CIContext(options: [CIContextOption.useSoftwareRenderer: true])
guard let result = contex.createCGImage(ciImage, from: rect.applying(transform)) else {
return image
}
return result
}
问题是您还将转换应用于输出 extent
。这是对我有用的代码,带有一些注释:
func translateAndClamp(image: UIImage) -> UIImage {
// no need to use use the internal CGImage
guard let ciImage = CIImage(image: image) else {
return image
}
// Important: use the CIImage's width here since the UIImage's size
// is in points, not pixels.
let transform = CGAffineTransform(translationX: ciImage.extent.width / 2, y: 0)
// This does the same as CIAffineClamp, but is shorter and doesn't return an Optional.
let outputImage = ciImage.transformed(by: transform).clampedToExtent()
// I would advise against using the software renderer, but maybe you have your reasons.
// Also, if you want to call this method more then once, it's better to create the
// context on object initialization and re-use it every time since it's expensive to create.
let context = CIContext(options: [CIContextOption.useSoftwareRenderer: true])
// Important: don't apply the transform to the output extent!
// Think of this as the "window" on the image you want to draw.
// Since you moved the image "inside" the window with the transform,
// the window needs to be the original image's extent.
let extent = ciImage.extent
guard let result = context.createCGImage(outputImage, from: extent) else {
return image
}
return UIImage(cgImage: result, scale: image.scale, orientation: image.imageOrientation)
}
我正在尝试做简单的事情。
我有图像视图,里面有图像。我想将图像平移图像大小的一半宽度,并在变换图像的边缘夹住像素,在 CIAffineClamp
的帮助下将它们向外扩展。但这是行不通的。它什么都不做。我附上了示例项目。
我的代码是:
func translateAndClamp(image: UIImage) -> UIImage {
guard let cgImage = image.cgImage else {
return image
}
let ciImage = CIImage(cgImage: cgImage)
var transform = CGAffineTransform.identity
transform = transform.translatedBy(x: image.size.width / 2, y: 0)
let filter = CIFilter(name: "CIAffineClamp")!
filter.setValue(ciImage, forKey: kCIInputImageKey)
filter.setValue(NSValue(cgAffineTransform: transform), forKey: "inputTransform")
let context = CIContext(options: [CIContextOption.useSoftwareRenderer: true])
guard let outputImage = filter.outputImage else {
return image
}
let extent = ciImage.extent.applying(transform)
guard let result = context.createCGImage(outputImage, from: extent) else {
return image
}
return UIImage(cgImage: result, scale: image.scale, orientation: image.imageOrientation)
}
更新
private func makeResultImageCroppedAndRotated(from image: UIImage) -> UIImage {
let angle = CGFloat(gestureHandler.rotation(from: imgView.transform))
guard let cgImage = image.cgImage else {
return image
}
let rotatedCGImage = rotateImage(image: cgImage, angle: angle)
let rotated = UIImage(cgImage: rotatedCGImage, scale: image.scale, orientation: image.imageOrientation)
let cropArea = makeCropArea(for: rotated, in: imgView, anchor: layoutContentView)
let cropped = cropImage(image: rotatedCGImage, to: cropArea)
return UIImage(cgImage: cropped, scale: image.scale, orientation: image.imageOrientation)
}
private func makeCropArea(for image: UIImage, in imageView: UIImageView, anchor: UIView) -> CGRect {
let boundingRect = LayoutCorrectionUtility.boundingAspectRect(for: image, inside: imageView)
let factor = LayoutCorrectionUtility.factor(for: image, insideBoundingRect: boundingRect)
let x = (anchor.bounds.origin.x - boundingRect.origin.x) * factor
let y = (anchor.bounds.origin.y - boundingRect.origin.y) * factor
let width = anchor.frame.width * factor
let height = anchor.frame.height * factor
return CGRect(x: x, y: y, width: width, height: height)
}
func rotateImage(image: CGImage, angle: CGFloat) -> CGImage {
let ciImage = CIImage(cgImage: image)
let newAngle = angle * CGFloat(-1)
var transform = CATransform3DIdentity
transform = CATransform3DRotate(transform, CGFloat(newAngle), 0, 0, 1)
let affineTransform = CATransform3DGetAffineTransform(transform)
let filter = CIFilter(name: "CIAffineClamp")
filter?.setValue(ciImage, forKey: kCIInputImageKey)
filter?.setDefaults()
filter?.setValue(NSValue(cgAffineTransform: affineTransform), forKey: "inputTransform")
let contex = CIContext(options: [CIContextOption.useSoftwareRenderer: true])
guard let outputImage = filter?.outputImage else {
return image
}
let extent = ciImage.extent.applying(affineTransform)
guard let result = contex.createCGImage(outputImage, from: extent) else {
return image
}
return result
}
func cropImage(image: CGImage, to rect: CGRect) -> CGImage {
let ciImage = CIImage(cgImage: image).clampedToExtent()
var transform = CGAffineTransform(scaleX: 1, y: -1)
transform = transform.translatedBy(x: 0, y: -CGFloat(image.height))
let contex = CIContext(options: [CIContextOption.useSoftwareRenderer: true])
guard let result = contex.createCGImage(ciImage, from: rect.applying(transform)) else {
return image
}
return result
}
问题是您还将转换应用于输出 extent
。这是对我有用的代码,带有一些注释:
func translateAndClamp(image: UIImage) -> UIImage {
// no need to use use the internal CGImage
guard let ciImage = CIImage(image: image) else {
return image
}
// Important: use the CIImage's width here since the UIImage's size
// is in points, not pixels.
let transform = CGAffineTransform(translationX: ciImage.extent.width / 2, y: 0)
// This does the same as CIAffineClamp, but is shorter and doesn't return an Optional.
let outputImage = ciImage.transformed(by: transform).clampedToExtent()
// I would advise against using the software renderer, but maybe you have your reasons.
// Also, if you want to call this method more then once, it's better to create the
// context on object initialization and re-use it every time since it's expensive to create.
let context = CIContext(options: [CIContextOption.useSoftwareRenderer: true])
// Important: don't apply the transform to the output extent!
// Think of this as the "window" on the image you want to draw.
// Since you moved the image "inside" the window with the transform,
// the window needs to be the original image's extent.
let extent = ciImage.extent
guard let result = context.createCGImage(outputImage, from: extent) else {
return image
}
return UIImage(cgImage: result, scale: image.scale, orientation: image.imageOrientation)
}