基于 NSData 的图片压缩

栏目: IOS · 发布时间: 7年前

内容简介:当前网上找到的图片压缩方法大部分都是基于 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, GifLive 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;
}
复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

复杂网络理论及其应用

复杂网络理论及其应用

汪小帆、李翔、陈关荣 / 清华大学出版社 / 2006 / 45.00元

国内首部复杂网络专著 【图书目录】 第1章 引论 1.1 引言 1.2 复杂网络研究简史 1.3 基本概念 1.4 本书内容简介 参考文献 第2章 网络拓扑基本模型及其性质 2.1 引言 2.2 规则网络 2.3 随机图 2.4 小世界网络模型 2.5 无标度网络模型 ......一起来看看 《复杂网络理论及其应用》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具