随笔-图片转码

Posted by Maqy on May 14, 2018

最近用到上传图片,遇到了一些问题,所以特地记了一下: 主要是在iOS11上,有了一个新的图片格式public.heicpublic.heif.那么这时候后端还没有支持这个格式的图片,所以上传后会报错。 当时的解决方案就是本地转成jpeg格式来上传。那么这时候就用到了图片格式的转码,现在来介绍一下。

假如现在的heic格式的图片原图大小在3.3M左右,如果通过PHAsset相册读取的话,有时候还会有图片旋转的问题,解决图片旋转需要转换成UIImage,再将图片旋转成正常的方向:

CIImage

CIImage *ciImage = [CIImage imageWithData:imageData];
CIContext *context = [CIContext context];
NSData *jpgData = [context JPEGRepresentationOfImage:ciImage colorSpace:ciImage.colorSpace options:@{}];

这个方法转换后大小在4.9M的样子,看起来还好,切无损转换,但是解决图片旋转的问题不太好搞。

UIImageJPEGRepresentation

// return image as JPEG. May return nil if image has no CGImageRef or invalid bitmap format. compression is 0(most)..1(least)
/// 将UIImage转成jpeg格式,参数2为1的时候质量最高,但转成功后很大,大概在12.5M的样子,不过容易解决图片旋转的问题
NSData * __nullable UIImageJPEGRepresentation(UIImage * __nonnull image, CGFloat compressionQuality);

imageIO

/// 要输出到的imageData
NSMutableData *outPutImageData = [NSMutableData data];
/// dataUTI: 你要输出成的格式,例如 public.jpeg public.png 等
CGImageDestinationRef imageIODestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)outPutImageData,
                                                                                        (__bridge CFStringRef)dataUTI, 1, nil);

// imageData: 输入的image, 通过输入的image获取imageSource,也就是图片的数据
CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, nil);
// 获取图片的元数据,参数0代表数据里的第0个, 比如 gif 图是一组图片元数据
NSDictionary *dic = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil);
NSMutableDictionary *mutableDic = dic.mutableCopy;
// 将图片旋转方向置成向上,解决图片旋转问题
mutableDic[(__bridge_transfer NSString *)kCGImagePropertyOrientation] = @(kCGImagePropertyOrientationUp);
CGImageRef cgImage= image.CGImage;

CGImageDestinationAddImage(imageIODestination, cgImage, (__bridge CFDictionaryRef)mutableDic);
// done
CGImageDestinationFinalize(imageIODestination);

这个解决方案转码的大小大概在4.9M,和CIImage一致,而且方便解决从相册读取图片的旋转问题。这也是最后选择的解决方案。

imageIO 还可以对图片数据进行直接操作,例如地理位置

Tip

imageIO需要用到CF和OC混编,所以要注意下内存管理。 一般来讲OC创建的对象直接用 __bridge 就好,管理内存由创建该对象的一方管理,及ARC。 如果是CF创建的对象要用 __bridge_transfer 比较好,将内存管理交由OC的ARC管理。