-
-
Notifications
You must be signed in to change notification settings - Fork 241
/
text_filter.py
87 lines (69 loc) · 3.25 KB
/
text_filter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import os
from fractions import Fraction
from typing import Callable, Tuple
from PIL import Image, ImageDraw, ImageFilter, ImageFont
from streamdeck_ui.config import FONTS_PATH
from streamdeck_ui.display.filter import Filter
class TextFilter(Filter):
font_blur: ImageFilter.Kernel = None
# Static instance - no need to create one per Filter instance
image: Image
def __init__(self, text: str, font: str, vertical_align: str):
super(TextFilter, self).__init__()
self.text = text
self.vertical_align = vertical_align
self.true_font = ImageFont.truetype(os.path.join(FONTS_PATH, font), 14)
# fmt: off
kernel = [
0, 1, 2, 1, 0,
1, 2, 4, 2, 1,
2, 4, 8, 4, 1,
1, 2, 4, 2, 1,
0, 1, 2, 1, 0]
# fmt: on
TextFilter.font_blur = ImageFilter.Kernel((5, 5), kernel, scale=0.1 * sum(kernel))
self.offset = 0.0
self.offset_direction = 1
self.image = None
# Hashcode should be created for anything that makes this frame unique
self.hashcode = hash((self.__class__, text, font, vertical_align))
def initialize(self, size: Tuple[int, int]):
self.image = Image.new("RGBA", size)
backdrop_draw = ImageDraw.Draw(self.image)
# Calculate the height and width of the text we're drawing, using the font itself
label_w, _ = backdrop_draw.textsize(self.text, font=self.true_font)
# Calculate dimensions for text that include ascender (above the line)
# and below the line (descender) characters. This is used to adust the
# font placement and should allow for button text to horizontally align
# across buttons. Basically we want to figure out what is the tallest
# text we will need to draw.
_, label_h = backdrop_draw.textsize("lLpgyL|", font=self.true_font)
gap = (size[1] - 5 * label_h) // 4
if self.vertical_align == "top":
label_y = 0
elif self.vertical_align == "middle-top":
label_y = gap + label_h
elif self.vertical_align == "middle":
label_y = size[1] // 2 - (label_h // 2)
elif self.vertical_align == "middle-bottom":
label_y = (gap + label_h) * 3
else:
label_y = size[1] - label_h
# Default or "bottom"
label_pos = ((size[0] - label_w) // 2, label_y)
backdrop_draw.text(label_pos, text=self.text, font=self.true_font, fill="black")
self.image = self.image.filter(TextFilter.font_blur)
foreground_draw = ImageDraw.Draw(self.image)
foreground_draw.text(label_pos, text=self.text, font=self.true_font, fill="white")
def transform(self, get_input: Callable[[], Image.Image], get_output: Callable[[int], Image.Image], input_changed: bool, time: Fraction) -> Tuple[Image.Image, int]:
"""
The transformation returns the loaded image, ando overwrites whatever came before.
"""
if input_changed:
image = get_output(self.hashcode)
if image:
return (image, self.hashcode)
input = get_input()
input.paste(self.image, self.image)
return (input, self.hashcode)
return (None, self.hashcode)