Replacing a UIImageJPEGR view with UIGraphicsImageRenderer jpegData

I am working on adding support for widescreen photos in iOS 10. When a user takes a photo from the camera, I need to use a new API that supports a new color space for saving UIGraphicsImageRenderer jpegData - UIGraphicsImageRenderer jpegData instead of UIImageJPEGRepresentation .

I have problems with image orientation. When taking a photo on my iPad in a portrait, the image does not draw correctly. See comments below:

Old API:

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

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

What is the correct way to replace UIImageJPEGRepresentation with UIGraphicsImageRenderer jpegData ?

+6
source share
2 answers

UIImage may have a different orientation depending on the rotation of the camera. You can dynamically enable the transformation that you want to apply to the image depending on this orientation, for example:

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

Answer based on UIImage + fixOrientation

+2
source

The image.draw() method should automatically adjust the orientation for you:

This method draws the entire image in the current graphics context, given the image orientation setting. In the default coordinate system, images are located down and to the right of the beginning of the specified rectangle. However, this method applies to any transformations applied to the current graphics context.

I'm not sure why you need to use ci.extent . It seems that extent (I do not pretend to be an expert in the Core Image API) is the size of the raw image data that is saved without correcting the image orientation. If your raw data needs rotation (non-up orientations such as Left , Right ), the volume will still be the original rectangle that is stored in the data.

I use the following code for an image with imageOrientation.rawValue = 3 , and my data imageOrientation.rawValue = 3 perfectly in the right size with the "fixed" imageOrientation.rawValue = 0 on reboot.

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

My input image "correctly displays" its data with my code, given the orientation of the image. On the right, I use draw(in: extent) . However, the image does not rotate. Just stretched.

enter image description here

0
source

Source: https://habr.com/ru/post/1014117/


All Articles