我正在尝试从PIL图像创建一个cairo ImageSurface,到目前为止我的代码是:
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)
但是这给了我
TypeError: buffer is not long enough.
我真的不明白为什么会这样,也许我对图像格式不太了解。
我正在使用cairo 1.10。
答案 0 :(得分:5)
Cairo的create_for_data()需要一个可写的缓冲区对象(一个字符串可以用作缓冲区对象,但它不可写),它只支持每像素32位数据(RGBA或RGB后跟一个未使用的字节) 。另一方面,PIL提供24bpp RGB只读缓冲区对象。
我建议你告诉PIL添加一个alpha通道,然后将PIL缓冲区转换为numpy数组,以获得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)
答案 1 :(得分:5)
如果符合以下条件,则接受的版本无效:
在cairo图像中,颜色的值乘以alpha的值预乘,并使用本机CPU字节序将它们存储为32位字。这意味着PIL图像:
r1 g1 b1 a1 r2 g2 b2 a2 ...
存储在cairo中的一个小端CPU中:
b1*a1 g1*a1 r1*a1 a1 b2*a2 g2*a2 r2*a2 a2 ...
并在大端CPU中:
a1 r1*a1 b1*a1 g1*a1 a2 r2*a2 g2*a2 b2*a2 ...
这是一个在没有NumPy依赖的小端机器上正常工作的版本:
def pil2cairo(im):
"""Transform a PIL Image into a Cairo ImageSurface."""
assert sys.byteorder == 'little', 'We don\'t support big endian'
if im.mode != 'RGBA':
im = im.convert('RGBA')
s = im.tostring('raw', 'BGRA')
a = array.array('B', s)
dest = cairo.ImageSurface(cairo.FORMAT_ARGB32, im.size[0], im.size[1])
ctx = cairo.Context(dest)
non_premult_src_wo_alpha = cairo.ImageSurface.create_for_data(
a, cairo.FORMAT_RGB24, im.size[0], im.size[1])
non_premult_src_alpha = cairo.ImageSurface.create_for_data(
a, cairo.FORMAT_ARGB32, im.size[0], im.size[1])
ctx.set_source_surface(non_premult_src_wo_alpha)
ctx.mask_surface(non_premult_src_alpha)
return dest
这里我用cairo进行预乘。我也试过用NumPy进行预乘,但结果却比较慢。此功能在我的电脑(Mac OS X,2.13GHz Intel Core 2 Duo)~1s内转换为6000x6000像素的图像,以及5ms转换500x500像素的图像。