用 UIGraphicsImageRenderer 的 jpegData 替换 UIImageJPEGRepresentation

Replacing UIImageJPEGRepresentation with UIGraphicsImageRenderer's jpegData

我正在努力在iOS10中添加对宽幅彩色照片的支持。当用户从相机拍摄照片时,我需要使用支持新颜色的新APIspace 保存照片数据 - UIGraphicsImageRendererjpegData 而不是 UIImageJPEGRepresentation

我 运行 在图像方向方面遇到了一些麻烦。在我的 iPad 上拍摄肖像照片时,图像绘制不正确。请参阅以下评论:

旧 API:

let image = info[UIImagePickerControllerOriginalImage] as! UIImage
let imageData = UIImageJPEGRepresentation(image, 1)

新 API:

let image = info[UIImagePickerControllerOriginalImage] as! UIImage
let cgImage = image.cgImage!
let ciImage = CIImage(cgImage: cgImage)

let format = UIGraphicsImageRendererFormat()
format.scale = 1
format.prefersExtendedRange = true

let renderer = UIGraphicsImageRenderer(bounds: ciImage.extent, format: format)
let imageData = renderer.jpegData(withCompressionQuality: 1, actions: { context in
    context.cgContext.draw(cgImage, in: ciImage.extent) //draws flipped horizontally
    //image.draw(at: .zero) //draws rotated 90 degrees leaving black at bottom
    //image.draw(in: ciImage.extent) //draws rotated 90 degrees stretching and compressing the image to fill the rect 
 })

UIGraphicsImageRendererjpegData 替换 UIImageJPEGRepresentation 的正确方法是什么?

UIImage 可以有不同的方向,具体取决于相机旋转。 您可以根据该方向动态解析需要应用于图像的转换,如下所示:

let renderer = UIGraphicsImageRenderer(size: image.size, format: format)
let imageData = renderer.jpegData(withCompressionQuality: 1, actions: { context in
    var workSize = image.size;
    workSize.width = floor(workSize.width / image.scale)
    workSize.height = floor(workSize.height / image.scale)
    // No-op if the orientation is already correct
    // if image.imageOrientation == .up { draw image }

    // We need to calculate the proper transformation to make the image upright.
    // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.

    var transform = CGAffineTransform.identity

    switch image.imageOrientation
    {
        case .down, .downMirrored:
            transform = transform.translatedBy(x: workSize.width, y: workSize.height)
            transform = transform.rotated(by: CGFloat(Double.pi))
            break

        case .left, .leftMirrored:
            transform = transform.translatedBy(x: workSize.width, y: 0.0)
            transform = transform.rotated(by: CGFloat(Double.pi / 2.0))
            break

        case .right, .rightMirrored:
            transform = transform.translatedBy(x: 0.0, y: workSize.height)
            transform = transform.rotated(by: CGFloat(-Double.pi / 2.0))
            break

        case .up, .upMirrored:
            break
    }

    switch image.imageOrientation
    {
        case .upMirrored, .downMirrored:
            transform = transform.translatedBy(x: workSize.width, y: 0.0)
            transform = transform.scaledBy(x: -1.0, y: 1.0)
            break

        case .leftMirrored, .rightMirrored:
            transform = transform.translatedBy(x: workSize.height, y: 0.0);
            transform = transform.scaledBy(x: -1.0, y: 1.0);
            break

        case .up, .down, .left, .right:
            break
    }

    // Now we draw the underlying CGImage into a new context, applying the transform
    // calculated above.

    let ctx = context.cgContext

    ctx.concatenate(transform)

    switch image.imageOrientation {
        case .left, .leftMirrored, .right, .rightMirrored:
            ctx.draw(image.cgImage!, in: CGRect(x: 0.0, y:0.0, width: workSize.height, height: workSize.width))
            break;

        default:
            ctx.draw(image.cgImage!, in: CGRect(origin: .zero, size: workSize))
            break;
    } 
 })

答案基于UIImage+fixOrientation

image.draw() 方法应该 correct the orientation automatically 适合你:

This method draws the entire image in the current graphics context, respecting the image’s orientation setting. In the default coordinate system, images are situated down and to the right of the origin of the specified rectangle. This method respects any transforms applied to the current graphics context, however.

我不确定您为什么需要使用 ci.extentextent 似乎是(我并不声称自己是 Core Image API 方面的专家)"raw" 图像数据的尺寸,它在没有任何图像方向校正的情况下存储。如果您的原始数据需要旋转(非向上方向,如 LeftRight),范围仍将是存储数据的原始矩形。

我对带有 imageOrientation.rawValue = 3 的图像使用以下代码,重新加载时,我的数据在 "corrected" imageOrientation.rawValue = 0 的正确尺寸下完全没问题。

let imageSize = image.Size
let renderer = UIGraphicsImageRenderer(size: renderedSize, format: UIGraphicsImageRendererFormat())
return renderer.jpegData(withCompressionQuality: 0.95) { (rendererContext) in
    let rect = CGRect(x: 0, y: 0, width: imageSize.width, height: imageSize.height)
    image.draw(in: rect)
}

我的输入图像 "renders" 它的数据与我的代码正确,考虑到图像方向。在右侧,我使用 draw(in: extent)。虽然图像没有旋转。只是拉伸。