iOS压缩高清大图的方法

2018-11-21  本文已影响26人  咸鱼有只喵

普通方法有可能爆掉内存,这里改一下:

方法一:


/*
 *定义一张图片可以占用的最大空间
 */
//每个像素占用的字节数
static const size_t kBytesPerPixel = 4;
//色彩空间占用的字节数
static const size_t kBitsPerComponent = 8;
static const CGFloat kDestImageSizeMB = 60.0f;

static const CGFloat kSourceImageTileSizeMB = 20.0f;

static const CGFloat kBytesPerMB = 1024.0f * 1024.0f;
//1MB可以存储多少像素
static const CGFloat kPixelsPerMB = kBytesPerMB / kBytesPerPixel;
//如果像素小于这个值,则不解压缩
static const CGFloat kDestTotalPixels = kDestImageSizeMB * kPixelsPerMB;
static const CGFloat kTileTotalPixels = kSourceImageTileSizeMB * kPixelsPerMB;

static const CGFloat kDestSeemOverlap = 2.0f;   // the numbers of pixels to overlap the seems where tiles meet.




- (UIImage *)downsize:(nullable UIImage *)sourceImage{
    // 创建NSAutoreleasePool
    @autoreleasepool {
        
        // 获取图片,这个时候是不会绘制
        if( sourceImage == nil ) NSLog(@"input image not found!");
        
        // 拿到当前图片的宽高
        CGSize sourceResolution = CGSizeZero;
        sourceResolution.width = CGImageGetWidth(sourceImage.CGImage);
        sourceResolution.height = CGImageGetHeight(sourceImage.CGImage);
        
        // 当前图片的像素
        float sourceTotalPixels = sourceResolution.width * sourceResolution.height;
        
        // 当前图片渲染到界面上的大小
        float sourceTotalMB = sourceTotalPixels / kPixelsPerMB;
        
        // 获取当前最合适的图片渲染大小,计算图片的缩放比例
        float imageScale = kDestTotalPixels / sourceTotalPixels;
        
        // 拿到缩放后的宽高
        CGSize destResolution = CGSizeZero;
        
        destResolution.width = (int)( sourceResolution.width * imageScale );
        destResolution.height = (int)( sourceResolution.height * imageScale );
        
        // 生成一个rgb的颜色空间
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        
        // 缩放情况下的每一行的字节数
        int bytesPerRow = kBytesPerPixel * destResolution.width;
        
        // 计算缩放情况下的位图大小,申请一块内存
        void* destBitmapData = malloc( bytesPerRow * destResolution.height );
        if( destBitmapData == NULL ) NSLog(@"failed to allocate space for the output image!");
        
        // 根据计算的参数生成一个合适尺寸的位图
        CGContextRef destContext;
        destContext = CGBitmapContextCreate( destBitmapData, destResolution.width, destResolution.height, 8, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast );
        
        // 如果生成失败了释放掉之前申请的内存
        if( destContext == NULL ) {
            free( destBitmapData );
            NSLog(@"failed to create the output bitmap context!");
        }
        
        // 释放掉颜色空间
        CGColorSpaceRelease( colorSpace );
        
        // 坐标系转换
        CGContextTranslateCTM( destContext, 0.0f, destResolution.height );
        CGContextScaleCTM( destContext, 1.0f, -1.0f );
        
        // 分块绘制的宽度(原始宽度)
        CGRect sourceTile = CGRectZero;
        sourceTile.size.width = sourceResolution.width;
        
        // 分块绘制的高度
        sourceTile.size.height = (int)( kTileTotalPixels / sourceTile.size.width );
        NSLog(@"source tile size: %f x %f",sourceTile.size.width, sourceTile.size.height);
        sourceTile.origin.x = 0.0f;
        
        // 绘制到位图上的宽高
        CGRect destTile = CGRectZero;
        destTile.size.width = destResolution.width;
        destTile.size.height = sourceTile.size.height * imageScale;
        destTile.origin.x = 0.0f;
        NSLog(@"dest tile size: %f x %f",destTile.size.width, destTile.size.height);
        
        // 重合的像素
        float sourceSeemOverlap =  (int)((kDestSeemOverlap/destResolution.height)*sourceResolution.height);
        //NSLog(@"dest seem overlap: %f, source seem overlap: %f",destSeemOverlap, sourceSeemOverlap);
        CGImageRef sourceTileImageRef;
        
        // 分块绘制需要多少次才能绘制完成
        int iterations = (int)( sourceResolution.height / sourceTile.size.height );
        int remainder = (int)sourceResolution.height % (int)sourceTile.size.height;
        if( remainder ) iterations++;
        
        // 添加重合线条
        float sourceTileHeightMinusOverlap = sourceTile.size.height;
        sourceTile.size.height += sourceSeemOverlap;
        destTile.size.height += kDestSeemOverlap;
        
        // 分块绘制
        for( int y = 0; y < iterations; ++y ) {
            // create an autorelease pool to catch calls to -autorelease made within the downsize loop.
            @autoreleasepool {
                
                NSLog(@"iteration %d of %d",y+1,iterations);
                sourceTile.origin.y = y * sourceTileHeightMinusOverlap + sourceSeemOverlap;
                destTile.origin.y = ( destResolution.height ) - ( ( y + 1 ) * sourceTileHeightMinusOverlap * imageScale + kDestSeemOverlap );
                
                // 分块拿到图片数据
                sourceTileImageRef = CGImageCreateWithImageInRect( sourceImage.CGImage, sourceTile );
                
                // 计算绘制的位置
                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 );
            }
        }
        
        CGImageRef destImageRef = CGBitmapContextCreateImage(destContext);
        CGContextRelease(destContext);
        if (destImageRef == NULL) {
            return sourceImage;
        }
        //生成处理结束以后的图片
        UIImage *destImage = [UIImage imageWithCGImage:destImageRef scale:sourceImage.scale orientation:sourceImage.imageOrientation];
        CGImageRelease(destImageRef);
        if (destImage == nil) {
            return sourceImage;
        }

        return destImage;
    }
}

方法二:

static size_t getAssetBytesCallback(void *info, void *buffer, off_t position, size_t count) {
    NSData *rep = (__bridge id)info;

    NSError *error = nil;
    BDALOG_INFO(@"%@ %@ ", @(position), @(count));
    [rep getBytes:(void *)buffer range:NSMakeRange(position, count)];
    return count;
}

static void releaseAssetCallback(void *info) { CFRelease(info); }

- (UIImage *)thumbnailForAsset:(NSData *)assetData maxPixelSize:(NSUInteger)size {
    NSParameterAssert(self.asset != nil);
    NSParameterAssert(size > 0);
    CGDataProviderDirectCallbacks callbacks = {
        .version = 0,
        .getBytePointer = NULL,
        .releaseBytePointer = NULL,
        .getBytesAtPosition = getAssetBytesCallback,
        .releaseInfo = releaseAssetCallback,
    };

    CGDataProviderRef provider = CGDataProviderCreateDirect((void *)CFBridgingRetain(assetData), [assetData length],
                                                            &callbacks); // CGDataProviderCreateWithURL(url);
    CGImageSourceRef source = CGImageSourceCreateWithDataProvider(provider, NULL);

    CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(source, 0, (__bridge CFDictionaryRef) @{
        (NSString *)kCGImageSourceCreateThumbnailFromImageAlways : @YES,
        (NSString *)kCGImageSourceThumbnailMaxPixelSize : @(size),
        (NSString *)kCGImageSourceCreateThumbnailWithTransform : @YES,
    });
    CFRelease(source);
    CFRelease(provider);

    if (!imageRef) {
        return nil;
    }

    UIImage *toReturn = [UIImage imageWithCGImage:imageRef];

    CFRelease(imageRef);

    return toReturn;
}

上一篇下一篇

猜你喜欢

热点阅读