问题描述
给出一个图像,当我想转换为标准的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)
}
}
但是,如果我使用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))
}
}
}
图像如下翻转。为什么会发生翻转,如何避免翻转?
解决方法
无需获取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像素的原始图像
直接使用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并直接对其进行操作。
第二种方法是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)均增加了一倍,以使颜色边缘更平滑。
要解决上下颠倒的情况,我们需要使用@ 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)。
翻转的结果与@LeoDabus提供的简化答案相同
extension UIImage {
func toSRGB() -> UIImage {
UIGraphicsImageRenderer(size: size).image { _ in
draw(in: CGRect(origin: .zero,size: size))
}
}
}