调整UIImage的大小而不将其完全加载到内存中?

时间:2011-05-02 17:21:26

标签: objective-c cocoa-touch ios uiimage

我正在开发一个应用程序,用户可能会尝试加载非常大的图像。这些图像首先在表格视图中显示为缩略图。我的原始代码会在大图像上崩溃,因此我将其重写为首先将图像直接下载到磁盘。

是否有一种已知方法可以在磁盘上调整图像大小而不通过UIImage将其完全加载到内存中?我目前正在尝试使用UIImage上的类别详细调整here,但我的应用在尝试缩放非常大的图片时崩溃(例如,this - 警告,巨大的形象)。

2 个答案:

答案 0 :(得分:49)

你应该看看ImageIO.framework中的CGImageSource,但它只能在iOS 4.0之后使用。

快速举例:

-(UIImage*)resizeImageToMaxSize:(CGFloat)max path:(NSString*)path
{
    CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)[NSURL fileURLWithPath:path], NULL);
    if (!imageSource)
        return nil;

    CFDictionaryRef options = (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:
        (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform,
        (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageIfAbsent,
        (id)@(max),
        (id)kCGImageSourceThumbnailMaxPixelSize,
        nil];
    CGImageRef imgRef = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options);

    UIImage* scaled = [UIImage imageWithCGImage:imgRef];

    CGImageRelease(imgRef);
    CFRelease(imageSource);

    return scaled;
}

答案 1 :(得分:1)

根据本次会议iOS Memory Deep Dive,我们最好使用new Map({ target: 'map', layers: [ new TileLayer({ source: new XYZ({ url: 'https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png' }) }) ], view: new View({ center: [0, 0], zoom: 2 }) }); 来缩小图像尺寸。

使用ImageIO缩小图像的缺点。

  • 将原始图像解压缩到内存中
  • 内部坐标空间变换很昂贵

使用UIImage

  • ImageIO可以读取图像大小和元数据信息,而不会弄脏内存。

  • ImageIO只能以调整大小的图像为代价来调整图像的大小。

关于内存中的图像

  • 内存使用与图像尺寸有关,与文件大小无关。
  • ImageIO始终使用UIGraphicsBeginImageContextWithOptions渲染格式,每个像素使用4个字节。
  • 图像具有SRGB 3个阶段。
  • load -> decode -> render的大小和调整大小很昂贵

对于下图,如果使用UIImage 我们只需要590KB即可加载图像,而我们需要 UIGraphicsBeginImageContextWithOptions = 10MB(解码时) enter image description here

在iOS 10中引入的2048 pixels x 1536 pixels x 4 bytes per pixel会自动选择iOS12中的最佳图形格式。这意味着,如果不需要SRGB,可以将UIGraphicsImageRenderer替换为UIGraphicsBeginImageContextWithOptions,从而节省75%的内存。

这是我有关iOS images in memory

的文章
UIGraphicsImageRenderer

func resize(url: NSURL, maxPixelSize: Int) -> CGImage? {
    let imgSource = CGImageSourceCreateWithURL(url, nil)
    guard let imageSource = imgSource else {
        return nil
    }

    var scaledImage: CGImage?
    let options: [NSString: Any] = [
            // The maximum width and height in pixels of a thumbnail.
            kCGImageSourceThumbnailMaxPixelSize: maxPixelSize,
            kCGImageSourceCreateThumbnailFromImageAlways: true,
            // Should include kCGImageSourceCreateThumbnailWithTransform: true in the options dictionary. Otherwise, the image result will appear rotated when an image is taken from camera in the portrait orientation.
            kCGImageSourceCreateThumbnailWithTransform: true
    ]
    scaledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary)

    return scaledImage
}


let filePath = Bundle.main.path(forResource:"large_leaves_70mp", ofType: "jpg")

let url = NSURL(fileURLWithPath: filePath ?? "")

let image = resize(url: url, maxPixelSize: 600)