Accessing or providing pixel data

The ImageData class represents an image as a string or sequence of pixel data, or as a ctypes pointer. Details such as the pitch and component layout are also stored in the class. You can access an ImageData object for any image with get_image_data:

kitten = pyglet.image.load('kitten.png').get_image_data()

The design of ImageData is to allow applications to access the detail in the format they prefer, rather than having to understand the many formats that each operating system and OpenGL make use of.

The pitch and format properties determine how the bytes are arranged. pitch gives the number of bytes between each consecutive row. The data is assumed to run from left-to-right, bottom-to-top, unless pitch is negative, in which case it runs from left-to-right, top-to-bottom. There is no need for rows to be tightly packed; larger pitch values are often used to align each row to machine word boundaries.

The format property gives the number and order of color components. It is a string of one or more of the letters corresponding to the components in the following table:

R Red
G Green
B Blue
A Alpha
L Luminance
I Intensity

For example, a format string of "RGBA" corresponds to four bytes of colour data, in the order red, green, blue, alpha. Note that machine endianness has no impact on the interpretation of a format string.

The length of a format string always gives the number of bytes per pixel. So, the minimum absolute pitch for a given image is len(kitten.format) * kitten.width.

To retrieve pixel data in a particular format, use the get_data method, specifying the desired format and pitch. The following example reads tightly packed rows in RGB format (the alpha component, if any, will be discarded):

kitten = kitten.get_image_data()
data = kitten.get_data('RGB', kitten.width * 3)

data always returns a string, however it can be set to a ctypes array, stdlib array, list of byte data, string, or ctypes pointer. To set the image data use set_data, again specifying the format and pitch:

kitten.set_data('RGB', kitten.width * 3, data)

You can also create ImageData directly, by providing each of these attributes to the constructor. This is any easy way to load textures into OpenGL from other programs or libraries.

Performance concerns

pyglet can use several methods to transform pixel data from one format to another. It will always try to select the most efficient means. For example, when providing texture data to OpenGL, the following possibilities are examined in order:

  1. Can the data be provided directly using a built-in OpenGL pixel format such as GL_RGB or GL_RGBA?
  2. Is there an extension present that handles this pixel format?
  3. Can the data be transformed with a single regular expression?
  4. If none of the above are possible, the image will be split into separate scanlines and a regular expression replacement done on each; then the lines will be joined together again.

The following table shows which image formats can be used directly with steps 1 and 2 above, as long as the image rows are tightly packed (that is, the pitch is equal to the width times the number of components).

Format Required extensions
"ARGB" GL_EXT_bgra and GL_APPLE_packed_pixels
"ABGR" GL_EXT_abgr
"BGR" GL_EXT_bgra
"BGRA" GL_EXT_bgra

If the image data is not in one of these formats, a regular expression will be constructed to pull it into one. If the rows are not tightly packed, or if the image is ordered from top-to-bottom, the rows will be split before the regular expression is applied. Each of these may incur a performance penalty -- you should avoid such formats for real-time texture updates if possible.