Team Ai
Apppublic

OpenMotionLab/MotionGPT

sourceHugging Facemitupdated 1y agoView on Hugging Face
118likes
font.py273 linesDownload Raw Back to pyrender
1"""Font texture loader and processor.2 3Author: Matthew Matl4"""5import freetype6import numpy as np7import os8 9import OpenGL10from OpenGL.GL import *11 12from .constants import TextAlign, FLOAT_SZ13from .texture import Texture14from .sampler import Sampler15 16 17class FontCache(object):18    """A cache for fonts.19    """20 21    def __init__(self, font_dir=None):22        self._font_cache = {}23        self.font_dir = font_dir24        if self.font_dir is None:25            base_dir, _ = os.path.split(os.path.realpath(__file__))26            self.font_dir = os.path.join(base_dir, 'fonts')27 28    def get_font(self, font_name, font_pt):29        # If it's a file, load it directly, else, try to load from font dir.30        if os.path.isfile(font_name):31            font_filename = font_name32            _, font_name = os.path.split(font_name)33            font_name, _ = os.path.split(font_name)34        else:35            font_filename = os.path.join(self.font_dir, font_name) + '.ttf'36 37        cid = OpenGL.contextdata.getContext()38        key = (cid, font_name, int(font_pt))39 40        if key not in self._font_cache:41            self._font_cache[key] = Font(font_filename, font_pt)42        return self._font_cache[key]43 44    def clear(self):45        for key in self._font_cache:46            self._font_cache[key].delete()47        self._font_cache = {}48 49 50class Character(object):51    """A single character, with its texture and attributes.52    """53 54    def __init__(self, texture, size, bearing, advance):55        self.texture = texture56        self.size = size57        self.bearing = bearing58        self.advance = advance59 60 61class Font(object):62    """A font object.63 64    Parameters65    ----------66    font_file : str67        The file to load the font from.68    font_pt : int69        The height of the font in pixels.70    """71 72    def __init__(self, font_file, font_pt=40):73        self.font_file = font_file74        self.font_pt = int(font_pt)75        self._face = freetype.Face(font_file)76        self._face.set_pixel_sizes(0, font_pt)77        self._character_map = {}78 79        for i in range(0, 128):80 81            # Generate texture82            face = self._face83            face.load_char(chr(i))84            buf = face.glyph.bitmap.buffer85            src = (np.array(buf) / 255.0).astype(np.float32)86            src = src.reshape((face.glyph.bitmap.rows,87                               face.glyph.bitmap.width))88            tex = Texture(89                sampler=Sampler(90                    magFilter=GL_LINEAR,91                    minFilter=GL_LINEAR,92                    wrapS=GL_CLAMP_TO_EDGE,93                    wrapT=GL_CLAMP_TO_EDGE94                ),95                source=src,96                source_channels='R',97            )98            character = Character(99                texture=tex,100                size=np.array([face.glyph.bitmap.width,101                               face.glyph.bitmap.rows]),102                bearing=np.array([face.glyph.bitmap_left,103                                  face.glyph.bitmap_top]),104                advance=face.glyph.advance.x105            )106            self._character_map[chr(i)] = character107 108        self._vbo = None109        self._vao = None110 111    @property112    def font_file(self):113        """str : The file the font was loaded from.114        """115        return self._font_file116 117    @font_file.setter118    def font_file(self, value):119        self._font_file = value120 121    @property122    def font_pt(self):123        """int : The height of the font in pixels.124        """125        return self._font_pt126 127    @font_pt.setter128    def font_pt(self, value):129        self._font_pt = int(value)130 131    def _add_to_context(self):132 133        self._vao = glGenVertexArrays(1)134        glBindVertexArray(self._vao)135        self._vbo = glGenBuffers(1)136        glBindBuffer(GL_ARRAY_BUFFER, self._vbo)137        glBufferData(GL_ARRAY_BUFFER, FLOAT_SZ * 6 * 4, None, GL_DYNAMIC_DRAW)138        glEnableVertexAttribArray(0)139        glVertexAttribPointer(140            0, 4, GL_FLOAT, GL_FALSE, 4 * FLOAT_SZ, ctypes.c_void_p(0)141        )142        glBindVertexArray(0)143 144        glPixelStorei(GL_UNPACK_ALIGNMENT, 1)145        for c in self._character_map:146            ch = self._character_map[c]147            if not ch.texture._in_context():148                ch.texture._add_to_context()149 150    def _remove_from_context(self):151        for c in self._character_map:152            ch = self._character_map[c]153            ch.texture.delete()154        if self._vao is not None:155            glDeleteVertexArrays(1, [self._vao])156            glDeleteBuffers(1, [self._vbo])157            self._vao = None158            self._vbo = None159 160    def _in_context(self):161        return self._vao is not None162 163    def _bind(self):164        glBindVertexArray(self._vao)165 166    def _unbind(self):167        glBindVertexArray(0)168 169    def delete(self):170        self._unbind()171        self._remove_from_context()172 173    def render_string(self, text, x, y, scale=1.0,174                      align=TextAlign.BOTTOM_LEFT):175        """Render a string to the current view buffer.176 177        Note178        ----179        Assumes correct shader program already bound w/ uniforms set.180 181        Parameters182        ----------183        text : str184            The text to render.185        x : int186            Horizontal pixel location of text.187        y : int188            Vertical pixel location of text.189        scale : int190            Scaling factor for text.191        align : int192            One of the TextAlign options which specifies where the ``x``193            and ``y`` parameters lie on the text. For example,194            :attr:`.TextAlign.BOTTOM_LEFT` means that ``x`` and ``y`` indicate195            the position of the bottom-left corner of the textbox.196        """197        glActiveTexture(GL_TEXTURE0)198        glEnable(GL_BLEND)199        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)200        glDisable(GL_DEPTH_TEST)201        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)202        self._bind()203 204        # Determine width and height of text relative to x, y205        width = 0.0206        height = 0.0207        for c in text:208            ch = self._character_map[c]209            height = max(height, ch.bearing[1] * scale)210            width += (ch.advance >> 6) * scale211 212        # Determine offsets based on alignments213        xoff = 0214        yoff = 0215        if align == TextAlign.BOTTOM_RIGHT:216            xoff = -width217        elif align == TextAlign.BOTTOM_CENTER:218            xoff = -width / 2.0219        elif align == TextAlign.TOP_LEFT:220            yoff = -height221        elif align == TextAlign.TOP_RIGHT:222            yoff = -height223            xoff = -width224        elif align == TextAlign.TOP_CENTER:225            yoff = -height226            xoff = -width / 2.0227        elif align == TextAlign.CENTER:228            xoff = -width / 2.0229            yoff = -height / 2.0230        elif align == TextAlign.CENTER_LEFT:231            yoff = -height / 2.0232        elif align == TextAlign.CENTER_RIGHT:233            xoff = -width234            yoff = -height / 2.0235 236        x += xoff237        y += yoff238 239        ch = None240        for c in text:241            ch = self._character_map[c]242            xpos = x + ch.bearing[0] * scale243            ypos = y - (ch.size[1] - ch.bearing[1]) * scale244            w = ch.size[0] * scale245            h = ch.size[1] * scale246 247            vertices = np.array([248                [xpos, ypos, 0.0, 0.0],249                [xpos + w, ypos, 1.0, 0.0],250                [xpos + w, ypos + h, 1.0, 1.0],251                [xpos + w, ypos + h, 1.0, 1.0],252                [xpos, ypos + h, 0.0, 1.0],253                [xpos, ypos, 0.0, 0.0],254            ], dtype=np.float32)255 256            ch.texture._bind()257 258            glBindBuffer(GL_ARRAY_BUFFER, self._vbo)259            glBufferData(260                GL_ARRAY_BUFFER, FLOAT_SZ * 6 * 4, vertices, GL_DYNAMIC_DRAW261            )262            # TODO MAKE THIS MORE EFFICIENT, lgBufferSubData is broken263            # glBufferSubData(264            #     GL_ARRAY_BUFFER, 0, 6 * 4 * FLOAT_SZ,265            #     np.ascontiguousarray(vertices.flatten)266            # )267            glDrawArrays(GL_TRIANGLES, 0, 6)268            x += (ch.advance >> 6) * scale269 270        self._unbind()271        if ch:272            ch.texture._unbind()273