美文网首页OC开发图片处理
iOS压缩高清大图片

iOS压缩高清大图片

作者: 飘金 | 来源:发表于2017-10-18 12:35 被阅读36次

    首先要知道为啥压缩大图片会内存暴增,然后降下来?(UIImageJPEGRepresentation,UIImagePNGRepresentation)

    图片的压缩会瞬间消耗很大内存,apple官方文档对CGBitmapContextCreate函数的注解:

    bitsPerComponent 表示存入内存中的每个像素中的每一个组件所占的位数;
    bytesPerRow 表示存入内存中的位图的每一行所占的字节数;
    

    解压缩操作中,每一个像素点都会分配一个空间来存储相关值,那么分辨率越高的图片,就意味着更多数量的像素点,也就意味着需要分配更多的空间!所以对于高分辨率图来说,解压缩操作的确会造成内存飙升,即使是几M的图片,解压缩过程中也是有可能消耗上G的内存!可以根据苹果官方的加载大图片例子得出结论,例子中有一句注释:

    #define kImageFilename @"large_leaves_70mp.jpg" // 7033x10110 image, 271 MB uncompressed
    

    large_leaves_70mp.jpg图片是7033x10110(占用磁盘大小8.3MB),分辨率 = 7033 x 10110 x 4(ARGB),对应位图占用大小 = 分辨率 x 1024 x 1024 ( = 271MB),解压会把图片转成位图,也就意味着会占用271MB内存,所以解压过程内存会瞬间消耗很大,等转成NSData后位图的内存就会被回收掉,内存就降下来,这时候NSData占用的大小即是图片的实际大小,该过程中由于会转成位图,而位图的大小是比图片的实际的大小大很多的,内存暴增的点就在位图。位图的内存大小计算是根据图片的分辨率而来(分辨率(width x heigth) x 1024 x 1024 x 4 (ARGB)),所以一般来说图片分辨率越高转成的位图占用的内存空间越大。

    其次得知道压缩图片的目的,用于做啥?

    这么一说感觉都用不到压缩了?一般的小图片当然还是要用到压缩,显示或上传,那如果非要压缩大图片呢?压缩大图片低设备肯定要挂,参考了苹果官方加载大图片的例子,大图片分成很多小片,利用GPU来处理每个小分片,最后显示,那么,大图片的压缩是不是也可以分片压缩呢?

    答案是可以的,整体思路和图片分片显示一样的:

    • 获取到某一图片分片(如果太大可以改变分片的大小到合适大小)

    • 对获取到的图片分片压缩,压缩得到的data可以直接使用NSFilehandle断点写入磁盘中(大图片相当于大文件,大文件的断点写入可以参考iOS大文件下载,断点下载),然后释放data,最好不要保存到内存中.

    • 等所有的图片分片都压缩完,自然也就压缩完。这时图片的data已经全部获取,接下来改干嘛就干嘛。

    代码其实没多大变化,直接在苹果官方的加载图片的例子中修改,因为要用到苹果官方加载大图片例子中的图片分片

    /** 循环获取到大图片的每个小分片*/
    for( int y = 0; y < iterations; ++y ) {
                @autoreleasepool {
                    NSLog(@"iteration %d of %d",y+1,iterations);
                   /** sourceTile大图片每个分片大小*/
                   sourceTile.origin.y = y * sourceTileHeightMinusOverlap + sourceSeemOverlap;
                    destTile.origin.y = ( destResolution.height ) - ( ( y + 1 ) * sourceTileHeightMinusOverlap * imageScale + destSeemOverlap );
                   /** 大图片的一个小分片sourceTileImageRef*/
                   sourceTileImageRef = CGImageCreateWithImageInRect( sourceImage.CGImage, sourceTile );
                    /** 这边是保存到内存中,图片大小45MB左右,最后iphone4s内存在65MB左右,还是可以接受的,但是根据业务的需求,最好是把data写入磁盘中去,而不是保存到内存,我这边是偷懒了一下。*/
    //                [UIImagePNGRepresentation([[UIImage alloc] initWithCGImage:sourceTileImageRef]) writeToFile:path atomically:YES];
                    [bigData appendData:UIImagePNGRepresentation([[UIImage alloc] initWithCGImage:sourceTileImageRef])];
                    if( y == iterations - 1 && remainder ) {
                        float dify = destTile.size.height;
                        destTile.size.height = CGImageGetHeight( sourceTileImageRef ) * imageScale;
                        dify -= destTile.size.height;
                        destTile.origin.y += dify;
                    }
                    
                    CGContextDrawImage( destContext, destTile, sourceTileImageRef );
                    CGImageRelease( sourceTileImageRef );
                }
                if( y < iterations - 1 ) {
                    sourceImage = [[UIImage alloc] initWithContentsOfFile:imagePath];
                    [self performSelectorOnMainThread:@selector(updateScrollView:) withObject:nil waitUntilDone:YES];
                }
            }
    

    注意

    上述压缩方式,会导致图片错乱,以为每次压缩生成的NSData时候一张分片图片,下一张分片图的NSData直接追加在上一张的后面iOS直接识别不了,我猜测一张完整的图片有开始标记和结尾标记,iOS系统在识别时只发现了第一张的开始标记和第一张的结束标记,这样iOS认为已经加载好了图片,就不会往下加载,故图片会错乱,只显示第一张分片的图片。这个是一个问题,还没解决。目前想到的是,每次压缩一个图片分片就保存到磁盘,最后把所有的图片合成一张。

    相关文章

      网友评论

        本文标题:iOS压缩高清大图片

        本文链接:https://www.haomeiwen.com/subject/ocgsuxtx.html