-
Star
(102)
You must be signed in to star a gist -
Fork
(17)
You must be signed in to fork a gist
-
-
Save schickling/b5d86cb070130f80bb40 to your computer and use it in GitHub Desktop.
extension UIImage { | |
func fixedOrientation() -> UIImage { | |
if imageOrientation == UIImageOrientation.Up { | |
return self | |
} | |
var transform: CGAffineTransform = CGAffineTransformIdentity | |
switch imageOrientation { | |
case UIImageOrientation.Down, UIImageOrientation.DownMirrored: | |
transform = CGAffineTransformTranslate(transform, size.width, size.height) | |
transform = CGAffineTransformRotate(transform, CGFloat(M_PI)) | |
break | |
case UIImageOrientation.Left, UIImageOrientation.LeftMirrored: | |
transform = CGAffineTransformTranslate(transform, size.width, 0) | |
transform = CGAffineTransformRotate(transform, CGFloat(M_PI_2)) | |
break | |
case UIImageOrientation.Right, UIImageOrientation.RightMirrored: | |
transform = CGAffineTransformTranslate(transform, 0, size.height) | |
transform = CGAffineTransformRotate(transform, CGFloat(-M_PI_2)) | |
break | |
case UIImageOrientation.Up, UIImageOrientation.UpMirrored: | |
break | |
} | |
switch imageOrientation { | |
case UIImageOrientation.UpMirrored, UIImageOrientation.DownMirrored: | |
CGAffineTransformTranslate(transform, size.width, 0) | |
CGAffineTransformScale(transform, -1, 1) | |
break | |
case UIImageOrientation.LeftMirrored, UIImageOrientation.RightMirrored: | |
CGAffineTransformTranslate(transform, size.height, 0) | |
CGAffineTransformScale(transform, -1, 1) | |
case UIImageOrientation.Up, UIImageOrientation.Down, UIImageOrientation.Left, UIImageOrientation.Right: | |
break | |
} | |
let ctx: CGContextRef = CGBitmapContextCreate(nil, Int(size.width), Int(size.height), CGImageGetBitsPerComponent(CGImage), 0, CGImageGetColorSpace(CGImage), CGImageAlphaInfo.PremultipliedLast.rawValue)! | |
CGContextConcatCTM(ctx, transform) | |
switch imageOrientation { | |
case UIImageOrientation.Left, UIImageOrientation.LeftMirrored, UIImageOrientation.Right, UIImageOrientation.RightMirrored: | |
CGContextDrawImage(ctx, CGRectMake(0, 0, size.height, size.width), CGImage) | |
break | |
default: | |
CGContextDrawImage(ctx, CGRectMake(0, 0, size.width, size.height), CGImage) | |
break | |
} | |
let cgImage: CGImageRef = CGBitmapContextCreateImage(ctx)! | |
return UIImage(CGImage: cgImage) | |
} | |
} |
@rubencodes
I also saw memory spikes. I first tried just keeping around a large enough memory blog to use with CGContext so it wouldn't have to keep creating one. That helped the smiles but the floor of memory usage rose (of course). Then I tripped on this CIImage filter (new in iOS11):
//image is a UIImage
let newImage = origImage.oriented(CGImagePropertyOrientation(image.imageOrientation))
guard let cgImage = context.createCGImage(newImage, from: newImage.extent) else { fatalError() }
let showImage = UIImage(cgImage: cgImage)
It creates much smaller memory spikes.
Swift 5
This version resolves memory spike as suggested by @peterdk.
extension UIImage {
static func fixedOrientation(for image: UIImage) -> UIImage? {
guard image.imageOrientation != .up else {
return image
}
let size = image.size
let imageOrientation = image.imageOrientation
var transform: CGAffineTransform = .identity
switch image.imageOrientation {
case .down, .downMirrored:
transform = transform.translatedBy(x: size.width, y: size.height)
transform = transform.rotated(by: CGFloat.pi)
case .left, .leftMirrored:
transform = transform.translatedBy(x: size.width, y: 0)
transform = transform.rotated(by: CGFloat.pi / 2.0)
case .right, .rightMirrored:
transform = transform.translatedBy(x: 0, y: size.height)
transform = transform.rotated(by: CGFloat.pi / -2.0)
case .up, .upMirrored:
break
@unknown default:
break
}
// Flip image one more time if needed to, this is to prevent flipped image
switch imageOrientation {
case .upMirrored, .downMirrored:
transform = transform.translatedBy(x: size.width, y: 0)
transform = transform.scaledBy(x: -1, y: 1)
case .leftMirrored, .rightMirrored:
transform = transform.translatedBy(x: size.height, y: 0)
transform = transform.scaledBy(x: -1, y: 1)
case .up, .down, .left, .right:
break
@unknown default:
break
}
guard var cgImage = image.cgImage else {
return nil
}
autoreleasepool {
var context: CGContext?
guard let colorSpace = cgImage.colorSpace, let _context = CGContext(data: nil, width: Int(image.size.width), height: Int(image.size.height), bitsPerComponent: cgImage.bitsPerComponent, bytesPerRow: 0, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else {
return
}
context = _context
context?.concatenate(transform)
var drawRect: CGRect = .zero
switch imageOrientation {
case .left, .leftMirrored, .right, .rightMirrored:
drawRect.size = CGSize(width: size.height, height: size.width)
default:
drawRect.size = CGSize(width: size.width, height: size.height)
}
context?.draw(cgImage, in: drawRect)
guard let newCGImage = context?.makeImage() else {
return
}
cgImage = newCGImage
}
let uiImage = UIImage(cgImage: cgImage, scale: 1, orientation: .up)
return uiImage
}
}
What about when the image is taken with frontal camera (selfie)? This does not work.
Great!
Nice!
Thanks all
Hi, here's my solution. It relies on iOS's UIImage
handing of the orientation.
So an "xyz" orientated image is rendered (using it's orientation info) onto an up
orientated canvas.
self.draw
of UIImage
, not CGImage
.
extension UIImage
{
/// `Re-orientate` the image to `up`.
///
func normalizedImage() -> UIImage?
{
if self.imageOrientation == .up
{
return self
}
else
{
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
defer
{
UIGraphicsEndImageContext()
}
self.draw(in: CGRect(origin: .zero, size: self.size))
return UIGraphicsGetImageFromCurrentImageContext()
}
}
}
Its saved my time! Thanks all !!!
Amazing. Great work buds.
Could someone explain me why we need transform.translatedBy
?
Wouldn't only .rotated(by...
and .scaledBy(...
(for flipped images) do it? 🤔
I believe those transform around the origin (0, 0) and would thus flip/rotate your image to be outside the box. if you try it with an angle of around 45° you’ll see how it would break.
I believe those transform around the origin (0, 0) and would thus flip/rotate your image to be outside the box. if you try it with an angle of around 45° you’ll see how it would break.
Thanks!
Hi, here's my solution. It relies on iOS's
UIImage
handing of the orientation. So an "xyz" orientated image is rendered (using it's orientation info) onto anup
orientated canvas.
⚠️ self.draw
ofUIImage
, notCGImage
.extension UIImage { /// `Re-orientate` the image to `up`. /// func normalizedImage() -> UIImage? { if self.imageOrientation == .up { return self } else { UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale) defer { UIGraphicsEndImageContext() } self.draw(in: CGRect(origin: .zero, size: self.size)) return UIGraphicsGetImageFromCurrentImageContext() } } }
UIGraphicsBeginImageContextWithOptions
was deprecated, use UIGraphicsImageRenderer
instead.
extension UIImage {
func normalizedImage() -> UIImage {
guard imageOrientation != .up else {
return self
}
return UIGraphicsImageRenderer(size: size, format: .preferred())
.image {
draw(in: $0.format.bounds)
}
}
}
@ChrisXu thank you :)
What should i pass for this label?)

@Sam-Spencer thank you, it helped. I used your version eve though there was newer which was reported to have no memory leak, because it's the most recent which works as is. The last one requires me to pass something for the for
labels.
In my case, your solution was not enough. I was having cropped image saved in some broken cropping.
Here how I used it:
func cropImage(_ image: UIImage, x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat) -> UIImage? {
let rectToCrop = CGRect(x: x, y: y, width: width, height: height)
let rect = CGRect(x: rectToCrop.origin.x, y: rectToCrop.origin.y, width: rectToCrop.width, height: rectToCrop.height)
guard let cropped = image.fixedOrientation()?.cgImage?.cropping(to: rect) else {
return nil
}
return UIImage(cgImage: cropped, scale: image.scale, orientation: .up)
}
And saving like
UIImageWriteToSavedPhotosAlbum(imageCropped, nil, nil, nil)
My problem was that i forgot to change image.imageOrientation
to .up
in the last line of the cropping function
UIImage(cgImage: cropped, scale: image.scale, orientation: .up)
Gosh, this works amazingly, but seems to cause a memory crash for me half the time. Just calling it once. Even after adding
return autoreleasepool {}
around it as suggested by @peterdk.