Skip to content

Instantly share code, notes, and snippets.

@schickling
Last active December 31, 2024 22:21
Show Gist options
  • Save schickling/b5d86cb070130f80bb40 to your computer and use it in GitHub Desktop.
Save schickling/b5d86cb070130f80bb40 to your computer and use it in GitHub Desktop.
Extension to fix orientation of an UIImage (Sets orientation to portrait)
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)
}
}
@SlavcoPetkovski
Copy link

What about when the image is taken with frontal camera (selfie)? This does not work.

@githubdelegate
Copy link

Great!

@AD-Paladins
Copy link

Nice!
Thanks all

@raygun101
Copy link

raygun101 commented Aug 12, 2021

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

@JanzTam
Copy link

JanzTam commented Aug 12, 2021

Its saved my time! Thanks all !!!

@logesh45
Copy link

Amazing. Great work buds.

@backslash-f
Copy link

Could someone explain me why we need transform.translatedBy?

Wouldn't only .rotated(by... and .scaledBy(... (for flipped images) do it? 🤔

@juliand665
Copy link

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.

@backslash-f
Copy link

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!

@0xfeedface1993
Copy link

0xfeedface1993 commented Sep 9, 2024

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

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

@kopyl
Copy link

kopyl commented Dec 31, 2024

@ChrisXu thank you :)
What should i pass for this label?)

image

@kopyl
Copy link

kopyl commented Dec 31, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment