2

How to load an image and rotate it according to it's orientation exif data and save it with UIImageOrientationUp exif data (or without any orientation exif data) so that software that don't handle exif orientation data will show correctly the image ?

AamirR
  • 11,672
  • 4
  • 59
  • 73
zeus
  • 12,173
  • 9
  • 63
  • 184

2 Answers2

3

Loading an image is as simple as this line:

UIImage *image = [UIImage imageNamed:@"NameOfImageHere"];

Or if you got image data:

NSData *imageData = [[NSData alloc] init];
UIImage *image = [UIImage imageWithData: imageData];

And following method will help you to fix the orientation using exif data within UIImage

- (UIImage *)imageByFixingOrientation {

    UIImage *image = self;

    if (image.imageOrientation == UIImageOrientationUp) return image;

    CGAffineTransform transform = CGAffineTransformIdentity;

    switch (image.imageOrientation) {
        case UIImageOrientationDown:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate(transform, image.size.width, image.size.height);
            transform = CGAffineTransformRotate(transform, M_PI);
            break;

        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
            transform = CGAffineTransformTranslate(transform, image.size.width, 0);
            transform = CGAffineTransformRotate(transform, M_PI_2);
            break;

        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, 0, image.size.height);
            transform = CGAffineTransformRotate(transform, -M_PI_2);
            break;
        case UIImageOrientationUp:
        case UIImageOrientationUpMirrored:
            break;
    }

    switch (image.imageOrientation) {
        case UIImageOrientationUpMirrored:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate(transform, image.size.width, 0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;

        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, image.size.height, 0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;
        case UIImageOrientationUp:
        case UIImageOrientationDown:
        case UIImageOrientationLeft:
        case UIImageOrientationRight:
            break;
    }

    // Now we draw the underlying CGImage into a new context, applying the transform
    // calculated above.
    CGContextRef ctx = CGBitmapContextCreate(NULL, image.size.width, image.size.height,
                                             CGImageGetBitsPerComponent(image.CGImage), 0,
                                             CGImageGetColorSpace(image.CGImage),
                                             CGImageGetBitmapInfo(image.CGImage));
    CGContextConcatCTM(ctx, transform);
    switch (image.imageOrientation) {
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            CGContextDrawImage(ctx, CGRectMake(0,0,image.size.height,image.size.width), image.CGImage);
            break;

        default:
            CGContextDrawImage(ctx, CGRectMake(0,0,image.size.width,image.size.height), image.CGImage);
            break;
    }

    // And now we just create a new UIImage from the drawing context
    CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
    UIImage *img = [UIImage imageWithCGImage:cgimg];
    CGContextRelease(ctx);
    CGImageRelease(cgimg);
    return img;
}
AamirR
  • 11,672
  • 4
  • 59
  • 73
3

Swift 4 equivalent of my previous objective-c method as an extension:

extension UIImage {

    func byFixingOrientation(andResizingImageToNewSize newSize: CGSize? = nil) -> UIImage {

        guard let cgImage = self.cgImage else { return self }

        let orientation = self.imageOrientation
        guard orientation != .up else { return UIImage(cgImage: cgImage, scale: 1, orientation: .up) }

        var transform = CGAffineTransform.identity
        let size = newSize ?? self.size

        if (orientation == .down || orientation == .downMirrored) {
            transform = transform.translatedBy(x: size.width, y: size.height)
            transform = transform.rotated(by: .pi)
        }
        else if (orientation == .left || orientation == .leftMirrored) {
            transform = transform.translatedBy(x: size.width, y: 0)
            transform = transform.rotated(by: CGFloat.pi / 2)
        }
        else if (orientation == .right || orientation == .rightMirrored) {
            transform = transform.translatedBy(x: 0, y: size.height)
            transform = transform.rotated(by: -(CGFloat.pi / 2))
        }

        if (orientation == .upMirrored || orientation == .downMirrored) {
            transform = transform.translatedBy(x: size.width, y: 0);
            transform = transform.scaledBy(x: -1, y: 1)
        }
        else if (orientation == .leftMirrored || orientation == .rightMirrored) {
            transform = transform.translatedBy(x: size.height, y: 0)
            transform = transform.scaledBy(x: -1, y: 1)
        }

        // Now we draw the underlying CGImage into a new context, applying the transform calculated above.
        guard let ctx = CGContext(data: nil, width: Int(size.width), height: Int(size.height),
                                  bitsPerComponent: cgImage.bitsPerComponent, bytesPerRow: 0,
                                  space: cgImage.colorSpace!, bitmapInfo: cgImage.bitmapInfo.rawValue)
        else {
            return UIImage(cgImage: cgImage, scale: 1, orientation: orientation)
        }

        ctx.concatenate(transform)

        // Create a new UIImage from the drawing context
        switch (orientation) {
        case .left, .leftMirrored, .right, .rightMirrored:
            ctx.draw(cgImage, in: CGRect(x: 0, y: 0, width: size.height, height: size.width))
        default:
            ctx.draw(cgImage, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
        }

        return UIImage(cgImage: ctx.makeImage() ?? cgImage, scale: 1, orientation: .up)
    }
}

Usage-1

let newImage = image.byFixingOrientation()

Usage-2 (Fix orientation and resize image to new size)

let newImage = image.byFixingOrientation(andResizingImageToNewSize: CGSize(width: 200, height: 200))
AamirR
  • 11,672
  • 4
  • 59
  • 73
  • thanks @AamirR ! if i do newImage = image.byFixingOrientation() and then i save the newImage to a file (jpeg), what will be the orientation Exif data of the image? – zeus Jul 14 '18 at 10:27
  • `.up` will be the new orientation, so that it will not be automatically processed by exif orientation aware program – AamirR Jul 14 '18 at 10:28
  • I just try your solution, it's didn't work :( when i try to load the resulting image with a non exif orientation aware program then the image is displayed in the wrong orientation :( – zeus Jul 14 '18 at 10:28
  • I try to compile, if i not make any mistake CGContext(data: nil, width: ...) is equivalent to CGBitmapContextCreate but i don't see the equivalent of ctx.concatenate(transform) :( – zeus Jul 14 '18 at 10:33
  • CGContextConcatCTM(ctx, transform); – AamirR Jul 14 '18 at 10:34