内容简介:当前网上找到的图片压缩方法大部分都是基于 UIImage 的,但是如果要支持 gif 的话,那么从相册读出来就得是 NSData,就必须要基于 NSData 来做压缩了。主要用到了1、通过
当前网上找到的图片压缩方法大部分都是基于 UIImage 的,但是如果要支持 gif 的话,那么从相册读出来就得是 NSData,就必须要基于 NSData 来做压缩了。
主要用到了 <ImageIO>
和 <CoreGraphics>
库。
步骤就是
1. `CGImageSource` 从 data读出 `CGImageSourceRef` 对象; 2. `CGImageSourceRef` 中读取 `CGImageRef`; 3. 经过 旋转、调整大小、压缩质量; 4. 然后用 `CGImageDestination` 重新写入新的`data`。 复制代码
一、 静态图片,并转为 jpg
1、通过 CGImageSourceRef
读取数据
//通过CFData读取文件的数据 CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL); //获取文件的帧数 size_t count = CGImageSourceGetCount(source); if (count == 0) { CFRelease(source); return nil; } 复制代码
2、 读取 每一帧的属性,一般 source 的 count 都是1, Gif
和 Live Photo
的都是大于1的。
// 图像属性 CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, 0, nil); //图像的旋转方向 CGImagePropertyOrientation orientation = (uint32_t)[(__bridge NSNumber *)CFDictionaryGetValue(cfFrameProperties, kCGImagePropertyOrientation) integerValue]; if (orientation == 0) { // 方向丢失 orientation = kCGImagePropertyOrientationUp; } 复制代码
3、读取到 CGImageRef
imageRef = CGImageSourceCreateImageAtIndex(source, 0, NULL); 复制代码
4、处理 大小,旋转
#pragma mark 调整大小,自动旋转,减少分开的计算而已 CGImageRef DDCreateResizeAndUpOrientationImageRef(CGImageRef imageRef, CGSize targetSize, CGImagePropertyOrientation currentOrientation) { size_t width = targetSize.width; size_t height = targetSize.height; // orientation 如果是左右的话,那么 size 要反一下,否则 宽高不对。 switch (currentOrientation) { case kCGImagePropertyOrientationLeft: case kCGImagePropertyOrientationLeftMirrored: case kCGImagePropertyOrientationRight: case kCGImagePropertyOrientationRightMirrored: { size_t temp = width; width = height; height = temp; } break; default: break; } CGColorSpaceRef space = CGImageGetColorSpace(imageRef); CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); if((bitmapInfo == kCGImageAlphaLast) || (bitmapInfo == kCGImageAlphaNone)) bitmapInfo = (CGBitmapInfo)kCGImageAlphaNoneSkipLast; size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef); // size_t bytesPerRow = width * 4; size_t bytesPerRow = 0; // create context CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, space, bitmapInfo); CGColorSpaceRelease(space); // release space if (!context) { return nil; } CGContextSetInterpolationQuality(context, kCGInterpolationMedium); if (currentOrientation != kCGImagePropertyOrientationUp) { // 需要旋转 图片 CGAffineTransform transform = TransformForOrientation(currentOrientation, CGSizeMake(width, height)); CGContextConcatCTM(context, transform); CGRect transposedRect = CGRectMake(0, 0, height, width); CGContextDrawImage(context, transposedRect, imageRef); } else { CGRect newRect = CGRectIntegral(CGRectMake(0, 0, width, height)); CGContextDrawImage(context, newRect, imageRef); } // get new imageRef CGImageRef decoded = CGBitmapContextCreateImage(context); CFRelease(context); if (!decoded) { return nil; } return decoded; } 复制代码
5、调整质量
通过 CGImageDestinationRef
写入 data,设置 kCGImageDestinationLossyCompressionQuality
参数来调整质量
参数主要是前面读取的 CGImageRef
的属性,这里因为不想重新读,所以就当参数传递了。
在 这里之前,需要 重新设置 图片的 orientation,否则结果 orientation 会丢失,造成旋转失效
// 修改参数 orientation,如果上面修改了 orientation,则这里也必须修改,否则会丢失新旋转。 CFMutableDictionaryRef mutDicRef = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, cfFrameProperties); CGFloat width = newSize.width; CGFloat height = newSize.height; CFNumberRef widthNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &width); CFNumberRef heightNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &height); CFDictionarySetValue(mutDicRef, kCGImagePropertyPixelWidth, widthNum); CFDictionarySetValue(mutDicRef, kCGImagePropertyPixelHeight, heightNum); CGImagePropertyOrientation newOrientation = kCGImagePropertyOrientationUp; CFNumberRef newOrientationNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &newOrientation); CFDictionarySetValue(mutDicRef, kCGImagePropertyOrientation, newOrientationNum); 复制代码
+ (NSData *)getCompressImageDataWIthImageRef:(CGImageRef)imageRef uttype:(CFStringRef)uttype properties:(CFDictionaryRef)properties isToJPG:(BOOL)isToJPG toMaxBytes:(NSUInteger)maxBytes { // 转为 jpg,并压缩质量 CFStringRef type = nil; if (isToJPG) { type = kUTTypeJPEG; } else if (uttype) { type = CFStringCreateCopy(kCFAllocatorDefault, uttype); } else { if (@available(iOS 9.0, *)) { type = CGImageGetUTType(imageRef); } } BOOL success = NO; NSMutableData *mutableData = nil; do { mutableData = [NSMutableData data]; // destination 的 type 是确定最终 image 的类型。 CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)mutableData, type, 1, NULL); CFMutableDictionaryRef mutDicRef = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, properties); float c = 0.5; CFNumberRef compressionQuality = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &c); CFDictionarySetValue(mutDicRef, kCGImageDestinationLossyCompressionQuality, compressionQuality); CGImageDestinationAddImage(destination, imageRef, mutDicRef); success = CGImageDestinationFinalize(destination); CFRelease(destination); CFRelease(mutDicRef); CFRelease(compressionQuality); if (!success) { NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: NSLocalizedString(@"Could not finalize image destination", nil) }; NSLog(@"resizePNGImageDataToSize %@",userInfo); break; } } while (mutableData.length > maxBytes); CFRelease(type); if (!success) { return nil; } return mutableData; } 复制代码
6、完整代码
#pragma mark - 压缩 静态图,并转为 JPG + (NSData *)thumbnailImageData:(NSData *)imageData fitSize:(CGSize)targetSize maxBytes:(double)maxBytes minBytes:(double)minBytes toJPG:(BOOL)isToJPG { if (targetSize.width == 0 || targetSize.height == 0) { return imageData; } // < 30k 也算了,不转了 if (imageData.length < minBytes) { return imageData; } CGSize size = [UIImage sizeFromImageData:imageData]; // size 不超,bytes 也不超,也不转了 if (size.width <= targetSize.width && size.height <= targetSize.height && imageData.length <= maxBytes) { return imageData; } //通过CFData读取文件的数据 CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL); //获取文件的帧数 size_t count = CGImageSourceGetCount(source); if (count == 0) { CFRelease(source); return nil; } // 计算 size CGSize newSize = [self fitJPGImageSize:size toSize:targetSize]; // 图像属性 CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, 0, nil); //图像的旋转方向 CGImagePropertyOrientation orientation = (uint32_t)[(__bridge NSNumber *)CFDictionaryGetValue(cfFrameProperties, kCGImagePropertyOrientation) integerValue]; if (orientation == 0) { // 方向丢失 orientation = kCGImagePropertyOrientationUp; } // 创建第一个 imageRef CGImageRef imageRef = NULL; imageRef = CGImageSourceCreateImageAtIndex(source, 0, NULL); if (!imageRef) { CFRelease(source); return nil; } // 1. 旋转 // 2. 调整大小 if (!CGSizeEqualToSize(newSize, size)) { // 需要压缩大小 或者 需要旋转,进行重绘 CGImageRef resizeImageRef = DDCreateResizeAndUpOrientationImageRef(imageRef, newSize, orientation); if (resizeImageRef) { CGImageRelease(imageRef); imageRef = NULL; imageRef = CGImageCreateCopy(resizeImageRef); CGImageRelease(resizeImageRef); } } else if (orientation != kCGImagePropertyOrientationUp){ // 旋转 CGImageRef rotateRef = DDCreateRotateImageRef(imageRef, orientation); if (rotateRef) { CGImageRelease(imageRef); imageRef = CGImageCreateCopy(rotateRef); CGImageRelease(rotateRef); } } // 3. 转为 jpg,并压缩质量 CFStringRef type = CGImageSourceGetType(source); if (isToJPG) { type = kUTTypeJPEG; } // 修改参数 orientation,如果上面修改了 orientation,则这里也必须修改,否则会丢失新旋转。 CFMutableDictionaryRef mutDicRef = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, cfFrameProperties); CGFloat width = newSize.width; CGFloat height = newSize.height; CFNumberRef widthNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &width); CFNumberRef heightNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &height); CFDictionarySetValue(mutDicRef, kCGImagePropertyPixelWidth, widthNum); CFDictionarySetValue(mutDicRef, kCGImagePropertyPixelHeight, heightNum); CGImagePropertyOrientation newOrientation = kCGImagePropertyOrientationUp; CFNumberRef newOrientationNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &newOrientation); CFDictionarySetValue(mutDicRef, kCGImagePropertyOrientation, newOrientationNum); NSData *mutableData = [self getCompressImageDataWIthImageRef:imageRef uttype:type properties:mutDicRef isToJPG:isToJPG toMaxBytes:maxBytes]; CFRelease(cfFrameProperties); CGImageRelease(imageRef); CFRelease(type); CFRelease(source); return mutableData; } 复制代码
二、 gif
gif 的处理类似,主要就是循环遍历 CGImageSourceRef
里的 CGImageRef
,但是gif 每一帧的图片可能不一样,所以压缩质量不太好处理,只处理了像素大小。
+ (NSData *)thumbnailGIFImageData:(NSData *)gifData fitSize:(CGSize)targetSize maxBytes:(double)maxBytes { if (targetSize.width == 0 || targetSize.height == 0) { return gifData; } // resize new size CGSize size = [UIImage sizeFromImageData:gifData]; if (size.width <= targetSize.width && size.height <= targetSize.height && gifData.length <= maxBytes) { return gifData; } // 计算新的大小 CGSize newSize = [NSData fitGifImageSize:size toSize:targetSize]; return [NSData resizeGifImageData:gifData toSize:newSize]; } /// gif 每一帧的图片大小应该稍微有点区别,不太一样,所以只调整 size,不压缩质量 + (NSData *)resizeGifImageData:(NSData *)gifData toSize:(CGSize)targetSize { if (targetSize.width == 0 || targetSize.height == 0) { return gifData; } //通过CFData读取gif文件的数据 CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)gifData, NULL); //获取gif文件的帧数 size_t count = CGImageSourceGetCount(source); //设置gif播放的时间 NSTimeInterval duration = 0.0f; CFDictionaryRef imageProperties = CGImageSourceCopyProperties(source, nil); NSLog(@"\n CGImageSourceCopyProperties %@ \n",imageProperties); // gif data properties NSMutableData *mutableData = [NSMutableData data]; CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)mutableData, kUTTypeGIF, count, NULL); CFDictionaryRef gifProperties; BOOL result = CFDictionaryGetValueIfPresent(imageProperties, kCGImagePropertyGIFDictionary, (const void **)&gifProperties); if (result) { CGImageDestinationSetProperties(destination, gifProperties); } CFRelease(imageProperties); for (size_t i = 0; i < count; i++) { //获取gif指定帧的像素位图 CGImageRef imageRef = CGImageSourceCreateImageAtIndex(source, i, NULL); if (!imageRef) { continue; } //获取每张图的播放时间 float frameDuration = [NSData frameDurationAtIndex:i source:source]; duration += frameDuration; // CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, i, nil); //图像的旋转方向 CGImagePropertyOrientation orientation = (uint32_t)[(__bridge NSNumber *)CFDictionaryGetValue(cfFrameProperties, kCGImagePropertyOrientation) integerValue]; if (orientation == 0) { // 方向丢失 orientation = kCGImagePropertyOrientationUp; } CGImageRef newImageRef = DDCreateResizeAndUpOrientationImageRef(imageRef, targetSize, orientation); // add quality CFMutableDictionaryRef mutDicRef = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, cfFrameProperties); float c = 0.5; CFNumberRef compressionQuality = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &c); CFDictionarySetValue(mutDicRef, kCGImageDestinationLossyCompressionQuality, compressionQuality); // orientation 必须修改,否则会丢失 旋转。 CGImagePropertyOrientation newOrientation = kCGImagePropertyOrientationUp; CFNumberRef newOrientationNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &newOrientation); CFDictionarySetValue(mutDicRef, kCGImagePropertyOrientation, newOrientationNum); // modify size CGFloat width = targetSize.width; CGFloat height = targetSize.height; CFNumberRef widthNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &width); CFNumberRef heightNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &height); CFDictionarySetValue(mutDicRef, kCGImagePropertyPixelWidth, widthNum); CFDictionarySetValue(mutDicRef, kCGImagePropertyPixelHeight, heightNum); NSLog(@" mutDicRef \n %@ \n",mutDicRef); CGImageDestinationAddImage(destination, newImageRef, mutDicRef); CFRelease(cfFrameProperties); CGImageRelease(newImageRef); CGImageRelease(imageRef); CFRelease(mutDicRef); CFRelease(compressionQuality); newImageRef = NULL; imageRef = NULL; mutDicRef = NULL; } BOOL success = CGImageDestinationFinalize(destination); CFRelease(destination); if (!success) { NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: NSLocalizedString(@"Could not finalize image destination", nil) }; NSLog(@"resizeGifImageDataToSize %@",userInfo); CFRelease(source); return nil; } CFRelease(source); if (mutableData.length > gifData.length) { return gifData; } return [NSData dataWithData:mutableData]; } #pragma mark gif 每一帧的 播放时间 + (float)frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source { float frameDuration = 0.1f; //获取这一帧图片的属性字典 CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil); NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties; //获取gif属性字典 NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary]; //获取这一帧持续的时间 NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime]; if (delayTimeUnclampedProp) { frameDuration = [delayTimeUnclampedProp floatValue]; } else { NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime]; if (delayTimeProp) { frameDuration = [delayTimeProp floatValue]; } } //如果帧数小于0.1,则指定为0.1 if (frameDuration < 0.011f) { frameDuration = 0.100f; } CFRelease(cfFrameProperties); return frameDuration; } 复制代码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Docker从入门到实战
黄靖钧 / 机械工业出版社 / 2017-6 / 69.00元
本书从Docker的相关概念与基础知识讲起,结合实际应用,通过不同开发环境的实战例子,详细介绍了Docker的基础知识与进阶实战的相关内容,以引领读者快速入门并提高。 本书共19章,分3篇。第1篇容器技术与Docker概念,涵盖的内容有容器技术、Docker简介、安装Docker等。第2篇Docker基础知识,涵盖的内容有Docker基础、Docker镜像、Dockerfile文件、Dock......一起来看看 《Docker从入门到实战》 这本书的介绍吧!