简体   繁体   中英

Why use a lot of memory when drawing image with UIGraphicsImageRenderer?

I want to add a white border to the photo, while preserving the quality of the photo, so I use UIGraphicsImageRenderer to draw a white background, and then draw the photo up, the result is a dramatic increase in memory usage, is there any better way?

The resolution of the original picture is 4032 * 3024.

let renderer = UIGraphicsImageRenderer(size: CGSize(width: canvasSideLength, height: canvasSideLength))
let newImage = renderer.image { context in
    UIColor.white.setFill()
    context.fill(CGRect(x: 0, y: 0, width: canvasSideLength, height: canvasSideLength))
    image.draw(in: CGRect(x: photoCanvasX, y: photoCanvasY, width: photoCanvasWidth, height: photoCanvasHeight))
}

在此处输入图像描述 在此处输入图像描述 在此处输入图像描述

When considering the memory used, don't be misled by the size of the JPG or PNG file, because that is generally compressed. You will require four bytes per pixel when performing image operations in memory (eg width × height × 4, in pixels).

Worse, by default, UIGraphicsImageRenderer will generate images at screen resolution (eg potentially 2× or 3× depending upon your device). Eg on a 3× device, consider:

let rect = CGRect(origin: .zero, size: CGSize(width: 8_519, height: 8_519))
let image = UIGraphicsImageRenderer(bounds: rect).image { _ in
    UIColor.white.setFill()
    UIBezierPath(rect: rect).fill()
}
print(image.cgImage!.width, "×", image.cgImage!.height)

That will print:

25557 × 25557

When you consider that it then takes 4 bytes per pixel, that adds up to 2.6gb. Even if the image is only 4,032 × 3,024, as suggested by your revised question, that's still 439mb per image.

You may want to make sure to specify an explicit scale of 1:

let rect = CGRect(origin: .zero, size: CGSize(width: 8_519, height: 8_519))
let format = UIGraphicsImageRendererFormat()
format.scale = 1
let image = UIGraphicsImageRenderer(bounds: rect, format: format).image { _ in
    UIColor.white.setFill()
    UIBezierPath(rect: rect).fill()
}
print(image.cgImage!.width, "×", image.cgImage!.height)

That will print, as you expected:

8519 × 8519

Then you are only going to require 290mb for the image. That's still a lot of memory, but a lot less than if you use the default scale (on retina devices). Or, considering your revised 4,032 × 3,024 image, this 1× image could take only 49mb, 1/9th the size of the corresponding 3× image where you didn't set the scale.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM