Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drawing image in Swift takes forever

In my app I'm creating an image mapping floats to a pixel value and use it as an overlay on Google Maps, but it takes forever to do, the same thing in Android is almost instant. My code looks like this:

private func imageFromPixels(pixels: [PixelData], width: Int, height: Int) -> UIImage? {

    let bitsPerComponent = 8
    let bitsPerPixel = bitsPerComponent * 4
    let bytesPerRow = bitsPerPixel * width / 8

    let providerRef = CGDataProvider(
        data: NSData(bytes: pixels, length: height * width * 4) 
    )

    let cgimage = CGImage(
        width: width,
        height: height,
        bitsPerComponent: bitsPerComponent,
        bitsPerPixel: bitsPerPixel,
        bytesPerRow: bytesPerRow,
        space: CGColorSpaceCreateDeviceRGB(),
        bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue),
        provider: providerRef!,
        decode: nil,
        shouldInterpolate: true,
        intent: .defaultIntent
    )

    if cgimage == nil {
        print("CGImage is not supposed to be nil")
        return nil
    }
    return UIImage(cgImage: cgimage!)
}

Any suggestions on how this can take so long to do? I can see it uses about 96% CPU power.

func fromData(pair: AllocationPair) -> UIImage? {

    let table = pair.table
    let data = pair.data

    prepareColors(allocations: table.allocations)

    let height = data.count
    let width = data[0].count

    var colors = [PixelData]()

    for row in data {
        for val in row {

            if (val == 0.0) {
                colors.append(PixelData(a: 0, r: 0, g: 0, b: 0))
                continue
            }

            if let interval = findInterval(table: table, value: val) {
                if let color = intervalColorDict[interval] {
                    colors.append(PixelData(a: color.a, r: color.r, g: color.g, b: color.b))
                } 
            }
        }
    }

    return imageFromPixels(pixels: colors, width: width, height: height)
}

I've tried to time profile it, and this is the output where it takes time.

enter image description here

like image 474
Recusiwe Avatar asked Feb 20 '17 20:02

Recusiwe


2 Answers

I tried your code and I found out that the problem isn't with your function.

I think that you should use a UInt8 based pixel structure and not a CGFloat based structure.

I translated your code for a cocoa application, and this is the result:

public struct PixelData {
    var a: UInt8
    var r: UInt8
    var g: UInt8
    var b: UInt8
}

func imageFromPixels(pixels: [PixelData], width: Int, height: Int) -> NSImage? {

    let bitsPerComponent = 8
    let bitsPerPixel = bitsPerComponent * 4
    let bytesPerRow = bitsPerPixel * width / 8

    let providerRef = CGDataProvider(
        data: NSData(bytes: pixels, length: height * width * 4)
    )

    let cgimage = CGImage(
        width: width,
        height: height,
        bitsPerComponent: bitsPerComponent,
        bitsPerPixel: bitsPerPixel,
        bytesPerRow: bytesPerRow,
        space: CGColorSpaceCreateDeviceRGB(),
        bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue),
        provider: providerRef!,
        decode: nil,
        shouldInterpolate: true,
        intent: .defaultIntent
    )

    if cgimage == nil {
        print("CGImage is not supposed to be nil")
        return nil
    }
    return NSImage(cgImage: cgimage!, size: NSSize(width: width, height: height))
}

var img = [PixelData]()

for i: UInt8 in 0 ..< 20 {
    for j: UInt8 in 0 ..< 20 {
        // Creating a red 20x20 image.
        img.append(PixelData(a: 255, r: 255, g: 0, b: 0))
    }
}

var ns = imageFromPixels(pixels: img, width: 20, height: 20)

This code is fast and light, here's the debug system impact values:

enter image description here

I think that the problem comes up with the part which loads the pixel data, check it and be sure that it works properly.

like image 139
Cristian Avatar answered Oct 25 '22 07:10

Cristian


If you need to access and change the actual bitmap data, you can use CGImage. In other cases use CIImage represented objects. So work with UIImage, convert UIImage to CIImage, make any manipulations on it, and then convert it back to a UIImage (as you did with CGImage in your code).

Here's a post explaining what is what: UIImage vs. CIImage vs. CGImage

Core Image doesn’t actually render an image until it is told to do so. This “lazy evaluation” method allows Core Image to operate as efficiently as possible.

CIImage page in Apple Developer Documentation

like image 27
Andy Jazz Avatar answered Oct 25 '22 06:10

Andy Jazz