Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Graphics / Quartz 2D image resizing crashes

The task is to resize an image.

I have read this post and adopted CGBitmapContextCreate & CGContextDrawImage approach. That is how my resizing function looks like:

extension UIImage {

    func with(maxHeight: CGFloat, maxWidth: CGFloat) -> UIImage? {
        guard let image = self.cgImage else {
            return nil
        }
        var height = CGFloat(image.height)
        var width = CGFloat(image.width)
        guard height > 0 && width > 0 else {
            return nil
        }
        let maxHeight = round(maxHeight)
        let maxWidth = round(maxWidth)
        guard height > maxHeight || width > maxWidth else {
            return nil
        }
        let heightProportions = height / maxHeight
        let widthProportions = width / maxWidth
        height = heightProportions > widthProportions ? maxHeight : round(height / widthProportions)
        width = widthProportions > heightProportions ? maxWidth : round(width / heightProportions)
        let size = CGSize(width: width, height: height)
        let bitmapInfo = image.bitmapInfo.rawValue
        let bitsPerComponent = image.bitsPerComponent
        let bytesPerRow = image.bytesPerRow
        let space = image.colorSpace ?? CGColorSpaceCreateDeviceRGB()
        let context = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: space, bitmapInfo: bitmapInfo)
        context?.interpolationQuality = .high
        context?.draw(image, in: CGRect.init(origin: .zero, size: size))
        guard let newImage = context?.makeImage() else {
            return nil
        }
        return UIImage(cgImage: newImage)
    }
}

This function did work well during testing but I've got a crash in production at line:

context?.draw(image, in: CGRect.init(origin: .zero, size: size))

Stacktrace:

#11. Crashed: com.apple.root.utility-qos
0  CoreGraphics                   0x184d9a59c ERROR_CGDataProvider_BufferIsNotReadable + 12
1  CoreGraphics                   0x184d9a2c0 CGDataProviderRetainBytePtr + 216
2  CoreGraphics                   0x184e8d06c get_image_pointer + 64
3  CoreGraphics                   0x184e8d2c8 img_raw_access + 52
4  CoreGraphics                   0x184e88000 img_interpolate_read + 708
5  CoreGraphics                   0x184e8c14c img_data_lock + 7048
6  CoreGraphics                   0x184e8a56c CGSImageDataLock + 184
7  CoreGraphics                   0x184caa628 ripc_AcquireRIPImageData + 308
8  CoreGraphics                   0x184e9f1b0 ripc_DrawImage + 644
9  CoreGraphics                   0x184e8efac CGContextDrawImageWithOptions + 632
10 libswiftCoreGraphics.dylib     0x104781638 (Missing)

So, I have two questions:

  1. What should I do to reproduce a crash situation and fix the issue?
  2. Is there any other approach to get a resized image of a good quality?
like image 597
iWheelBuy Avatar asked Nov 07 '22 09:11

iWheelBuy


1 Answers

I use this simpler extension to wrap UIImage's draw(in:). I have found it to be high quality and reasonably fast.

extension UIImage {

    public func resized(maxSize: CGSize) -> UIImage? {
        let imageSize = self.size
        guard imageSize.height > 0, imageSize.width > 0 else { return nil }

        let ratio = min(maxSize.width/imageSize.width, maxSize.height/imageSize.height)
        let newSize = CGSize(width: imageSize.width*ratio, height: imageSize.height*ratio)

        let renderer = UIGraphicsImageRenderer(size: newSize)
        return renderer.image(actions: { (ctx) in
            self.draw(in: CGRect(origin: .zero, size: newSize))
        })
    }
}

Usage:

let resizedImage = myImage.resized(maxSize: CGSize(width: maxWidth, height: maxHeight))

Note that the size here is measured in points, meaning that the underlying number of pixels will be automatically multiplied by the device's scale factor (e.g. 2x for retina displays). In other words, this method is designed for situations where the resulting UIImage will be shown on screen.

like image 124
Robin Stewart Avatar answered Nov 16 '22 07:11

Robin Stewart