1

I'm trying to create and save a .png from raw pixel data. I create an array of UInt8 where each number is a rgba value, kinda like this: [r, g, b, a, r, g, b, a...]. I can use this array to create a CGImage just fine. I can even use the CGImage to create an NSImage. The NSImage displays how I expect it to display when its loaded into an NSImageView.

What I want to do is to save an NSImage to disk. I've tried calling TIFFRepresentation on the NSImage and saving the NSData to "~/Desktop", but no file is saved. Any thoughts?

var pixels = [UInt8]()

for wPixel in 0...width {
    for hPixel in 0...height {
        pixels.append(0xff)
        pixels.append(0xaa)
        pixels.append(UInt8(wPixel % 200))
        pixels.append(0x00)     
    }
}

let image = createImage(100, height:100, pixels: pixels)
let nsImage = NSImage(CGImage: image, size: CGSize(width: 100, height: 100))
NSBitmapImageRep(data: nsImage.TIFFRepresentation!)!.representationUsingType(.NSPNGFileType, properties: [:])!.writeToFile("~/Desktop/image.png", atomically: true)



func createImage(width: Int, height: Int, pixels:Array<UInt8>) -> CGImage{
    let componentsPerPixel: Int = 4; // rgba
    let provider: CGDataProviderRef = CGDataProviderCreateWithData(nil,
                                                                   pixels,
                                                                   width * height * componentsPerPixel,
                                                                   nil)!;

    let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
    let bitmapInfo:CGBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.None.rawValue)

    let bitsPerComponent = 8
    let bitsPerPixel = 32

    let bytesPerRow = (bitsPerComponent * width) ;

    let cgImage = CGImageCreate(
        width,
        height,
        bitsPerComponent,
        bitsPerPixel,
        bytesPerRow,
        rgbColorSpace,
        bitmapInfo,
        provider,
        nil,
        true,
        .RenderingIntentDefault
    )

    print(cgImage.debugDescription)
    return cgImage!;
}
Tarvo Mäesepp
  • 4,477
  • 3
  • 44
  • 92
Lukas Valine
  • 183
  • 9
  • 3
    You need to expand tilde in path – Leo Dabus Jul 13 '16 at 20:17
  • how to get the desktop url http://stackoverflow.com/a/27756077/2303865 – Leo Dabus Jul 13 '16 at 20:18
  • I remember now I already tried that. I get: "CGImageDestinationFinalize failed for output type 'public.tiff' " and also "fatal error: unexpectedly found nil while unwrapping an Optional value" – Lukas Valine Jul 13 '16 at 20:23
  • Make sure you are not using playground to test it. Don't force unwrapp your optionals. Get rid of your !!!s – Leo Dabus Jul 13 '16 at 20:25
  • I'm not. Its a simple mac app. Also tried it as a command line tool. Same error both times. – Lukas Valine Jul 13 '16 at 20:28
  • I changed all the !!! to "if let unwrapped = stuff {}" type things. That got rid of one error. However, I still get "CGImageDestinationFinalize failed for output type 'public.tiff'" – Lukas Valine Jul 13 '16 at 20:37
  • Is it TIFF or PNG? – Leo Dabus Jul 13 '16 at 20:38
  • I'm trying to save a PNG. I'm not sure where public.tiff is coming from. I googled around and I can't find anything on it. – Lukas Valine Jul 13 '16 at 20:40
  • http://stackoverflow.com/a/29263098/2303865 – Leo Dabus Jul 13 '16 at 20:44
  • Yeah, I found that exact same thread earlier. I'm doing basically the same thing with NSBitmapImageRep(data: nsImage.TIFFRepresentation!)!.representationUsingType(.NSPNGFileType, properties: [:])!.writeToFile("~/Desktop/image.png", atomically: true) – Lukas Valine Jul 13 '16 at 20:47
  • Another strange thing is that after I call "nsimage.TIFFRepresentation" any NSImageView using nsimage suddenly turns black, despite nsimage.TIFFRepresentation being read only. – Lukas Valine Jul 13 '16 at 20:57
  • This latter problem may be result of deeper memory management issue. You're calling `CGDataProviderCreateWithData`, but you're not providing an function to release the memory. If you `malloc`'ed it, you'd just leak, but it's worse in this case. When `pixels` falls out of scope, the memory previously associated with it is now available for other purposes even though the `CGDataProvider` is referencing it. I prefer to call `CGBitmapContextCreate` with `nil` for the data parameter and get the system generated buffer with `CGBitmapContextGetData`. It gets you out of the memory management weeds. – Rob Jul 13 '16 at 21:16
  • Interesting. So I create a CGContext with no data, and then pass it into CGBitmapContextGetData and that returns an unsafeMutablePointer. What do I do with that? – Lukas Valine Jul 13 '16 at 21:34
  • With `CGBitmapContextCreate`, "In iOS 4.0 and later, and OS X v10.6 and later, you can pass `NULL` [for the `data` parameter] if you want Quartz to allocate memory for the bitmap. This frees you from managing your own memory, which reduces memory leak issues." Then `CGBitmapContextGetData` can be used to get access to that buffer so you can fill the pixels as you so choose. But you don't have to manage this memory. The OS will. – Rob Jul 13 '16 at 22:36
  • By the way, rather than `writeToFile(atomically:)`, I'd generally suggest `writeToFile(options:) throws`, so if there was an error writing to the file (e.g. maybe this is sandboxed app), you'll be notified what the issue was. – Rob Jul 15 '16 at 10:33

0 Answers0