Make blit_buffer accept memoryviews and objects that implement the buffer interface in additon to bytes. Use new conversion function.

This commit is contained in:
Matthew Einhorn 2014-05-12 01:20:57 -04:00
parent 1565c28cc2
commit 801e1ee6ce
2 changed files with 61 additions and 14 deletions

View File

@ -42,15 +42,17 @@ Blitting custom data
--------------------
You can create your own data and blit it to the texture using
:meth:`Texture.blit_data`::
:meth:`Texture.blit_buffer`.
For example, to blit immutable bytes data::
# create a 64x64 texture, defaults to rgb / ubyte
texture = Texture.create(size=(64, 64))
# create 64x64 rgb tab, and fill with value from 0 to 255
# create 64x64 rgb tab, and fill with values from 0 to 255
# we'll have a gradient from black to white
size = 64 * 64 * 3
buf = [int(x * 255 / size) for x in xrange(size)]
buf = [int(x * 255 / size) for x in range(size)]
# then, convert the array to a ubyte string
buf = b''.join(map(chr, buf))
@ -63,6 +65,34 @@ You can create your own data and blit it to the texture using
with self.canvas:
Rectangle(texture=texture, pos=self.pos, size=(64, 64))
Since 1.8.1, you can blit data stored in a instance that implements the python
buffer interface, or a memoryview thereof, such as numpy arrays, python
`array.array`, a `bytearray`, or a cython array. This is beneficial if you
expect to blit similar data, with perhaps a few changes in the data.
When using a bytes representation of the data, for every change you have to
regenerate the bytes instance, from perhaps a list, which is very inefficient.
When using a buffer object, you can simply edit parts of the original data.
Similarly, unless starting with a bytes object, converting to bytes requires a
full copy, however, when using a buffer instance, no memory is copied, except
to upload it to the GPU.
Continuing with the example above::
from array import array
size = 64 * 64 * 3
buf = [int(x * 255 / size) for x in range(size)]
# initialize the array with the buffer values
arr = array('B', buf)
# now blit the array
texture.blit_buffer(arr, colorfmt='rgb', bufferfmt='ubyte')
# now change some elements in the original array
arr[24] = arr[50] = 99
# blit again the buffer
texture.blit_buffer(arr, colorfmt='rgb', bufferfmt='ubyte')
BGR/BGRA support
----------------
@ -194,7 +224,6 @@ include "common.pxi"
include "opengl_utils_def.pxi"
include "img_tools.pxi"
from array import array
from kivy.weakmethod import WeakMethod
from kivy.graphics.context cimport get_context
@ -802,8 +831,15 @@ cdef class Texture:
called in order to update the texture.
:Parameters:
`pbuffer` : bytes
Image data.
`pbuffer` : bytes, or a class that implements the buffer interface\
(including memoryview).
A buffer containing the image data. It can be either a bytes
object or a instance of a class that implements the python
buffer interface, e.g. `array.array`, `bytearray`, numpy arrays
etc. If it's not a bytes object, the underlying buffer must
be contiguous, have only one dimension and must not be
readonly, even though the data is not modified, due to a cython
limitation. See module description for usage details.
`size` : tuple, defaults to texture size
Size of the image (width, height)
`colorfmt` : str, defaults to 'rgb'
@ -820,9 +856,12 @@ cdef class Texture:
Indicate if we need to regenerate the mipmap from level 0.
.. versionadded:: 1.0.7
added `mipmap_level` and `mipmap_generation`
.. versionchanged:: 1.8.1
`pbuffer` can now be any class instance that implements the python
buffer interface and / or memoryviews thereof.
'''
cdef GLuint target = self._target
if colorfmt is None:
@ -839,20 +878,27 @@ cdef class Texture:
# time.
self.bind()
# need conversion ?
cdef bytes data
data = pbuffer
data, colorfmt = _convert_buffer(data, colorfmt)
# need conversion, do check here because it seems to be faster ?
if not gl_has_texture_native_format(colorfmt):
pbuffer, colorfmt = convert_to_gl_format(pbuffer, colorfmt)
cdef char [:] view
cdef char *cdata
cdef long datasize
if isinstance(pbuffer, bytes): # if it's bytes, just use memory
cdata = <bytes>pbuffer # explicit bytes
datasize = len(pbuffer)
else: # if it's a memoryview or buffer type, use start of memory
view = pbuffer
cdata = &view[0]
datasize = view.nbytes
# prepare nogil
cdef int iglfmt = _color_fmt_to_gl(self._icolorfmt)
cdef int glfmt = _color_fmt_to_gl(colorfmt)
cdef long datasize = len(pbuffer)
cdef int x = pos[0]
cdef int y = pos[1]
cdef int w = size[0]
cdef int h = size[1]
cdef char *cdata = <char *>data
cdef int glbufferfmt = bufferfmt
cdef int is_allocated = self._is_allocated
cdef int is_compressed = _is_compressed_fmt(colorfmt)

View File

@ -428,7 +428,8 @@ graphics_dependencies = {
'texture.pxd': ['c_opengl.pxd'],
'texture.pyx': [
'config.pxi', 'common.pxi', 'opengl_utils_def.pxi', 'context.pxd',
'c_opengl.pxd', 'c_opengl_debug.pxd', 'opengl_utils.pxd'],
'c_opengl.pxd', 'c_opengl_debug.pxd', 'opengl_utils.pxd',
'img_tools.pxi'],
'vbo.pxd': ['buffer.pxd', 'c_opengl.pxd', 'vertex.pxd'],
'vbo.pyx': [
'config.pxi', 'common.pxi', 'c_opengl_debug.pxd', 'context.pxd',