Example #1
0
    def initializeGL(self, gls: 'GLShared', width: int, height: int) -> None:
        self.__width = width
        self.__height = height

        # Initialize (but don't fill) the Color LUT
        self.__texture_colors = Texture(debug_name="Layer Color LUT")
        with self.__texture_colors.on(GL.GL_TEXTURE_1D):
            GL.glTexParameteri(GL.GL_TEXTURE_1D, GL.GL_TEXTURE_MIN_FILTER,
                               GL.GL_NEAREST)
            GL.glTexParameteri(GL.GL_TEXTURE_1D, GL.GL_TEXTURE_MAG_FILTER,
                               GL.GL_NEAREST)
            GL.glTexParameteri(GL.GL_TEXTURE_1D, GL.GL_TEXTURE_WRAP_S,
                               GL.GL_CLAMP_TO_EDGE)
            GL.glTexParameteri(GL.GL_TEXTURE_1D, GL.GL_TEXTURE_WRAP_T,
                               GL.GL_CLAMP_TO_EDGE)

        # Compositing shader and geometry
        self.__composite_shader = gls.shader_cache.get(
            "layer_composite_vert",
            "layer_composite_frag",
            fragment_bindings={"final_color": 0})

        ar = self.__get_vbo_data()
        self.__composite_vao = VAO(debug_name="Compositor Quad VAO")
        self.__composite_vbo = VBO(ar, GL.GL_STATIC_DRAW)
        GL.glObjectLabel(GL.GL_BUFFER, int(self.__composite_vbo), -1,
                         "Compositor Quad VBO")

        with self.__composite_vao:
            self.__composite_vbo.bind()
            VBOBind(self.__composite_shader.program, ar.dtype,
                    "vertex").assign()
            VBOBind(self.__composite_shader.program, ar.dtype,
                    "texpos").assign()
Example #2
0
    def initializeGL(self) -> None:
        self.sdf_shader = self.gls.shader_cache.get("image_vert", "tex_frag")

        self.buffer_dtype = numpy.dtype([("vertex", numpy.float32, 2),
                                         ("texpos", numpy.float32, 2)])

        self.b1 = VBOBind(self.sdf_shader.program, self.buffer_dtype, "vertex")
        self.b2 = VBOBind(self.sdf_shader.program, self.buffer_dtype, "texpos")

        self.tex = Texture()
Example #3
0
    def initGL(self, gls: 'GLShared') -> None:
        self._tex = Texture()

        # Setup the basic texture parameters
        with self._tex.on(GL.GL_TEXTURE_2D):
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER,
                               GL.GL_NEAREST)
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER,
                               GL.GL_LINEAR)
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S,
                               GL.GL_CLAMP_TO_EDGE)
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T,
                               GL.GL_CLAMP_TO_EDGE)

            # numpy packs data tightly, whereas the openGL default is 4-byte-aligned
            # fix line alignment to 1 byte so odd-sized textures load right
            GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1)

            # Download the data to the buffer. cv2 stores data in BGR format
            GL.glTexImage2D(
                GL.GL_TEXTURE_2D, 0, GL.GL_RGB, self.im.shape[1],
                self.im.shape[0], 0, GL.GL_BGR, GL.GL_UNSIGNED_BYTE,
                self.im.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)))

        self.prog = gls.shader_cache.get("image_vert", "image_frag")

        ar = numpy.ndarray(
            (4, ),
            dtype=[("vertex", numpy.float32, 2),
                   ("texpos", numpy.float32, 2)])  # type: ignore

        sca = max(self.im.shape[0], self.im.shape[1])
        x = self.im.shape[1] / float(sca)
        y = self.im.shape[0] / float(sca)
        ar["vertex"] = [(-x, -y), (-x, y), (x, -y), (x, y)]
        ar["texpos"] = [(0, 0), (0, 1), (1, 0), (1, 1)]

        self.b1 = VBOBind(self.prog.program, ar.dtype, "vertex")
        self.b2 = VBOBind(self.prog.program, ar.dtype, "texpos")

        self.vbo = VBO(ar, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER)

        self.mat_loc = GL.glGetUniformLocation(self.prog.program, "mat")
        self.tex1_loc = GL.glGetUniformLocation(self.prog.program, "tex1")

        self.vao = VAO()
        with self.vbo, self.vao:
            self.b1.assign()
            self.b2.assign()
Example #4
0
    def initGL(self, gls):
        self._tex = Texture()

        # Setup the basic texture parameters
        with self._tex.on(GL.GL_TEXTURE_2D):
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
            GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_S,GL.GL_CLAMP_TO_EDGE);
            GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_T,GL.GL_CLAMP_TO_EDGE);

            # numpy packs data tightly, whereas the openGL default is 4-byte-aligned
            # fix line alignment to 1 byte so odd-sized textures load right
            GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1)

            # Download the data to the buffer. cv2 stores data in BGR format
            GL.glTexImage2D(GL.GL_TEXTURE_2D,
                0,
                GL.GL_RGB,
                self.im.shape[1],
                self.im.shape[0],
                0,
                GL.GL_BGR,
                GL.GL_UNSIGNED_BYTE,
                self.im.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8))
                )

        self.prog = gls.shader_cache.get("image_vert", "image_frag")

        ar = numpy.ndarray(4, dtype=[
            ("vertex", numpy.float32, 2),
            ("texpos", numpy.float32, 2)
        ])

        sca = max(self.im.shape[0], self.im.shape[1])
        x = self.im.shape[1] / float(sca)
        y = self.im.shape[0] / float(sca)
        ar["vertex"] = [ (-x,-y), (-x, y), (x,-y), (x, y)]
        ar["texpos"] = [ (0,0), (0, 1), (1,0), (1, 1)]

        self.b1 = vbobind(self.prog, ar.dtype, "vertex")
        self.b2 = vbobind(self.prog, ar.dtype,"texpos")

        self.vbo = VBO(ar, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER)

        self.mat_loc = GL.glGetUniformLocation(self.prog, "mat")
        self.tex1_loc = GL.glGetUniformLocation(self.prog, "tex1")


        self.vao = VAO()
        with self.vbo, self.vao:
            self.b1.assign()
            self.b2.assign()
Example #5
0
    def initializeGL(self):
        self.sdf_shader = self.gls.shader_cache.get("image_vert", "tex_frag")

        self.buffer_dtype = numpy.dtype([
            ("vertex", numpy.float32, 2),
            ("texpos", numpy.float32, 2)
        ])

        self.b1 = vbobind(self.sdf_shader, self.buffer_dtype, "vertex")
        self.b2 = vbobind(self.sdf_shader, self.buffer_dtype, "texpos")


        self.tex = Texture()
Example #6
0
    def __setup(self, width: int, height: int) -> None:
        if self.__is_setup:
            self.__teardown()

        self.__is_setup = True

        self.__fbo = GL.glGenFramebuffers(1)
        if self.__debug_name is not None:
            GL.glObjectLabel(GL.GL_FRAMEBUFFER, self.__fbo, -1,
                             self.__debug_name)

        self.__info_tex = Texture(debug_name=self.__debug_name)

        self.__i8_texture(self.__info_tex, GL.GL_RG8UI, GL.GL_RG_INTEGER,
                          width, height)

        with self:

            GL.glFramebufferTexture2D(GL.GL_FRAMEBUFFER,
                                      GL.GL_COLOR_ATTACHMENT0,
                                      GL.GL_TEXTURE_2D, self.__info_tex.v, 0)

            GL.glDrawBuffers([GL.GL_COLOR_ATTACHMENT0])

            status = GL.glCheckFramebufferStatus(GL.GL_FRAMEBUFFER)

            if status != GL.GL_FRAMEBUFFER_COMPLETE:
                lut = [
                    GL.GL_FRAMEBUFFER_UNDEFINED,
                    GL.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
                    GL.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT,
                    GL.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER,
                    GL.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER,
                    GL.GL_FRAMEBUFFER_UNSUPPORTED,
                    GL.GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
                    GL.GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS
                ]

                # Find the magic constant name
                for i in lut:
                    if status == int(i):
                        result = i
                        break
                else:
                    result = "unknown %d" % status

                print("Error, could not create framebuffer. Status: %s" %
                      str(result))
                assert False
Example #7
0
class ImageView:
    def __init__(self, il: 'ImageLayer') -> None:
        """

        :param il:
        :type il: pcbre.model.imagelayer.ImageLayer
        :return:
        """

        self.il = il
        self.im = il.decoded_image
        self.mat = None

    def initGL(self, gls: 'GLShared') -> None:
        self._tex = Texture()

        # Setup the basic texture parameters
        with self._tex.on(GL.GL_TEXTURE_2D):
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER,
                               GL.GL_NEAREST)
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER,
                               GL.GL_LINEAR)
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S,
                               GL.GL_CLAMP_TO_EDGE)
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T,
                               GL.GL_CLAMP_TO_EDGE)

            # numpy packs data tightly, whereas the openGL default is 4-byte-aligned
            # fix line alignment to 1 byte so odd-sized textures load right
            GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1)

            # Download the data to the buffer. cv2 stores data in BGR format
            GL.glTexImage2D(
                GL.GL_TEXTURE_2D, 0, GL.GL_RGB, self.im.shape[1],
                self.im.shape[0], 0, GL.GL_BGR, GL.GL_UNSIGNED_BYTE,
                self.im.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)))

        self.prog = gls.shader_cache.get("image_vert", "image_frag")

        ar = numpy.ndarray(
            (4, ),
            dtype=[("vertex", numpy.float32, 2),
                   ("texpos", numpy.float32, 2)])  # type: ignore

        sca = max(self.im.shape[0], self.im.shape[1])
        x = self.im.shape[1] / float(sca)
        y = self.im.shape[0] / float(sca)
        ar["vertex"] = [(-x, -y), (-x, y), (x, -y), (x, y)]
        ar["texpos"] = [(0, 0), (0, 1), (1, 0), (1, 1)]

        self.b1 = VBOBind(self.prog.program, ar.dtype, "vertex")
        self.b2 = VBOBind(self.prog.program, ar.dtype, "texpos")

        self.vbo = VBO(ar, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER)

        self.mat_loc = GL.glGetUniformLocation(self.prog.program, "mat")
        self.tex1_loc = GL.glGetUniformLocation(self.prog.program, "tex1")

        self.vao = VAO()
        with self.vbo, self.vao:
            self.b1.assign()
            self.b2.assign()

    def render(self, viewPort: 'npt.NDArray[numpy.float64]') -> None:
        m_pre = self.mat
        if self.mat is None:
            m_pre = self.il.transform_matrix
        mat = viewPort.dot(m_pre)

        GL.glActiveTexture(GL.GL_TEXTURE0)
        with self.prog.program, self._tex.on(GL.GL_TEXTURE_2D), self.vao:
            GL.glUniformMatrix3fv(self.mat_loc, 1, True,
                                  mat.astype(numpy.float32))
            GL.glUniform1i(self.tex1_loc, 0)

            GL.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, 4)
Example #8
0
class TextRender:
    def __init__(self, gls: Any, sdf_atlas: SDFTextAtlas) -> None:
        self.gls = gls

        self.sdf_atlas = sdf_atlas

        self.last_glyph_count = 0
        self.__cached_metrics: Dict[str, _StringMetrics] = {}

    def initializeGL(self) -> None:
        self.sdf_shader = self.gls.shader_cache.get("image_vert", "tex_frag")

        self.buffer_dtype = numpy.dtype([("vertex", numpy.float32, 2),
                                         ("texpos", numpy.float32, 2)])

        self.b1 = VBOBind(self.sdf_shader.program, self.buffer_dtype, "vertex")
        self.b2 = VBOBind(self.sdf_shader.program, self.buffer_dtype, "texpos")

        self.tex = Texture()

        # TODO: implement short-int caching
        # build a De-bruijn sequence (shorted substring containing all substrings)
        # self.int_seq = de_bruijn(4)
        # self.int_seq += self.int_seq[:3]

    def getStringMetrics(self, text: str) -> _StringMetrics:
        """
        create an array of coord data for rendering
        :return:
        """

        try:
            return self.__cached_metrics[text]
        except KeyError:
            pass

        # Starting pen X coordinate
        va = VA_tex(1024)

        pen_x = 0

        left, right, top, bottom = 0.0, 0.0, 0.0, 0.0

        for ch in text:
            # Fetch the glyph from the atlas
            gp = self.sdf_atlas.getGlyph(ch)

            # width and height of the rendered quad is proportional to the glpyh size
            margin = self.sdf_atlas.margin
            w = (gp.w + margin * 2)
            h = (gp.h + margin * 2)
            # Calculate the offset to the corner of the character.
            c_off_x = pen_x + gp.l - margin

            # Y position is a bit tricky. the "top" of the glyph is whats specified, but we care about the bottom-left
            # so subtract the height
            c_off_y = gp.t - gp.h - margin

            # Update the bounding rect
            left = min(left, (c_off_x + margin) / BASE_FONT)
            right = max(right, (c_off_x + w - margin) / BASE_FONT)
            bottom = min(bottom, (c_off_y + margin) / BASE_FONT)
            top = max(top, (c_off_y + h - margin) / BASE_FONT)

            # Calculate the draw rect positions
            x0 = (c_off_x) / BASE_FONT
            y0 = (c_off_y) / BASE_FONT
            x1 = (c_off_x + w) / BASE_FONT
            y1 = (c_off_y + h) / BASE_FONT

            # Add two triangles for the rect
            va.add_tex(x0, y0, gp.sx, gp.sy)
            va.add_tex(x0, y1, gp.sx, gp.ty)
            va.add_tex(x1, y0, gp.tx, gp.sy)
            va.add_tex(x1, y0, gp.tx, gp.sy)
            va.add_tex(x0, y1, gp.sx, gp.ty)
            va.add_tex(x1, y1, gp.tx, gp.ty)

            # And increment to the next character
            pen_x += gp.hb

        cm = _StringMetrics(va, (left, right, bottom, top))

        self.__cached_metrics[text] = cm
        return cm

    def updateTexture(self) -> None:
        # Don't update the texture if its up-to-date
        if len(self.sdf_atlas.atlas) == self.last_glyph_count:
            return

        self.last_glyph_count = len(self.sdf_atlas.atlas)

        # Setup the basic texture parameters
        with self.tex.on(GL.GL_TEXTURE_2D):
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER,
                               GL.GL_LINEAR)
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER,
                               GL.GL_LINEAR)

            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S,
                               GL.GL_CLAMP_TO_EDGE)
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T,
                               GL.GL_CLAMP_TO_EDGE)

            # numpy packs data tightly, whereas the openGL default is 4-byte-aligned
            # fix line alignment to 1 byte so odd-sized textures load right
            GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1)

            # Download the data to the buffer.
            GL.glTexImage2D(
                GL.GL_TEXTURE_2D, 0, GL.GL_RED, self.sdf_atlas.image.shape[1],
                self.sdf_atlas.image.shape[0], 0, GL.GL_RED,
                GL.GL_UNSIGNED_BYTE,
                self.sdf_atlas.image.ctypes.data_as(
                    ctypes.POINTER(ctypes.c_uint8)))
Example #9
0
class TextRender(object):
    def __init__(self, gls, sdf_atlas):
        self.gls = gls

        self.sdf_atlas = sdf_atlas

        self.last_glyph_count = 0

    def initializeGL(self):
        self.sdf_shader = self.gls.shader_cache.get("image_vert", "tex_frag")

        self.buffer_dtype = numpy.dtype([("vertex", numpy.float32, 2),
                                         ("texpos", numpy.float32, 2)])

        self.b1 = vbobind(self.sdf_shader, self.buffer_dtype, "vertex")
        self.b2 = vbobind(self.sdf_shader, self.buffer_dtype, "texpos")

        self.tex = Texture()

        # TODO: implement short-int caching
        # build a De-bruijn sequence (shorted substring containing all substrings)
        # self.int_seq = de_bruijn(4)
        # self.int_seq += self.int_seq[:3]

    def getStringMetrics(self, text):
        """
        create an array of coord data for rendering
        :return:
        """

        # In the future, we'll probably want to move to a texture-buffer-object
        # approach for storing glyph metrics, such that all we need to submit is an
        # array of character indicies and X-offsets
        #
        # This would pack into 8 bytes/char quite nicely (4 byte char index, float32 left)
        # With this, streaming text to the GPU would be much more effective

        # Starting pen X coordinate
        q = []
        pen_x = 0

        left, right, top, bottom = 0, 0, 0, 0

        for ch in text:
            # Fetch the glyph from the atlas
            gp = self.sdf_atlas.getGlyph(ch)

            # width and height of the rendered quad is proportional to the glpyh size
            margin = self.sdf_atlas.margin
            w = (gp.w + margin * 2)
            h = (gp.h + margin * 2)
            # Calculate the offset to the corner of the character.
            c_off_x = pen_x + gp.l - margin
            # Y position is a bit tricky. the "top" of the glyph is whats specified, but we care about the bottom-left
            # so subtract the height
            c_off_y = gp.t - gp.h - margin

            left = min(left, (c_off_x + margin) / BASE_FONT)
            right = max(right, (c_off_x + w - margin) / BASE_FONT)
            bottom = min(bottom, (c_off_y + margin) / BASE_FONT)
            top = max(top, (c_off_y + h - margin) / BASE_FONT)

            x0 = (c_off_x) / BASE_FONT
            y0 = (c_off_y) / BASE_FONT
            x1 = (c_off_x + w) / BASE_FONT
            y1 = (c_off_y + h) / BASE_FONT

            q.append(((x0, y0), (gp.sx, gp.sy)))
            q.append(((x0, y1), (gp.sx, gp.ty)))
            q.append(((x1, y0), (gp.tx, gp.sy)))
            q.append(((x1, y0), (gp.tx, gp.sy)))
            q.append(((x0, y1), (gp.sx, gp.ty)))
            q.append(((x1, y1), (gp.tx, gp.ty)))

            # And increment to the next character
            pen_x += gp.hb
        return _StringMetrics(q, (left, right, bottom, top))

    def updateTexture(self):
        # Don't update the texture if its up-to-date
        if len(self.sdf_atlas.atlas) == self.last_glyph_count:
            return

        self.last_glyph_count = len(self.sdf_atlas.atlas)

        # Setup the basic texture parameters
        with self.tex.on(GL.GL_TEXTURE_2D):
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER,
                               GL.GL_LINEAR)
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER,
                               GL.GL_LINEAR)

            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S,
                               GL.GL_CLAMP_TO_EDGE)
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T,
                               GL.GL_CLAMP_TO_EDGE)

            # numpy packs data tightly, whereas the openGL default is 4-byte-aligned
            # fix line alignment to 1 byte so odd-sized textures load right
            GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1)

            # Download the data to the buffer.
            GL.glTexImage2D(
                GL.GL_TEXTURE_2D, 0, GL.GL_RED, self.sdf_atlas.image.shape[1],
                self.sdf_atlas.image.shape[0], 0, GL.GL_RED,
                GL.GL_UNSIGNED_BYTE,
                self.sdf_atlas.image.ctypes.data_as(
                    ctypes.POINTER(ctypes.c_uint8)))
Example #10
0
class ImageView(object):
    def __init__(self, il):
        """

        :param il:
        :type il: pcbre.model.imagelayer.ImageLayer
        :return:
        """

        self.il = il
        self.im = il.decoded_image



        # haaaaaax
        #flipy = pcbre.matrix.flip(1)
        #self.mat = flipy.dot(self.il.transform_matrix.dot(numpy.linalg.inv(self.dview_tmat)))
        #self.mat = self.dview_tmat
        self.mat = None

    def initGL(self, gls):
        self._tex = Texture()

        # Setup the basic texture parameters
        with self._tex.on(GL.GL_TEXTURE_2D):
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
            GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_S,GL.GL_CLAMP_TO_EDGE);
            GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_T,GL.GL_CLAMP_TO_EDGE);

            # numpy packs data tightly, whereas the openGL default is 4-byte-aligned
            # fix line alignment to 1 byte so odd-sized textures load right
            GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1)

            # Download the data to the buffer. cv2 stores data in BGR format
            GL.glTexImage2D(GL.GL_TEXTURE_2D,
                0,
                GL.GL_RGB,
                self.im.shape[1],
                self.im.shape[0],
                0,
                GL.GL_BGR,
                GL.GL_UNSIGNED_BYTE,
                self.im.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8))
                )

        self.prog = gls.shader_cache.get("image_vert", "image_frag")

        ar = numpy.ndarray(4, dtype=[
            ("vertex", numpy.float32, 2),
            ("texpos", numpy.float32, 2)
        ])

        sca = max(self.im.shape[0], self.im.shape[1])
        x = self.im.shape[1] / float(sca)
        y = self.im.shape[0] / float(sca)
        ar["vertex"] = [ (-x,-y), (-x, y), (x,-y), (x, y)]
        ar["texpos"] = [ (0,0), (0, 1), (1,0), (1, 1)]

        self.b1 = vbobind(self.prog, ar.dtype, "vertex")
        self.b2 = vbobind(self.prog, ar.dtype,"texpos")

        self.vbo = VBO(ar, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER)

        self.mat_loc = GL.glGetUniformLocation(self.prog, "mat")
        self.tex1_loc = GL.glGetUniformLocation(self.prog, "tex1")


        self.vao = VAO()
        with self.vbo, self.vao:
            self.b1.assign()
            self.b2.assign()



    def render(self, viewPort):
        m_pre = self.mat
        if self.mat is None:
            m_pre = self.il.transform_matrix
        mat = viewPort.dot(m_pre)

        GL.glActiveTexture(GL.GL_TEXTURE0)
        with self.prog, self._tex.on(GL.GL_TEXTURE_2D), self.vao:
            GL.glUniformMatrix3fv(self.mat_loc, 1, True, mat.astype(numpy.float32))
            GL.glUniform1i(self.tex1_loc, 0)

            GL.glDrawArrays(GL.GL_TRIANGLE_STRIP,0,4)


    def tfI2W(self, pt):
        x_, y_, t_ = self.il.transform_matrix.dot([pt[0], pt[1], 1.])
        return (x_/t_, y_/t_)

    def tfW2I(self, pt):
        reverse = numpy.linalg.inv(self.il.transform_matrix)
        x_, y_, t_ = reverse.dot([pt[0], pt[1], 1.])
        return (x_/t_, y_/t_)
Example #11
0
class TextRender(object):
    def __init__(self, gls, sdf_atlas):
        self.gls = gls

        self.sdf_atlas = sdf_atlas

        self.last_glyph_count = 0

    def initializeGL(self):
        self.sdf_shader = self.gls.shader_cache.get("image_vert", "tex_frag")

        self.buffer_dtype = numpy.dtype([
            ("vertex", numpy.float32, 2),
            ("texpos", numpy.float32, 2)
        ])

        self.b1 = vbobind(self.sdf_shader, self.buffer_dtype, "vertex")
        self.b2 = vbobind(self.sdf_shader, self.buffer_dtype, "texpos")


        self.tex = Texture()


        # TODO: implement short-int caching
        # build a De-bruijn sequence (shorted substring containing all substrings)
        # self.int_seq = de_bruijn(4)
        # self.int_seq += self.int_seq[:3]


    def getString(self, text):
        """
        create an array of coord data for rendering
        :return:
        """

        # In the future, we'll probably want to move to a texture-buffer-object
        # approach for storing glyph metrics, such that all we need to submit is an
        # array of character indicies and X-offsets
        #
        # This would pack into 8 bytes/char quite nicely (4 byte char index, float32 left)
        # With this, streaming text to the GPU would be much more effective

        # Starting pen X coordinate
        q = []
        pen_x = 0

        left, right, top, bottom = 0, 0, 0, 0

        for ch in text:
            # Fetch the glyph from the atlas
            gp = self.sdf_atlas.getGlyph(ch)

            # width and height of the rendered quad is proportional to the glpyh size
            margin = self.sdf_atlas.margin
            w = (gp.w + margin * 2)
            h = (gp.h + margin * 2)
            # Calculate the offset to the corner of the character.
            c_off_x = pen_x + gp.l - margin
            # Y position is a bit tricky. the "top" of the glyph is whats specified, but we care about the bottom-left
            # so subtract the height
            c_off_y = gp.t - gp.h - margin

            left = min(left, (c_off_x + margin) / BASE_FONT)
            right = max(right, (c_off_x + w - margin) / BASE_FONT)
            bottom = min(bottom, (c_off_y + margin) / BASE_FONT)
            top = max(top, (c_off_y + h - margin) / BASE_FONT)

            x0 = (c_off_x) / BASE_FONT
            y0 = (c_off_y) / BASE_FONT
            x1 = (c_off_x + w) / BASE_FONT
            y1 = (c_off_y + h) / BASE_FONT

            q.append(((x0, y0), (gp.sx, gp.sy)))
            q.append(((x0, y1), (gp.sx, gp.ty)))
            q.append(((x1, y0), (gp.tx, gp.sy)))
            q.append(((x1, y0), (gp.tx, gp.sy)))
            q.append(((x0, y1), (gp.sx, gp.ty)))
            q.append(((x1, y1), (gp.tx, gp.ty)))

            # And increment to the next character
            pen_x += gp.hb
        return TextInfo(q, (left, right, bottom, top))




    def updateTexture(self):
        # Don't update the texture if its up-to-date
        if len(self.sdf_atlas.atlas) == self.last_glyph_count:
            return

        self.last_glyph_count = len(self.sdf_atlas.atlas)

        # Setup the basic texture parameters
        with self.tex.on(GL.GL_TEXTURE_2D):
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR)
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR)

            GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_S,GL.GL_CLAMP_TO_EDGE)
            GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_T,GL.GL_CLAMP_TO_EDGE)

            # numpy packs data tightly, whereas the openGL default is 4-byte-aligned
            # fix line alignment to 1 byte so odd-sized textures load right
            GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1)

            # Download the data to the buffer. cv2 stores data in BGR format
            GL.glTexImage2D(GL.GL_TEXTURE_2D,
                            0,
                            GL.GL_RED,
                            self.sdf_atlas.image.shape[1],
                            self.sdf_atlas.image.shape[0],
                            0,
                            GL.GL_RED,
                            GL.GL_UNSIGNED_BYTE,
                            self.sdf_atlas.image.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)))
Example #12
0
class CompositeManager:
    def __init__(self) -> None:
        self.__width = 0
        self.__height = 0

        self.__layer_fbs: 'List[RenderLayer]' = []

        self.__active_count = 0
        self.__keys: 'Dict[Any, RenderLayer]' = {}

    def get(self, key: Any) -> 'RenderLayer':
        # If we've already got a composite target for this key
        # return it
        if key in self.__keys:
            return self.__keys[key]

        # Allocate a new layer if required
        if self.__active_count == len(self.__layer_fbs):
            self.__layer_fbs.append(RenderLayer(self.__width, self.__height))

        # Save new layer fb for reuse
        self.__keys[key] = self.__layer_fbs[self.__active_count]
        self.__active_count += 1
        return self.__keys[key]

    def resize(self, width: int, height: int) -> None:
        if width == self.__width and height == self.__height:
            return

        self.__width = width
        self.__height = height

        for i in self.__layer_fbs:
            i.resize(width, height)

        self.__composite_vbo.set_array(self.__get_vbo_data())
        with self.__composite_vbo:
            self.__composite_vbo.copy_data()

    def restart(self) -> None:
        """
        Call at the start of rendering. Resets all layers to initial state
        :return:
        """

        self.__keys = {}

        self.__active_count = 0

        for n, _ in enumerate(self.__layer_fbs):
            self.reset_layer(n)

    def reset_layer(self, n: int) -> None:
        """
        Reset a particular layer to an empty state. This implies alpha of 0 (transparent) and type of 0 (undrawn)
        :param n:
        :return:
        """
        with self.__layer_fbs[n]:
            GL.glClearBufferfv(GL.GL_COLOR, 0, (0, 255, 0, 0))

    def __get_vbo_data(self) -> 'npt.NDArray[numpy.float64]':
        if self.__width == 0 or self.__height == 0:
            assert False

        # Fullscreen textured quad
        filled_points = [
            ((-1.0, -1.0), (0.0, 0.0)),
            ((1.0, -1.0), (1.0, 0.0)),
            ((-1.0, 1.0), (0.0, 1.0)),
            ((1.0, 1.0), (1.0, 1.0)),
        ]

        ar = numpy.array(filled_points,
                         dtype=[("vertex", numpy.float32, 2),
                                ("texpos", numpy.float32, 2)])

        sscale = max(self.__width, self.__height)
        xscale = self.__width / sscale
        yscale = self.__height / sscale

        ar["vertex"][:, 0] *= xscale
        ar["vertex"][:, 1] *= yscale

        return ar

    def initializeGL(self, gls: 'GLShared', width: int, height: int) -> None:
        self.__width = width
        self.__height = height

        # Initialize (but don't fill) the Color LUT
        self.__texture_colors = Texture(debug_name="Layer Color LUT")
        with self.__texture_colors.on(GL.GL_TEXTURE_1D):
            GL.glTexParameteri(GL.GL_TEXTURE_1D, GL.GL_TEXTURE_MIN_FILTER,
                               GL.GL_NEAREST)
            GL.glTexParameteri(GL.GL_TEXTURE_1D, GL.GL_TEXTURE_MAG_FILTER,
                               GL.GL_NEAREST)
            GL.glTexParameteri(GL.GL_TEXTURE_1D, GL.GL_TEXTURE_WRAP_S,
                               GL.GL_CLAMP_TO_EDGE)
            GL.glTexParameteri(GL.GL_TEXTURE_1D, GL.GL_TEXTURE_WRAP_T,
                               GL.GL_CLAMP_TO_EDGE)

        # Compositing shader and geometry
        self.__composite_shader = gls.shader_cache.get(
            "layer_composite_vert",
            "layer_composite_frag",
            fragment_bindings={"final_color": 0})

        ar = self.__get_vbo_data()
        self.__composite_vao = VAO(debug_name="Compositor Quad VAO")
        self.__composite_vbo = VBO(ar, GL.GL_STATIC_DRAW)
        GL.glObjectLabel(GL.GL_BUFFER, int(self.__composite_vbo), -1,
                         "Compositor Quad VBO")

        with self.__composite_vao:
            self.__composite_vbo.bind()
            VBOBind(self.__composite_shader.program, ar.dtype,
                    "vertex").assign()
            VBOBind(self.__composite_shader.program, ar.dtype,
                    "texpos").assign()

    def set_color_table(self,
                        colors: 'Sequence[Tuple[int, int, int, int]]') -> None:
        array: 'npt.NDArray[numpy.uint8]' = numpy.ndarray((256, 4),
                                                          dtype=numpy.uint8)

        # Create a stub array with the color table data
        array.fill(0)
        array[:] = (255, 0, 255, 255)
        array[:len(colors)] = colors

        with self.__texture_colors.on(GL.GL_TEXTURE_1D):
            GL.glTexImage1D(GL.GL_TEXTURE_1D, 0, GL.GL_RGBA, 256, 0,
                            GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, array)

    class _PREBIND:
        def __init__(self, layer_list: 'Dict[Any, RenderLayer]',
                     composite_shader: 'EnhShaderProgram'):
            self.layer_list = layer_list
            self.composite_shader = composite_shader

        def composite(self, n: Any,
                      layer_primary_color: 'Tuple[int,int,int]') -> None:
            alpha = 0.5 * 255
            try:
                layer = self.layer_list[n]
            except KeyError:
                return

            GL.glActiveTexture(GL.GL_TEXTURE0)
            GL.glBindTexture(GL.GL_TEXTURE_2D, layer.info_texture.v)

            GL.glUniform4f(self.composite_shader.uniforms.layer_color,
                           layer_primary_color[0], layer_primary_color[1],
                           layer_primary_color[2], alpha)

            GL.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, 4)

    @contextlib.contextmanager
    def composite_prebind(self) -> Generator['_PREBIND', None, None]:
        GL.glActiveTexture(GL.GL_TEXTURE1)
        GL.glBindTexture(GL.GL_TEXTURE_1D, self.__texture_colors.v)

        GL.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA)

        # Composite the layer to the screen
        with self.__composite_shader.program, self.__composite_vao:
            GL.glUniform1i(self.__composite_shader.uniforms.layer_info, 0)
            GL.glUniform1i(self.__composite_shader.uniforms.color_tab, 1)

            yield self._PREBIND(self.__keys, self.__composite_shader)
Example #13
0
class ImageView(object):
    def __init__(self, il):
        """

        :param il:
        :type il: pcbre.model.imagelayer.ImageLayer
        :return:
        """

        self.il = il
        self.im = il.decoded_image

        # haaaaaax
        #flipy = pcbre.matrix.flip(1)
        #self.mat = flipy.dot(self.il.transform_matrix.dot(numpy.linalg.inv(self.dview_tmat)))
        #self.mat = self.dview_tmat
        self.mat = None

    def initGL(self, gls):
        self._tex = Texture()

        # Setup the basic texture parameters
        with self._tex.on(GL.GL_TEXTURE_2D):
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER,
                               GL.GL_NEAREST)
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER,
                               GL.GL_LINEAR)
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S,
                               GL.GL_CLAMP_TO_EDGE)
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T,
                               GL.GL_CLAMP_TO_EDGE)

            # numpy packs data tightly, whereas the openGL default is 4-byte-aligned
            # fix line alignment to 1 byte so odd-sized textures load right
            GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1)

            # Download the data to the buffer. cv2 stores data in BGR format
            GL.glTexImage2D(
                GL.GL_TEXTURE_2D, 0, GL.GL_RGB, self.im.shape[1],
                self.im.shape[0], 0, GL.GL_BGR, GL.GL_UNSIGNED_BYTE,
                self.im.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)))

        self.prog = gls.shader_cache.get("image_vert", "image_frag")

        ar = numpy.ndarray(4,
                           dtype=[("vertex", numpy.float32, 2),
                                  ("texpos", numpy.float32, 2)])

        sca = max(self.im.shape[0], self.im.shape[1])
        x = self.im.shape[1] / float(sca)
        y = self.im.shape[0] / float(sca)
        ar["vertex"] = [(-x, -y), (-x, y), (x, -y), (x, y)]
        ar["texpos"] = [(0, 0), (0, 1), (1, 0), (1, 1)]

        self.b1 = vbobind(self.prog, ar.dtype, "vertex")
        self.b2 = vbobind(self.prog, ar.dtype, "texpos")

        self.vbo = VBO(ar, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER)

        self.mat_loc = GL.glGetUniformLocation(self.prog, "mat")
        self.tex1_loc = GL.glGetUniformLocation(self.prog, "tex1")

        self.vao = VAO()
        with self.vbo, self.vao:
            self.b1.assign()
            self.b2.assign()

    def render(self, viewPort):
        #proj = viewPort.dot(self.il.transform_matrix)
        #cv2.warpPerspective(self.im, proj,
        #               (viewPort.width, viewPort.height), # No depth coord
        #               surface.buffer, 0, cv2.BORDER_TRANSPARENT)

        m_pre = self.mat
        if self.mat is None:
            m_pre = self.il.transform_matrix
        mat = viewPort.dot(m_pre)

        GL.glActiveTexture(GL.GL_TEXTURE0)
        with self.prog, self._tex.on(GL.GL_TEXTURE_2D), self.vao:
            #with self.prog, self.vao:
            GL.glUniformMatrix3fv(self.mat_loc, 1, True,
                                  mat.astype(numpy.float32))
            GL.glUniform1i(self.tex1_loc, 0)

            GL.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, 4)

    def tfI2W(self, pt):
        x_, y_, t_ = self.il.transform_matrix.dot([pt[0], pt[1], 1.])
        return (x_ / t_, y_ / t_)

    def tfW2I(self, pt):
        reverse = numpy.linalg.inv(self.il.transform_matrix)
        x_, y_, t_ = reverse.dot([pt[0], pt[1], 1.])
        return (x_ / t_, y_ / t_)