kivy/kivy — pull request #2186 by matham (2014-05-13 00:18:26+00:00)
Title: Use memoryviews for blit_buffer

This takes what the `graphics-cython-buffer` (#2133) was trying to do and solves it in a different way.

* That branch created a memory class to allow in-place editing of memory. Upon further thought I think that is unneeded because we have enough options without having to create such a class ourselves. We can use python or cython arrays for this. Furthermore, by accepting generic memoryviews, we could also accept numpy arrays with not additional changes.
* What this pr does, is simply change blit_buffer to accept any class which implements the python buffer interface, including memoryviews. With that, we can achieve identical functionality as the `graphics-cython-buffer` branch. Mainly, it allows creating a cython/python.numpy array, blit it, edit a few elements, and blit it again, all without having to create any additional copies because blit_buffer uses the underlying memory of the memoryview.
* As @tito suggested, I also moved the buffer conversion function to its own file called `img_tools.pxi`. Further, the buffer conversion function instead of returning a bytes object, creates and returns a python array, which significantly improves performance when we need to do e.g. bgr->rgb conversion.

Following are some blit stats with master/this branch (note, to test bgr->rgb, you have to make sure your opengl doesn't support bgr, otherwise it won't convert it. I disabled it directly when testing in opengl_utils.pyx)::

```
import timeit
from functools import partial
from array import array
from kivy.base import EventLoop
EventLoop.ensure_window()
from kivy.graphics.texture import Texture

texture = Texture.create(size=(128, 128))
size = 128 * 128 * 3
buf = [int(x * 255 / size) for x in range(size)]
buf = b''.join(map(chr, buf))
array_buf = array('b', buf)
t1 = timeit.Timer(partial(texture.blit_buffer, buf, colorfmt='rgb',
bufferfmt='ubyte')).repeat(1, 10000)
t2 = timeit.Timer(partial(texture.blit_buffer, array_buf, colorfmt='rgb',
bufferfmt='ubyte')).repeat(1, 10000)
t3 = timeit.Timer(partial(texture.blit_buffer, buf, colorfmt='bgr',
bufferfmt='ubyte')).repeat(1, 10000)
t4 = timeit.Timer(partial(texture.blit_buffer, array_buf, colorfmt='bgr',
bufferfmt='ubyte')).repeat(1, 10000)
print 'blit rgb; bytes, array: {:.4f}, {:.4f}'.format(t1[0], t2[0])
print 'blit bgr; bytes, array: {:.4f}, {:.4f}'.format(t3[0], t4[0])
```

Printed:

blit rgb; bytes, array: 0.5547, 0.5592
blit bgr; bytes, array: 1.1083, 1.2903

on my branch, and after disabling the array test because master doesn't support bliting none-bytes data it printed:

blit rgb; bytes, array: 0.5582, 0.0000
blit bgr; bytes, array: 6.1036, 0.0000

on master.