def _opengl_context(): """Assure we are running with a valid OpenGL context. Only create a Canvas is one doesn't exist. Creating and closing a Canvas causes vispy to process Qt events which can cause problems. Ideally call opengl_context() on start after creating your first Canvas. However it will work either way. """ canvas = Canvas(show=False) if get_current_canvas() is None else None try: yield finally: if canvas is not None: canvas.close()
def _text_to_vbo(text, font, anchor_x, anchor_y, lowres_size): """Convert text characters to VBO""" # Necessary to flush commands before requesting current viewport because # There may be a set_viewport command waiting in the queue. # TODO: would be nicer if each canvas just remembers and manages its own # viewport, rather than relying on the context for this. canvas = context.get_current_canvas() canvas.context.flush_commands() text_vtype = np.dtype([('a_position', np.float32, 2), ('a_texcoord', np.float32, 2)]) vertices = np.zeros(len(text) * 4, dtype=text_vtype) prev = None width = height = ascender = descender = 0 ratio, slop = 1. / font.ratio, font.slop x_off = -slop # Need to make sure we have a unicode string here (Py2.7 mis-interprets # characters like "•" otherwise) if sys.version[0] == '2' and isinstance(text, str): text = text.decode('utf-8') # Need to store the original viewport, because the font[char] will # trigger SDF rendering, which changes our viewport # todo: get rid of call to glGetParameter! # Also analyse chars with large ascender and descender, otherwise the # vertical alignment can be very inconsistent for char in 'hy': glyph = font[char] y0 = glyph['offset'][1] * ratio + slop y1 = y0 - glyph['size'][1] ascender = max(ascender, y0 - slop) descender = min(descender, y1 + slop) height = max(height, glyph['size'][1] - 2 * slop) # Get/set the fonts whitespace length and line height (size of this ok?) glyph = font[' '] spacewidth = glyph['advance'] * ratio lineheight = height * 1.5 # Added escape sequences characters: {unicode:offset,...} # ord('\a') = 7 # ord('\b') = 8 # ord('\f') = 12 # ord('\n') = 10 => linebreak # ord('\r') = 13 # ord('\t') = 9 => tab, set equal 4 whitespaces? # ord('\v') = 11 => vertical tab, set equal 4 linebreaks? # If text coordinate offset > 0 -> it applies to x-direction # If text coordinate offset < 0 -> it applies to y-direction esc_seq = {7: 0, 8: 0, 9: -4, 10: 1, 11: 4, 12: 0, 13: 0} # Keep track of y_offset to set lines at right position y_offset = 0 # When a line break occur, record the vertices index value vi_marker = 0 ii_offset = 0 # Offset since certain characters won't be drawn # The running tracker of characters vertex index vi = 0 orig_viewport = canvas.context.get_viewport() for ii, char in enumerate(text): if ord(char) in esc_seq: if esc_seq[ord(char)] < 0: # Add offset in x-direction x_off += abs(esc_seq[ord(char)]) * spacewidth width += abs(esc_seq[ord(char)]) * spacewidth elif esc_seq[ord(char)] > 0: # Add offset in y-direction and reset things in x-direction dx = dy = 0 if anchor_x == 'right': dx = -width elif anchor_x == 'center': dx = -width / 2. vertices['a_position'][vi_marker:vi + 4] += (dx, dy) vi_marker = vi + 4 ii_offset -= 1 # Reset variables that affects x-direction positioning x_off = -slop width = 0 # Add offset in y-direction y_offset += esc_seq[ord(char)] * lineheight else: # For ordinary characters, normal procedure glyph = font[char] kerning = glyph['kerning'].get(prev, 0.) * ratio x0 = x_off + glyph['offset'][0] * ratio + kerning y0 = glyph['offset'][1] * ratio + slop - y_offset x1 = x0 + glyph['size'][0] y1 = y0 - glyph['size'][1] u0, v0, u1, v1 = glyph['texcoords'] position = [[x0, y0], [x0, y1], [x1, y1], [x1, y0]] texcoords = [[u0, v0], [u0, v1], [u1, v1], [u1, v0]] vi = (ii + ii_offset) * 4 vertices['a_position'][vi:vi + 4] = position vertices['a_texcoord'][vi:vi + 4] = texcoords x_move = glyph['advance'] * ratio + kerning x_off += x_move ascender = max(ascender, y0 - slop) descender = min(descender, y1 + slop) width += x_move height = max(height, glyph['size'][1] - 2 * slop) prev = char if orig_viewport is not None: canvas.context.set_viewport(*orig_viewport) dx = dy = 0 if anchor_y == 'top': dy = -descender elif anchor_y in ('center', 'middle'): dy = (-descender - ascender) / 2 elif anchor_y == 'bottom': dy = -ascender if anchor_x == 'right': dx = -width elif anchor_x == 'center': dx = -width / 2. # If any linebreaks occured in text, we only want to translate characters # in the last line in text (those after the vi_marker) vertices['a_position'][0:vi_marker] += (0, dy) vertices['a_position'][vi_marker:] += (dx, dy) vertices['a_position'] /= lowres_size return vertices