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.

Chris Becke
  • 34,244
  • 12
  • 79
  • 148

3 Answers3

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);
Defragged
  • 1,914
  • 15
  • 15
chansuk park
  • 131
  • 1
  • 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. – Chris Becke Sep 26 '10 at 16:46
  • 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? – Praxiteles Dec 12 '12 at 07:51
  • How might I be able to make the 64s variable? – esreli Jul 16 '14 at 23:59
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)
Lou Zell
  • 5,255
  • 3
  • 28
  • 23
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.

Can Berk Güder
  • 109,922
  • 25
  • 130
  • 137
  • 1
    ha. the correct function was right under my nose. It looks quite simple to wrap my data in a CFData object, and then CGDataProviderCreateWithCFData. – Chris Becke Feb 14 '10 at 16:14
  • @ChrisBecke - Can you give me more info? I'm having similar issue. – Patricia Jul 03 '14 at 22:37