Logo
Section 1 / 4

存储图像数据

了解 Pycairo 如何存储图像数据。

导入

存储图像数据

了解 Pycairo 如何存储图像数据。

Pycairo 可以与多个其他 Python 库集成,以扩展其功能。本节将介绍一些示例。

Pycairo 如何存储图像数据?

几种传输图像数据的技术使用了 ImageSurface 方法:create_for_data 和 get_data。

create_for_data 接受一个可写缓冲区或 memoryview 对象,其中包含图像数据。它还需要指定格式(如 cairo.Format.RGB24 格式)以及图像的 width 和 height。

1surface = cairo.ImageSurface.create_for_data(data, format, width, height, [stride])

这会创建一个新的 ImageSurface,其中包含 data 中的图像。stride 是一个可选整数,表示数据缓冲区中每行起始位置之间的字节数。可以忽略该参数,函数会根据图像宽度自动计算其值。

get_data 函数返回一个 memoryview 对象,其中包含来自 ImageSurface 的图像数据:

1data = surface.get_data()

此处的 surface 是一个已存在的 ImageSurface。

这是一个实验环境,在这里我们创建了一个 surface,并在其上绘制了一个红色矩形。我们将调用 get_data 函数,然后调用 create_for_data 函数,以不同格式(RGB24)创建一个新的表面 surface2。

1import cairo 2 3surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 600, 600) 4ctx = cairo.Context(surface) 5ctx.set_source_rgb(1, 1, 1) 6ctx.paint() 7 8ctx.rectangle(100, 100, 400, 400) 9ctx.set_source_rgb(1, 0, 0) 10ctx.fill() 11 12data = surface.get_data() 13 14surface2 = cairo.ImageSurface.create_for_data(data, cairo.FORMAT_RGB24, 600, 600) 15 16surface2.write_to_png('output/surface2.png')

数据格式

彩色图像本质上是一个二维数组。下图展示了一张宽为 4 像素、高为 3 像素的图像。

数据由 3 行组成,如下图所示。每一行包含 4 个像素。图中的像素按 扫描线顺序 编号。根据此顺序,第一行的所有像素从左到右排列,接着是第二行,也从左到右排列,依此类推。这种顺序如下所示:

计算机内存是一维的。当 Pycairo 将图像数据存储在数据缓冲区中时,它会以扫描线顺序将数据作为值列表存储,如下所示:

RGB24 数据

在 RGB24 数据中,每个颜色都是一个 8 位整数,如“颜色”一课所述。Pycairo 将这三个颜色存储为 32 位整数,格式为 0x00RRGGBB,其中 RR 是 8 位红色值,依此类推。最高有效字节未使用,下一个字节保存红色值,再下一个字节保存绿色值,最低有效字节保存蓝色值。

因此,从 ImageSurface.get_data 获取的数据应被视为 32 位整数值的数组。要从 32 位整数 c 中提取 RGB 值,请使用以下公式:

1red = (c>>16)&0xFF 2green = (c>>8)&0xFF 3blue = c&0xFF

要从红色、绿色和蓝色值创建一个 32 位 RGB 值,请使用:

1c = ((red&0xFF)<<16) + ((green&0xFF)<<8) + (blue&0xFF)

让我们看一个 0xff00ff(紫色)的例子:

1# 从 32 位值中提取红色、绿色和蓝色 2c = 0xff00ff 3 4red = (c>>16)&0xFF 5green = (c>>8)&0xFF 6blue = c&0xFF 7 8print("Red:", red) 9print("Green:", green) 10print("Blue:", blue) 11 12# 创建 32 位 RGB 值 13c = ((red&0xFF)<<16) + ((green&0xFF)<<8) + (blue&0xFF) 14print("32-bit RGB int value:", c) 15print("32-bit RGB hex value:", hex(c))

以字节形式访问数据

另一种访问数据的方法是将缓冲区视为字节值数组。在这种情况下,每个 32 位整数可以被拆分为 4 个独立的字节,分别是零字节、红色字节、绿色字节和蓝色字节。数据本身没有变化,只是解释方式不同。

使用这种解释时需要注意的一点是,不同平台以不同的方式存储 32 位整数。它们使用不同的字节序。两种选项是 小端序 和 大端序。

如果平台是小端序,32 位整数在内存中将以最低有效字节优先的方式存储。根据这一规则,0x00RRGGBB 将被存储为 4 个字节:0xBB、0xGG、0xRR 和 0x00。例如,Windows PC 上使用的 Intel 处理器就是小端序。

如果平台是大端序,32 位整数在内存中将以最高有效字节优先的方式存储。这意味着值 0x00RRGGBB 将被存储为 4 个字节:0x00、0xRR、0xGG 和 0xBB。

请注意,以 32 位整数读取值时,所有平台的结果都相同,因为处理器会自动根据自身的字节序读取数据。然而,如果我们选择以字节形式访问数据,则必须考虑字节序,以确保代码在不同平台之间具有可移植性。在运行时,我们可以轻松通过以下代码确定当前平台的字节序:

1import sys 2order = sys.byteorder

order 字符串的值为 “big” 或 “little”。

ARGB32 数据

ARGB32 数据与 RGB24 数据非常相似。区别在于 32 位字的高 8 位包含透明度值。以下是相应的转换:

1alpha = (c>>24)&0xFF 2red = (c>>16)&0xFF 3green = (c>>8)&0xFF 4blue = c&0xFF

以及

1c = ((alpha&0xFF)<<24) + ((red&0xFF)<<16) + ((green&0xFF)<<8) + (blue&0xFF)

关于 ARGB32 数据的另一点是,它使用预乘透明度。这意味着红色、绿色和蓝色值在存储前已与透明度值相乘。例如,假设有一个深橙色 0xC08020,其透明度值为 0x80(即透明度为 0.5),则会存储为 0x80604010。因为红色、绿色和蓝色值都会乘以透明度值 0.5。

让我们看一个 0x00ff00ff(透明紫色)的例子:

1# 从 32 位值中提取红色、绿色和蓝色 2c = 0x00ff00ff 3 4alpha = (c>>24)&0xFF 5red = (c>>16)&0xFF 6green = (c>>8)&0xFF 7blue = c&0xFF 8 9print("Alpha:", alpha) 10print("Red:", red) 11print("Green:", green) 12print("Blue:", blue) 13 14# 创建 32 位 RGB 值 15c = ((alpha&0xFF)<<24) + ((red&0xFF)<<16) + ((green&0xFF)<<8) + (blue&0xFF) 16print("32-bit RGB int value:", c) 17print("32-bit RGB hex value:", hex(c))

其他格式

其他颜色格式使用较少。

  • RGB30 将 RGB 数据存储为 32 位整数,但每个颜色使用 10 位而非 8 位。
  • RGB16_565 将 RGB 数据存储在 16 位字中。它使用最高 5 位表示红色,接下来的 6 位表示绿色,最低 5 位表示蓝色。
  • A8 将透明度数据存储在 8 位字中。
  • A1 使用 1 位透明度数据,意味着每个像素要么完全透明,要么完全不透明。它在一个 32 位字中存储 32 个像素值。位序取决于处理器的字节序。对于大端序平台,像素从最高有效位向下存储;对于小端序平台,像素从最低有效位向上存储。使用 A1 格式时,解码图像数据时应考虑字节序。

表面和输出格式

PIL (Pillow) 集成