Search code examples
pythonpython-imaging-librarycairo

Convert PIL Image to Cairo ImageSurface


I'm trying to create a cairo ImageSurface from a PIL image, the code I have so far is:

im = Image.open(filename)
imstr = im.tostring()
a = array.array('B', imstr)
height, width = im.size
stride = cairo.ImageSurface.format_stride_for_width(cairo.FORMAT_RGB24, width)
return cairo.ImageSurface.create_for_data(a, cairo.FORMAT_ARGB24, width, height, stride)

But this is giving me

TypeError: buffer is not long enough.

I don't really understand why this is, perhaps I don't understand image formats well enough.

I'm using cairo 1.10.


Solution

  • Cairo's create_for_data() is wants a writeable buffer object (a string can be used as a buffer object, but it's not writable), and it only supports 32 bits per pixel data (RGBA, or RGB followed by one unused byte). PIL, on the other hand, provides a 24bpp RGB read-only buffer object.

    I suggest you tell PIL to add an alpha channel, then convert the PIL buffer to a numpy array to get a writable buffer for Cairo.

    im = Image.open(filename)
    im.putalpha(256) # create alpha channel
    arr = numpy.array(im)
    height, width, channels = arr.shape
    surface = cairo.ImageSurface.create_for_data(arr, cairo.FORMAT_RGB24, width, height)