【问题标题】:Resizing images using CGImageCreate to reduce memory usage使用 CGImageCreate 调整图像大小以减少内存使用
【发布时间】:2014-03-04 03:49:52
【问题描述】:

有时图像非常大(从互联网下载),所以我需要调整它们的大小,但不想引起内存问题,这意味着我不想将所有图像数据加载到内存中并在记忆。

我正在研究各种调整图像大小的方法,监控它们的内存使用情况。具体来说,CGContextDrawImageCGImageSourceCreateThumbnailAtIndexCGImageCreateCGDataProviderRef

现在我在使用CGImageCreateCGDataProviderRef时遇到了一些问题,我的代码是这样的:

//This method read the data from filePath, resize the image and write the resized data to the destPath
- (void)resizeImageFileUsingCGImageCreate:(NSString*)filePath toDestPath:(NSString*)destPath {

    CGFloat factor = 0.2;
    CGSize originalSize = [self sizeOfImageAtURL:[[NSURL alloc] initFileURLWithPath:filePath]];
    CGSize destSize = CGSizeMake((NSUInteger)(originalSize.width * factor), (NSUInteger)(originalSize.height * factor));



    UIImage *srcImage = [UIImage imageWithContentsOfFile:filePath];
    CGImageRef srcImgRef = srcImage.CGImage;
    NSData *data = [NSData dataWithContentsOfFile:filePath];
    CFDataRef cfData = (__bridge_retained CFDataRef)data;
    CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(cfData);
    CFRelease(cfData);

    CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(srcImgRef);

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    CGImageRef imageRef = CGImageCreate(destSize.width, destSize.height, 8, 32, 4 * destSize.width, colorSpace, bitmapInfo, dataProvider, NULL, NO, 0);

    CGDataProviderRelease(dataProvider);
    CGColorSpaceRelease(colorSpace);
    UIImage *image = nil;
    if (imageRef != NULL) {
        image = [[UIImage alloc] initWithCGImage:imageRef];
        CGImageRelease(imageRef);
        //Why UIImageJPEGRepresentation(image, 0.5) returns nil?
        [UIImageJPEGRepresentation(image, 0.5) writeToFile:destPath atomically:YES];


    }


}

在代码的最后一行,UIImageJPEGRepresentation(image, 0.5) 总是返回 nil,这让我很奇怪。您能帮我指出使用CGImageCreateCGDataProviderRef 调整图像大小的正确方法吗?非常感谢!

【问题讨论】:

  • UIImageJPEGRepresentation之前,可以在UIImageView中显示UIImage吗?我认为CGDataProvider 的原始数据应该是未压缩的 RGB 位图数据,而不是 png/jpeg 文件的数据。
  • 谢谢@Alleen!是的,我也注意到了这个问题,因为如果我用CGDataProviderRef dataProvider = CGImageGetDataProvider(srcImgRef) 更改dataProvider,并更改factor = 1,我将得到相同大小的图像。但我不知道如何在CGDataProviderRef 中缩放数据
  • 也许您应该将数据作为数组进行操作?

标签: ios objective-c core-graphics core-image


【解决方案1】:

我将向您展示我认为更好的方法,而不是直接回答您的问题:使用 ImageIO 框架。

#import <ImageIO/ImageIO.h>

以下是如何从磁盘加载图像的缩小版本(imageSource 是一个 NSURL;scalemaxwmaxh 必须预先设置为您想要的比例和最大尺寸):

CGImageSourceRef src = 
    CGImageSourceCreateWithURL((__bridge CFURLRef)imageSource, nil);
NSDictionary* d = @{
    (id)kCGImageSourceShouldAllowFloat: (id)kCFBooleanTrue,
    (id)kCGImageSourceCreateThumbnailWithTransform: (id)kCFBooleanTrue,
    (id)kCGImageSourceCreateThumbnailFromImageAlways: (id)kCFBooleanTrue,
    (id)kCGImageSourceThumbnailMaxPixelSize: @((int)(maxw > maxh ? maxw : maxh))
};
CGImageRef imref = 
    CGImageSourceCreateThumbnailAtIndex(src, 0, (__bridge CFDictionaryRef)d);
if (NULL != src)
    CFRelease(src);
UIImage* im = 
    [UIImage imageWithCGImage:imref scale:scale orientation:UIImageOrientationUp];
if (NULL != imref)
    CFRelease(imref);

这段代码的好处是我们从不将大版本的图像保存在内存中;它永远不会加载。我们最终得到了一个较小的 UIImage,我们准备在我们的界面中使用它。

如果您拥有的是来自 Internet 的 NSData,并且您尚未将其保存到磁盘,那么要创建您的图像源,请使用:

src = CGImageSourceCreateWithData((__bridge CFDataRef)imageSource, nil);

但实际上,如果图像很大,您应该使用下载任务,以便整个图像从不在内存中,而是在到达时保存到磁盘。

【讨论】:

  • 谢谢@matt!使用CGImageSourceCreateThumbnailAtIndex 是我正在研究的方法之一,这很好。但我需要知道CGImageCreateCGDataProviderRef 的使用方式,因为我想做一个基准来展示它们的优缺点。
【解决方案2】:

而不是这样做:

    [UIImageJPEGRepresentation(image, 0.5) writeToFile:destPath atomically:YES];

你应该这样做:

NSData *imageData = UIImageJPEGRepresentation(image, 0.5);
if(imageData) 
{
    NSError *error = nil;
    BOOL success = [imageData writeToFile:destPath options:NSDataWritingAtomic error:&error];
    if(NO == success)
    {
        NSLog(@"couldn't write image data to file %@ because of error %@", destPath, error)
    }
}

在我的代码版本中,我将图像数据放入 NSData 对象,然后如果写入图像数据失败,则会打印出错误。

【讨论】:

  • 谢谢@Michael!我认为不需要提取代码[UIImageJPEGRepresentation(image, 0.5) writeToFile:destPath atomically:YES],因为将消息发送到nil 对象是安全的。
  • 如果“image”到达“UIImageJPEGRepresentation()”时不为零?
猜你喜欢
  • 2011-06-04
  • 1970-01-01
  • 1970-01-01
  • 2011-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-24
  • 2013-11-19
相关资源
最近更新 更多