Search code examples
iosswiftnsimagecgimage

NSImage/CGImage slice extension error: CGImageCreateWithImageProvider: invalid image size: 0 x 0


I have a method in an extension of NSImage, cgImageSlice, which is supposed to return [CGImage] containing each image slice. When running the code, I get the error CGImageCreateWithImageProvider: invalid image size: 0 x 0 after the first loop on line 18, however the size of the rect is not as such. Why is this?

extension NSImage {
    var cgImage: CGImage? {
        var proposedRect = CGRect(origin: .zero, size: size)
        return cgImage(forProposedRect: &proposedRect, context: nil, hints: nil)
    }

    func sliceSize(axis: Axis, slices: Int) -> CGSize {
        CGSize(width: axis == .horizontal ? size.width : size.width / CGFloat(slices), height: axis == .vertical ? size.height : size.height / CGFloat(slices))
    }

    func cgImageSlice(axis: Axis, slices: Int, sliceSize size: CGSize? = nil) -> [CGImage] {
        let sliceSize = { () -> CGSize in
            size == nil ? self.sliceSize(axis: axis, slices: slices) : size!
        }()
        var cgImages = [CGImage]()
        for i in 0 ..< slices {
            let rect = CGRect(x: CGFloat(i) * sliceSize.width, y: CGFloat(i) * sliceSize.height, width: sliceSize.width, height: sliceSize.height)
            if let cgImage = cgImage?.cropping(to: rect) {
                cgImages.append(cgImage)
            }
        }
        return cgImages
    }

    func nsImageSlice(axis: Axis, slices: Int) -> [NSImage] {
        let sliceSize = self.sliceSize(axis: axis, slices: slices)
        var images = [NSImage]()
        for cgImage in cgImageSlice(axis: axis, slices: slices, sliceSize: sliceSize) {
            images.append(NSImage(cgImage: cgImage, size: sliceSize))
        }
        return images
    }

    enum Axis {
        case horizontal
        case vertical
    }
}

Solution

  • I managed to overcome the issue by moving the rect outside of the for loop. I'm not quite sure why this fixed it, but it did!

    extension NSImage {
        var cgImage: CGImage? {
            var proposedRect = CGRect(origin: .zero, size: size)
            return cgImage(forProposedRect: &proposedRect, context: nil, hints: nil)
        }
    
        func sliceSize(axis: Axis, slices: Int) -> CGSize {
            CGSize(width: axis == .horizontal ? size.width : size.width / CGFloat(slices), height: axis == .vertical ? size.height : size.height / CGFloat(slices))
        }
    
        func cgImageSlice(axis: Axis, slices: Int, sliceSize size: CGSize? = nil) -> [CGImage] {
            guard let cgImage = self.cgImage else {
                return []
            }
            let sliceSize = { () -> CGSize in
                size == nil ? self.sliceSize(axis: axis, slices: slices) : size!
            }()
            var cgImages = [CGImage]()
            var rect = CGRect(origin: .zero, size: sliceSize)
            for i in 1 ... slices {
                if let cropped = cgImage.cropping(to: rect) {
                    cgImages.append(cropped)
                }
                rect.origin = CGPoint(x: 0, y: sliceSize.height * CGFloat(i))
            }
            return cgImages
        }
    
        func nsImageSlice(axis: Axis, slices: Int) -> [NSImage] {
            let sliceSize = self.sliceSize(axis: axis, slices: slices)
            var images = [NSImage]()
            for cgImage in cgImageSlice(axis: axis, slices: slices, sliceSize: sliceSize) {
                images.append(NSImage(cgImage: cgImage, size: sliceSize))
            }
            return images
        }
    
        enum Axis {
            case horizontal
            case vertical
        }
    }