Skip to content

Commit

Permalink
Slighly improve performance by caching font lookup
Browse files Browse the repository at this point in the history
  • Loading branch information
ihabunek committed Sep 5, 2024
1 parent 20639fe commit 1eceae4
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 31 deletions.
10 changes: 8 additions & 2 deletions twitchdl/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from twitchdl import cache
from twitchdl.entities import Badge, Comment, Emote, Video
from twitchdl.exceptions import ConsoleError
from twitchdl.fonts import Font, group_by_font, load_font
from twitchdl.fonts import Font, char_name, group_by_font, load_font, make_group_by_font
from twitchdl.naming import video_filename
from twitchdl.output import green, print_json, print_log, print_status
from twitchdl.twitch import get_comments, get_video, get_video_comments
Expand Down Expand Up @@ -231,8 +231,9 @@ def __init__(
self.x: int = 0
self.y: int = 0

self.line_height = max(f.height for f in fonts if not f.is_bitmap)
self.group_by_font = make_group_by_font(fonts, self.on_char_not_found)

self.line_height = max(f.height for f in fonts if not f.is_bitmap)
left, _, right, _ = fonts[0].image_font.getbbox(" ")
self.space_size = int(right - left)

Expand All @@ -241,6 +242,10 @@ def __init__(
self._image = Image.new("RGBA", image_size, self.background)
self._draw = ImageDraw.Draw(self._image)

def on_char_not_found(self, char: str):
"""Invoked when a char cannot be rendered in any of the fonts."""
print_status(f"Cannot render char '{char}' Name: {char_name(char)} Codepoint: {ord(char)}")

@property
def image(self):
return self._image
Expand All @@ -257,6 +262,7 @@ def draw(self) -> ImageDraw.ImageDraw:
def draw_text(self, text: str, color: Optional[str] = None):
# Split into words while keeping the whitespace
for word in re.split(r"(?=\s)", text):
# for fragment, font in self.group_by_font(word):
for fragment, font in group_by_font(word, self.fonts):
if font.is_bitmap:
for emoji in fragment:
Expand Down
61 changes: 34 additions & 27 deletions twitchdl/fonts.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
from functools import lru_cache
import hashlib
import json
import math
import unicodedata
from dataclasses import dataclass
from pathlib import Path
from typing import Generator, Iterable, List, Optional, Set, Tuple
from typing import Callable, Dict, Generator, Iterable, List, Optional, Set, Tuple

from fontTools.ttLib import TTFont, TTLibFileIsCollectionError # type: ignore
from fontTools.ttLib.ttCollection import TTCollection # type: ignore
from PIL import ImageFont

from twitchdl.cache import get_cache_dir
from twitchdl.output import print_log
from twitchdl.output import print_log, print_status


@dataclass
Expand Down Expand Up @@ -119,36 +120,42 @@ def dump_codepoints(path: Path):
print(f"{codepoint}\t{chr(codepoint)}\t{name}")


def get_font_for_char(fonts: List[Font], char: str) -> Optional[Font]:
for font in fonts:
if ord(char) in font.codepoints:
return font
return None
def make_group_by_font(
fonts: List[Font],
on_char_not_found: Callable[[str], None],
) -> Callable[[str], Generator[Tuple[str, Font], None, None]]:

@lru_cache
def get_font(char: str) -> Optional[Font]:
for font in fonts:
if ord(char) in font.codepoints:
return font

def group_by_font(text: str, fonts: List[Font]) -> Generator[Tuple[str, Font], None, None]:
"""Split given text into chunks which can be rendered by the same font."""
if not text:
return
def group_by_font(text: str):
"""Split given text into chunks which can be rendered by the same font."""
if not text:
return

buffer = ""
font = None
buffer = ""
font = None

for char in text:
char_font = get_font_for_char(fonts, char)
if not char_font:
print(f"Cannot render char: {char} {char_name(char)} {ord(char)}")
continue
for char in text:
char_font = get_font(char)
if not char_font:
on_char_not_found(char)
continue

if not font:
font = char_font
if not font:
font = char_font

if font == char_font:
buffer += char
else:
if font == char_font:
buffer += char
else:
yield buffer, font
font = char_font
buffer = char

if buffer and font:
yield buffer, font
font = char_font
buffer = char

if buffer and font:
yield buffer, font
return group_by_font
4 changes: 2 additions & 2 deletions twitchdl/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@

import click

from twitchdl.output import print_status

T = TypeVar("T")
K = TypeVar("K")
V = TypeVar("V")
Expand Down Expand Up @@ -149,6 +147,8 @@ def timed(message: str = "Time taken"):

@contextmanager
def monitor_performance(group: str):
from twitchdl.output import print_status

global perfs
start = time.monotonic()
yield
Expand Down

0 comments on commit 1eceae4

Please sign in to comment.