File: _offscreen_util.py

package info (click to toggle)
python-vispy 0.16.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 9,112 kB
  • sloc: python: 61,648; javascript: 6,800; ansic: 2,104; makefile: 141; sh: 6
file content (121 lines) | stat: -rw-r--r-- 3,697 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
"""
Utils for offscreen rendering.
"""

from .. import Application, Canvas
from ... import gloo


class OffscreenContext:
    """ A helper class to provide an OpenGL context. This context is global
    to the application.
    """

    _global_instance = None
    _canvas = None

    @classmethod
    def get_global_instance(cls):
        """ Get a global context. Note that any assumptions about OpenGL state
        being local will not hold.
        """
        if cls._global_instance is None:
            cls._global_instance = cls()
        return cls._global_instance

    def __init__(self):
        if self._canvas is not None:
            return  # already initialized

        self._is_closed = False

        # Glfw is probably the most lightweight approach, so let's try that.
        # But there are two incompatible packages providing glfw :/
        self.glfw = None
        try:
            import glfw
        except ImportError:
            pass
        else:
            need_from_glfw = ["create_window", "make_context_current"]
            if all(hasattr(glfw, attr) for attr in need_from_glfw):
                self.glfw = glfw

        if self.glfw:
            self.glfw.init()
            self.glfw.window_hint(self.glfw.VISIBLE, 0)
            self._canvas = self.glfw.create_window(1, 1, "dummy window", None, None)
        else:
            try:
                _app = Application('default')
            except Exception:
                raise RuntimeError(
                    "Cannot find a backend to create an OpenGL context. "
                    "Install e.g. PyQt5, PySide2, or `pip install glfw`."
                )
            self._canvas = Canvas(app=_app)
            self._canvas.show(False)

    def make_current(self):
        """ Make this the currently active context.
        """
        # If an application only used off-screen canvases this would technically
        # have to be called just once. But note that an application/session
        # could run both real canvases and off-screen ones.
        if self.glfw:
            self.glfw.make_context_current(self._canvas)
        else:
            self._canvas.set_current()

    def close(self):
        """ Close the context. """
        # Cannot close the global instance
        if self is OffscreenContext._global_instance:
            return
        elif not self._is_closed:
            self._is_closed = True
            if self.glfw:
                self.glfw.destroy_window(self._canvas)
            else:
                self._canvas.close()

    def __del__(self):
        self.close()


class FrameBufferHelper:
    """ Provides a canvas to render to, using an FBO.
    """

    def __init__(self):
        self._fbo = None
        self._physical_size = 1, 1
        self._fbo_size = -1, -1

    def _ensure_fbo(self):
        if self._fbo_size != self._physical_size:
            self._fbo_size = self._physical_size
            w, h = self._fbo_size
            if self._fbo is None:
                color_buffer = gloo.Texture2D((h, w, 4))
                depth_buffer = gloo.RenderBuffer((h, w))
                self._fbo = gloo.FrameBuffer(color_buffer, depth_buffer)
            else:
                self._fbo.resize((h, w))

    def set_physical_size(self, w, h):
        """ Set the physical size of the canvas.
        """
        self._physical_size = w, h

    def get_frame(self):
        """ Call this within the with-context to obtain the frame buffer contents.
        """
        return self._fbo.read()

    def __enter__(self):
        self._ensure_fbo()
        return self._fbo.__enter__()

    def __exit__(self, *args):
        return self._fbo.__exit__(*args)