Search code examples
pythontextureskivyblit

Animate texture in Kivy using blit_buffer and pos parameter


I have some image data that I'm blitting to a texture and displaying in Kivy. The image data is the same width, with a greater height than the widget. After the texture is created, I want to animate the y position of it for a scrolling effect.

Previously, I had been blitting the entire buffer to the texture, and animating the position of the widget itself. However, the buffer data is occasionally larger than the maximum supported texture size of my GPU, and nothing displays on the screen.

I figured a better approach would be to only blit the section of the source buffer I need, and leave the texture the same size as the widget.

After looking at the documentation for Kivy, I discovered there's a pos parameter. I figured I could use an animated property for the document ypos in the call to blit_buffer, but when I tried it, nothing displayed again.

I switched the pos parameter to (0, 100) to see if my usage of the parameter did what I expected, and it still didn't display. Switching it to (0, 0) works as expected.

Am I using blit_buffer() correctly? What's the best way to blit only a portion of an image data buffer?

EDIT: I recreated this issue with a standalone script:

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.core.image import Image
from kivy.graphics.texture import Texture
from kivy.graphics import Rectangle

import array

class MyWidget(Widget):
    def __init__(self, **kwargs):
        super(MyWidget, self).__init__(**kwargs)

        texture = Texture.create(size=(512, 512))
        buf = [int(x * 255 / (512*512*3)) for x in range(512*512*3)]
        buf = array.array('B', buf).tostring()

        texture.blit_buffer(
            buf,
            colorfmt='rgb',
            bufferfmt='ubyte',
            pos=(0, 100)
        )

        with self.canvas:
            Rectangle(texture=texture, pos=self.pos, size=(512, 512))

class MyApp(App):
    def build(self):
        return MyWidget()

if __name__ == '__main__':
    MyApp().run()

Solution

  • It seems the issue was that the pos argument of Texture.blit_buffer() specifies the destination coordinates of the image data, rather than the source coordinates.

    I ended up solving my problem by using PIL to crop the image data, then creating a tiling renderer that contains multiple textures each tile is blit to.