createCGImage(_:from:) 没有使用正确的矩形

createCGImage(_:from:) not using correct rect

我正在尝试模糊图像的一部分:

extension UIImage {
    func blur(rect: CGRect, radius: CGFloat) -> UIImage? {
        let context = CIContext(options: nil)
        let inputImage = CIImage(cgImage: self.cgImage!)
        let filter = CIFilter(name: "CIGaussianBlur")
        filter?.setValue(inputImage, forKey: kCIInputImageKey)
        filter?.setValue(radius, forKey: kCIInputRadiusKey)
        let outputImage = filter?.outputImage
        if let outputImage = outputImage {
            let cgImage = context.createCGImage(outputImage, from: rect)
            if let cgImage = cgImage {
                return UIImage(cgImage: cgImage)
            }
        }
        return nil
    }
}

用法:

guard let blurredImage = self.imageView.image?.blur(rect: self.blur.frame, radius: 2.0) else { fatalError("could not blur image") }
let img = UIImageView(image: blurredImage)
img.frame = self.blur.frame
self.view.addSubview(img)

但是,当应用程序启动时,图片的错误部分模糊了。下面是图片;底部的矩形应该会模糊其后面的图像,但它会模糊其他部分。

问题是在 viewDidLoad 内添加任何叠加层都不能提供正确的框架尺寸 将其添加到侧面 viewDidAppear

您是否注意到您的插图看起来不仅模糊,而且放大了?这应该是一个提示...

UIKit 视图、框架和图像大小在 "points" 中,一个用于布局的 display-resolution-agnostic 单元。您的 iPhone X 显示屏与 iPhone 6/7/8 显示屏具有相同的点宽度,即使 iPhone X 具有更多像素。 (也就是说,它们的比例因子不同:iPhone 6/7/8 是 2x 比例,iPhone X 是 3x。)

CGImageCIImage 测量像素而不是点,因此在使用 UIKit 的视图尺寸时需要考虑屏幕的比例因子。由于您是从整个视图创建 image-to-be-blurred,然后仅渲染模糊图像的一部分,因此处理比例因子的地方将在您的 createCGImage(_:from:) 调用中——只需将原点和整个视图 contentScaleFactor.

的矩形大小

此外,请注意,您不一定需要通过 CGImageUIImageCIImage 并返回 — 您可以将外部图像视图的图像传递给 CIImage(image:), and the result of the filter to UIImage(ciImage:). In that case, UIKit and CoreImage manage the rendering context for you and can probably ensure better performance (say, by keeping the whole pipeline on GPU). But since you were using the render call to specify a crop rect, you'd need to do that elsewhere — for example, the CIImage cropped(to:)呼唤。

看来您的方向是正确的。有几件事需要解决...

可以通过将屏幕比例传递给您的方法并更新矩形的原点和大小来调整比例

由于 CoreGraphics 和 UIKit 坐标系之间的转换,模糊部分的定位不正确。为了解决这个问题,你只需要用你想要模糊的矩形的位置和高度减去 window 的高度(考虑到比例)

请查看下面的代码:

extension UIImage {
    func blur(rect: CGRect, radius: CGFloat, scale: CGFloat) -> UIImage? {

        if let cgImage = self.cgImage {
            let inputImage = CIImage(cgImage: cgImage)
            let context = CIContext(options: nil)
            let filter = CIFilter(name: "CIGaussianBlur")
            filter?.setValue(inputImage, forKey: kCIInputImageKey)
            filter?.setValue(radius, forKey: kCIInputRadiusKey)

            // Rect bounds need to be adjusted for based on Image's Scale and Container's Scale
            // Y Position Needs to be Inverted because of UIKit <-> CoreGraphics Coordinate Systems

            let newRect = CGRect(
                x: rect.minX * scale,
                y: ((((CGFloat(cgImage.height) / scale)) - rect.minY - rect.height) * scale),
                width: rect.width * scale,
                height: rect.height * scale
            )


            if let outputImage = filter?.outputImage,
                let cgImage = context.createCGImage(outputImage, from: newRect) {
                return UIImage(
                    cgImage: cgImage,
                    scale: scale,
                    orientation: imageOrientation
                )
            }
        }
        return nil
    }
}

let blurredImage = self.imageView.image?.blur(rect: self.blur.frame, radius: 2.0, scale: UIScreen.main.scale)