iOS上传图片后台旋转展示问题
iOS上传图片后台旋转展示问题
在一次上传图片到服务器后,去后台页面查看,发现iOS显示的图片总是旋转90℃的,而Android上传的图片展示正常的。
经过一番google之后发现是iOS相机加入了方向传感器,它能够记录下拍摄时相机的方向,并将这一信息保存在照片中。照片的存储方式还是没有任何改变,它仍然是以相机的坐标系来保存,只是当相机来浏览这些照片时,相机可以根据照片中的方向信息,结合此时相机的方向,对照片进行旋转,从而转到适合人们观看的角度。
但是很遗憾,这一标准并没有被广泛的传播开来,或者说始终如一的贯彻,这也导致了本文所讨论的问题。
EXIF(Exchangeable Image File Format)
可交换图像文件格式常被简称为Exif(Exchangeable image file format),是专门为数码相机的照片设定的,可以记录数码照片的属性信息和拍摄数据… Exif可以附加于JPEG、TIFF、RIFF等文件之中
Orientation
在EXIF涵盖的各种信息之中,其中有一个叫做Orientation (rotation)的标签,用于记录图像的方向,这便是相机写入方向信息的最终位置。它总共定义了八个值:
orientation-eight-values.png注意:对于上面的八种方向中,加了*
的并不常见,因为它们代表的是镜像方向,如果不做任何的处理,不管相机以任何角度拍摄,都无法出现镜像的情况
这个表格代表什么意义?我们来看第一行,值为1时,右边两列的值分别为:Row #0 is Top,Column #0 is Left side
,其实很好理解,它表示照片的第一行位于顶端,而第一列位于左侧,那么这张照片自然就是以正常角度拍摄的。
对着前面的四种拍摄角度,由于相机都是以其自身的坐标系来保存照片,因此每张照片对应的第一行和第一列的位置始终如下:
orientation-row0-column0.png以同样的方法,我们可以推断出上面四种方式拍摄时,对应EXIF中Orientation的值如下所示:
orientation-value.png实例
这里推荐两个工具网站
原始数据
获取未处理前base64的图片数据,经过网页展示如下:
original.pngExif读取未处理图片数据:
originalData.png发现Orientation的值是6,和上面位置分析一致,图片自然是左旋转。但需要注意的是,这些图片在iOS上是正常显示的
处理图片
先介绍一种简单方法
该方法简单粗暴了点,但用起来非常简单。
- (UIImage *)normalizedImage:(UIImage *)image {
if (image.imageOrientation == UIImageOrientationUp) return image;
UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
[image drawInRect:(CGRect){0, 0, image.size}];
UIImage *normalizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return normalizedImage;
}
这里是利用了UIImage
中的drawInRect
方法,它会将图像绘制到画布上,并且已经考虑好了图像的方向,理解该方法需要配合一下官方文档:
-drawInRect:
Draws the entire image in the specified rectangle, scaling it as needed to fit.
Discussion
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.
处理之后
处理后base64的图片数据,经过网页展示如下:
correct.pngExif读取处理后图片数据:
correctData.png再附加一种简单明了的方法
(代码较长,有兴趣的同学可以看看相互学习一下)
直接上代码了
为UIImage创建一个category,其中包含fixOrientation方法:
UIImage+fixOrientation.h
@interface UIImage (fixOrientation)
- (UIImage *)fixOrientation;
@end
UIImage+fixOrientation.m
@implementation UIImage (fixOrientation)
- (UIImage *)fixOrientation {
// No-op if the orientation is already correct
if (self.imageOrientation == UIImageOrientationUp) return self;
// 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.
CGAffineTransform transform = CGAffineTransformIdentity;
switch (self.imageOrientation) {
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
transform = CGAffineTransformTranslate(transform, self.size.width, 0);
transform = CGAffineTransformRotate(transform, M_PI_2);
break;
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, 0, self.size.height);
transform = CGAffineTransformRotate(transform, -M_PI_2);
break;
case UIImageOrientationUp:
case UIImageOrientationUpMirrored:
break;
}
switch (self.imageOrientation) {
case UIImageOrientationUpMirrored:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, self.size.width, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
case UIImageOrientationLeftMirrored:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, self.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, self.size.width, self.size.height,
CGImageGetBitsPerComponent(self.CGImage), 0,
CGImageGetColorSpace(self.CGImage),
CGImageGetBitmapInfo(self.CGImage));
CGContextConcatCTM(ctx, transform);
switch (self.imageOrientation) {
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
// Grr...
CGContextDrawImage(ctx, CGRectMake(0,0,self.size.height,self.size.width), self.CGImage);
break;
default:
CGContextDrawImage(ctx, CGRectMake(0,0,self.size.width,self.size.height), self.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;
}
@end