使用UIGraphicImageRenderer的CGContext重绘图像,翻转图像如何防止翻转?

问题描述

给出一个图像,当我想转换为标准的sRGB时,我可以使用CGContext来帮助绘制它,如下所示。

给出原始图像

enter image description here

当我直接使用CGContext时,它会正确重绘

extension UIImage {
    func toSRGB() -> UIImage {        
        guard let cgImage = cgImage,let colorSpace = CGColorSpace(name: CGColorSpace.sRGB),let cgContext = CGContext(
                data: nil,width: Int(size.width),height: Int(size.height),bitsPerComponent: 8,bytesPerRow: 0,space: colorSpace,bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)
            else { return self }
        cgContext.draw(cgImage,in: CGRect(origin: .zero,size: size))

        guard let newCGImage = cgContext.makeImage()
            else { return self }

        return UIImage.init(cgImage: newCGImage)
    }
}

但是,如果我使用CGContext中的UIGraphicImageRenderer

extension UIImage {
    func toSRGB() -> UIImage {
        guard let cgImage = cgImage else { return self }

        let renderer = UIGraphicsImageRenderer(size: size)
        return renderer.image { ctx in
            ctx.cgContext.draw(cgImage,size: size))
        }
    }
}

图像如下翻转。为什么会发生翻转,如何避免翻转?

enter image description here

解决方法

无需获取cgImage。您可以简单地绘制UIImage:

extension UIImage {
    func toSRGB() -> UIImage {
        UIGraphicsImageRenderer(size: size).image { _ in
            draw(in: CGRect(origin: .zero,size: size))
        }
    }
}

图像翻转的原因是difference between coordinate system in UIKit and Quartz。 UIKit垂直原点位于顶部,而Quartz垂直原点位于底部。解决此问题的最简单方法是应用CGAffineTransform将其缩放为负一,然后向后平移高度距离:

extension CGAffineTransform {
    static func flippingVerticaly(_ height: CGFloat) -> CGAffineTransform {
        var transform = CGAffineTransform(scaleX: 1,y: -1)
        transform = transform.translatedBy(x: 0,y: -height)
        return transform
    }
}

extension UIImage {
    func toSRGB() -> UIImage {
        guard let cgImage = cgImage else { return self }
        return UIGraphicsImageRenderer(size: size).image { ctx in
            ctx.cgContext.concatenate(.flippingVerticaly(size.height))
            ctx.cgContext.draw(cgImage,in: CGRect(origin: .zero,size: size))
        }
    }
}

编辑/更新:

要保持比例为1倍,您可以传递图像渲染器格式:

extension UIImage {
    func toSRGB() -> UIImage {
        guard let cgImage = cgImage else { return self }
        let format = imageRendererFormat
        format.scale = 1
        return UIGraphicsImageRenderer(size: size,format: format).image { ctx in
            ctx.cgContext.concatenate(.flippingVerticaly(size.height))
            ctx.cgContext.draw(cgImage,size: size))
        }
    }
}
,

探索之后,只需补充说明一下我对它们之间差异的注意事项。

5x3像素的原始图像

enter image description here

直接使用CGContext时

extension UIImage {
    func toSRGB() -> UIImage {        
        guard let cgImage = cgImage,let colorSpace = CGColorSpace(name: CGColorSpace.sRGB),let cgContext = CGContext(
                data: nil,width: Int(size.width),height: Int(size.height),bitsPerComponent: 8,bytesPerRow: 0,space: colorSpace,bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)
            else { return self }
        cgContext.draw(cgImage,size: size))

        guard let newCGImage = cgContext.makeImage()
            else { return self }

        return UIImage.init(cgImage: newCGImage)
    }
}

我们保留了相同的像素(5x3)。这是该解决方案的优点,但是它需要一个来访问CGContext并直接对其进行操作。

enter image description here

第二种方法是UIGraphicsImageRenderer并访问内部ctx

extension UIImage {
    func toSRGB() -> UIImage {
        guard let cgImage = cgImage else { return self }

        let renderer = UIGraphicsImageRenderer(size: size)
        return renderer.image { ctx in
            ctx.cgContext.draw(cgImage,size: size))
        }
    }
}

使用这种方法,

  • 看起来颠倒了。
  • 像素的宽度和高度(10x6)均增加了一倍,以使颜色边缘更平滑。

enter image description here

要解决上下颠倒的情况,我们需要使用@ p @ leoDabus的答案将其翻转

extension CGAffineTransform {
    static func flippingVerticaly(_ height: CGFloat) -> CGAffineTransform {
        var transform = CGAffineTransform(scaleX: 1,y: -height)
        return transform
    }
}

extension UIImage {
    func toSRGB() -> UIImage {
        guard let cgImage = cgImage else { return self }
        return UIGraphicsImageRenderer(size: size).image { ctx in
            ctx.cgContext.concatenate(.flippingVerticaly(size.height))
            ctx.cgContext.draw(cgImage,size: size))
        }
    }
}

现在,图像具有正确的方向,但宽度和高度仍然是原来的两倍(10x6)。

enter image description here

翻转的结果与@LeoDabus提供的简化答案相同

extension UIImage {
    func toSRGB() -> UIImage {
        UIGraphicsImageRenderer(size: size).image { _ in
            draw(in: CGRect(origin: .zero,size: size))
        }
    }
}

enter image description here

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...