|
| 1 | +#!/usr/bin/env python3 |
| 2 | +"""A TrueType Font example using the FreeType library. |
| 3 | +
|
| 4 | +You will need to get this external library from PyPI: |
| 5 | +
|
| 6 | + pip install freetype-py |
| 7 | +
|
| 8 | +This script has known issues and may crash when the window is resized. |
| 9 | +""" |
| 10 | +from typing import Tuple |
| 11 | + |
| 12 | +import freetype # type: ignore # pip install freetype-py |
| 13 | +import numpy as np |
| 14 | +import tcod |
| 15 | + |
| 16 | +FONT = "VeraMono.ttf" |
| 17 | + |
| 18 | + |
| 19 | +def load_ttf(path: str, size: Tuple[int, int]) -> tcod.tileset.Tileset: |
| 20 | + """Load a TTF file as a tcod tileset.""" |
| 21 | + ttf = freetype.Face(path) |
| 22 | + ttf.set_pixel_sizes(*size) |
| 23 | + half_advance = size[0] - (ttf.bbox.xMax - ttf.bbox.xMin) // 64 |
| 24 | + |
| 25 | + tileset = tcod.tileset.Tileset(*size) |
| 26 | + for codepoint, glyph_index in ttf.get_chars(): |
| 27 | + ttf.load_glyph(glyph_index) |
| 28 | + bitmap = ttf.glyph.bitmap |
| 29 | + assert bitmap.pixel_mode == freetype.FT_PIXEL_MODE_GRAY |
| 30 | + bitmap_array = np.asarray(bitmap.buffer).reshape( |
| 31 | + (bitmap.width, bitmap.rows), order="F" |
| 32 | + ) |
| 33 | + if bitmap_array.size == 0: |
| 34 | + continue |
| 35 | + output_image = np.zeros(size, dtype=np.uint8, order="F") |
| 36 | + out_slice = output_image |
| 37 | + left = ttf.glyph.bitmap_left + half_advance |
| 38 | + top = size[1] - ttf.glyph.bitmap_top + ttf.bbox.yMin // 64 |
| 39 | + out_slice = out_slice[max(0, left) :, max(0, top) :] |
| 40 | + out_slice[ |
| 41 | + : bitmap_array.shape[0], : bitmap_array.shape[1] |
| 42 | + ] = bitmap_array[: out_slice.shape[0], : out_slice.shape[1]] |
| 43 | + tileset.set_tile(codepoint, output_image.transpose()) |
| 44 | + return tileset |
| 45 | + |
| 46 | + |
| 47 | +def main() -> None: |
| 48 | + console = tcod.Console(16, 12, order="F") |
| 49 | + with tcod.context.new( |
| 50 | + columns=console.width, |
| 51 | + rows=console.height, |
| 52 | + tileset=load_ttf(FONT, (24, 24)), |
| 53 | + ) as context: |
| 54 | + while True: |
| 55 | + console.clear() |
| 56 | + console.tiles_rgb["bg"][::2, ::2] = 0x20 |
| 57 | + console.tiles_rgb["bg"][1::2, 1::2] = 0x20 |
| 58 | + console.tiles_rgb["ch"][:16, :6] = np.arange(0x20, 0x80).reshape( |
| 59 | + 0x10, -1, order="F" |
| 60 | + ) |
| 61 | + console.print(0, 7, "Example text.") |
| 62 | + context.present(console, integer_scaling=True) |
| 63 | + for event in tcod.event.wait(): |
| 64 | + if isinstance(event, tcod.event.Quit): |
| 65 | + raise SystemExit() |
| 66 | + if ( |
| 67 | + isinstance(event, tcod.event.WindowResized) |
| 68 | + and event.type == "WINDOWSIZECHANGED" |
| 69 | + ): |
| 70 | + context.change_tileset( |
| 71 | + load_ttf( |
| 72 | + path=FONT, |
| 73 | + size=( |
| 74 | + event.width // console.width, |
| 75 | + event.height // console.height, |
| 76 | + ), |
| 77 | + ) |
| 78 | + ) |
| 79 | + |
| 80 | + |
| 81 | +if __name__ == "__main__": |
| 82 | + main() |
0 commit comments