CGImage from byte array
Asked Answered
H

3

19

Loading a CGImage or NSImage from a file using a standard image format (jpeg, gif, png et.) is all very simple.

However, I now need to create a CGImage from an array in bytes in memory generated using libfreetype. Its really easy to create OpenGL textures from an array of formatted bytes, and I can see how to create a CGBitmapContext to write to. But I can't seem to find an easy way to create a CGImage from a raw pixel array.

Headland answered 14/2, 2010 at 13:25 Comment(0)
B
8

You can create a CGDataProvider, and let CG request the necessary data from your provider, instead of writing to an image buffer.

Here's a very simple example that generates a black CGImage of size 64x64.

CGDataProviderSequentialCallbacks callbacks;
callbacks.getBytes = getBytes;

CGDataProviderRef provider = CGDataProviderCreateSequential(NULL, &callbacks);
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGImageRef img = CGImageCreate(64,                         // width
                               64,                         // height
                               8,                          // bitsPerComponent
                               24,                         // bitsPerPixel
                               64*3,                       // bytesPerRow
                               space,                      // colorspace
                               kCGBitmapByteOrderDefault,  // bitmapInfo
                               provider,                   // CGDataProvider
                               NULL,                       // decode array
                               NO,                         // shouldInterpolate
                               kCGRenderingIntentDefault); // intent

CGColorSpaceRelease(space);
CGDataProviderRelease(provider);

// use the created CGImage

CGImageRelease(img);

and getBytes is defined like this:

size_t getBytes(void *info, void *buffer, size_t count) {
    memset(buffer, 0x00, count);
    return count;
}

of course, you will want to implement the other callbacks (skipForward, rewind, releaseInfo), and use a proper structure or object for info.

For more information, check out the CGImage and CGDataProvider references.

Blocking answered 14/2, 2010 at 14:13 Comment(2)
ha. the correct function was right under my nose. It looks quite simple to wrap my data in a CFData object, and then CGDataProviderCreateWithCFData.Headland
@ChrisBecke - Can you give me more info? I'm having similar issue.Turki
J
13

Using CFData can make the above code simpler. Here is the code.

// raw pixel data memory of 64 * 64 pixel size

UInt8 pixelData[64 * 64 * 3];

// fill the raw pixel buffer with arbitrary gray color for test

for(size_t ui = 0; ui < 64 * 64 * 3; ui++)
  pixelData[ui] = 210;

CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();

CFDataRef rgbData = CFDataCreate(NULL, pixelData, 64 * 64 * 3);

CGDataProviderRef provider = CGDataProviderCreateWithCFData(rgbData);

CGImageRef rgbImageRef = CGImageCreate(64, 64, 8, 24, 64 * 3, colorspace, kCGBitmapByteOrderDefault, provider, NULL, true, kCGRenderingIntentDefault); 

CFRelease(rgbData);

CGDataProviderRelease(provider);

CGColorSpaceRelease(colorspace);

// use the created CGImage

CGImageRelease(rgbImageRef);
Jasmin answered 26/9, 2010 at 13:13 Comment(3)
The thing with the core foundation APIs - CFData etc. is that they look like they are designed to carry around ref counted instances of immutable data. But in practice they do seem to create new allocations and memcpy a lot.Headland
Why is colorspace 64 * 3 in the CGImageCreate call? According to Apple docs "colorspace = the destination color space. The number of components in this color space must be the same as the number in the specified image." How are you determining the number of components for the color space?Predigest
How might I be able to make the 64s variable?Rondi
O
13

Old question, but still useful! Swift 4 equivalent of chansuk park's answer:

let height = 64
let width = 64
let numComponents = 3
let numBytes = height * width * numComponents
let pixelData = [UInt8](repeating: 210, count: numBytes)
let colorspace = CGColorSpaceCreateDeviceRGB()
let rgbData = CFDataCreate(nil, pixelData, numBytes)!
let provider = CGDataProvider(data: rgbData)!
let rgbImageRef = CGImage(width: width,
                          height: height,
                          bitsPerComponent: 8,
                          bitsPerPixel: 8 * numComponents,
                          bytesPerRow: width * numComponents,
                          space: colorspace,
                          bitmapInfo: CGBitmapInfo(rawValue: 0),
                          provider: provider,
                          decode: nil,
                          shouldInterpolate: true,
                          intent: CGColorRenderingIntent.defaultIntent)!
// Do something with rgbImageRef, or for UIImage:
let outputImage = UIImage(cgImage: rgbImageRef)
Oscular answered 11/3, 2018 at 0:19 Comment(1)
you don't need to release things in swift 4?Dews
B
8

You can create a CGDataProvider, and let CG request the necessary data from your provider, instead of writing to an image buffer.

Here's a very simple example that generates a black CGImage of size 64x64.

CGDataProviderSequentialCallbacks callbacks;
callbacks.getBytes = getBytes;

CGDataProviderRef provider = CGDataProviderCreateSequential(NULL, &callbacks);
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGImageRef img = CGImageCreate(64,                         // width
                               64,                         // height
                               8,                          // bitsPerComponent
                               24,                         // bitsPerPixel
                               64*3,                       // bytesPerRow
                               space,                      // colorspace
                               kCGBitmapByteOrderDefault,  // bitmapInfo
                               provider,                   // CGDataProvider
                               NULL,                       // decode array
                               NO,                         // shouldInterpolate
                               kCGRenderingIntentDefault); // intent

CGColorSpaceRelease(space);
CGDataProviderRelease(provider);

// use the created CGImage

CGImageRelease(img);

and getBytes is defined like this:

size_t getBytes(void *info, void *buffer, size_t count) {
    memset(buffer, 0x00, count);
    return count;
}

of course, you will want to implement the other callbacks (skipForward, rewind, releaseInfo), and use a proper structure or object for info.

For more information, check out the CGImage and CGDataProvider references.

Blocking answered 14/2, 2010 at 14:13 Comment(2)
ha. the correct function was right under my nose. It looks quite simple to wrap my data in a CFData object, and then CGDataProviderCreateWithCFData.Headland
@ChrisBecke - Can you give me more info? I'm having similar issue.Turki

© 2022 - 2024 — McMap. All rights reserved.