0

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

给定原始图像

在此处输入图像描述

当我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)
    }
}

但是,如果我使用CGContextfromUIGraphicImageRenderer

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, in: CGRect(origin: .zero, size: size))
        }
    }
}

图像被颠倒如下。为什么它被翻转了,我怎样才能避免翻转?

在此处输入图像描述

4

2 回答 2

2

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

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

图像翻转的原因是UIKit 和 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))
        }
    }
}

编辑/更新:

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

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, in: CGRect(origin: .zero, size: size))
        }
    }
}
于 2020-10-26T13:43:53.560 回答
1

经过探索,只是为了澄清我注意到它们之间的差异,如下所示。

5x3像素的原始图像

在此处输入图像描述

直接使用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)
    }
}

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

在此处输入图像描述

第二种方法是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, in: CGRect(origin: .zero, size: size))
        }
    }
}

使用这种方法,它

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

在此处输入图像描述

为了解决颠倒的问题,我们需要根据@LeoDabus 的回答翻转它,使用

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))
        }
    }
}

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

在此处输入图像描述

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

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

在此处输入图像描述

于 2020-10-27T05:22:44.977 回答