如何无损获取UIImage的水平/垂直镜像?
无损获取镜像图片的解决方案
Hey,我明白你遇到的困扰——用修改UIImageOrientation或者CIImage变换的方法做镜像后,画质莫名下降了对吧?其实问题出在这些方法的底层处理上:要么是orientation的间接转换在某些场景下触发了系统的隐式重绘采样,要么是默认创建的CIContext没有对齐原图的像素格式和缩放比例,导致了不必要的图像损失。
下面给你两种可靠的无损镜像实现方案,都是能完美保留原图分辨率、scale和颜色信息的:
方案一:Core Graphics直接绘制(最推荐)
这个方法直接操作像素缓冲区,完全绕开可能导致损失的中间转换,是最稳妥的方式。可以给UIImage写个分类,方便调用:
#import <UIKit/UIKit.h> @interface UIImage (Mirror) // 水平镜像(左右翻转) - (UIImage *)horizontalMirrorImage; // 垂直镜像(上下翻转) - (UIImage *)verticalMirrorImage; @end @implementation UIImage (Mirror) - (UIImage *)horizontalMirrorImage { // 用原图的size和scale创建绘图上下文,保证像素密度一致 UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale); CGContextRef context = UIGraphicsGetCurrentContext(); // 先把坐标系平移到图片右侧,再水平翻转X轴 CGContextTranslateCTM(context, self.size.width, 0); CGContextScaleCTM(context, -1.0, 1.0); // 直接绘制原图,完全复用原有像素数据 [self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)]; UIImage *mirroredImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return mirroredImage; } - (UIImage *)verticalMirrorImage { UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale); CGContextRef context = UIGraphicsGetCurrentContext(); // 平移坐标系到图片底部,再垂直翻转Y轴 CGContextTranslateCTM(context, 0, self.size.height); CGContextScaleCTM(context, 1.0, -1.0); [self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)]; UIImage *mirroredImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return mirroredImage; } @end
为什么这个方案无损?
- 用
UIGraphicsBeginImageContextWithOptions时传入了原图的scale,保证输出图片的像素密度和原图完全一致; - 直接通过坐标系变换绘制,没有经过CIImage转换或orientation触发的隐式重绘;
- 绘制过程完全复用原图的像素数据,不会改变颜色空间或进行额外的采样处理。
方案二:优化后的CIImage方案(如果你偏好Core Image)
如果你坚持想用CIImage的方式,那一定要给CIContext配置和原图匹配的参数,避免默认设置导致的颜色空间转换或采样损失:
- (UIImage*)getHoriMirror:(UIImage*)img { CIImage* cii = [CIImage imageWithCGImage:img.CGImage]; // 水平镜像用CGAffineTransformMakeScale(-1, 1),垂直镜像用(1, -1) cii = [cii imageByApplyingTransform:CGAffineTransformMakeScale(-1, 1)]; // 获取原图的颜色空间,保证CIContext和原图对齐 CGColorSpaceRef colorSpace = CGImageGetColorSpace(img.CGImage); CIContext *context = [CIContext contextWithCGColorSpace:colorSpace options:@{ kCIContextOutputColorSpace: colorSpace, kCIContextWorkingColorSpace: colorSpace, kCIContextUseSoftwareRenderer: @NO // 优先用硬件加速 }]; CGImageRef cgImage = [context createCGImage:cii fromRect:[cii extent]]; // 一定要保留原图的scale和orientation UIImage* uiImage = [UIImage imageWithCGImage:cgImage scale:img.scale orientation:img.imageOrientation]; CGImageRelease(cgImage); return uiImage; }
这样修改后,CIImage的处理就不会再因为颜色空间不匹配或者scale丢失导致画质下降了。
内容的提问来源于stack exchange,提问作者Sudhanshu Monga
相关产品推荐
相关产品推荐

