Skip to content

Commit 9d604e6

Browse files
authored
Merge pull request #203 from FoamyGuy/outlined_label
Outlined label
2 parents c3be259 + 88775e0 commit 9d604e6

File tree

4 files changed

+229
-0
lines changed

4 files changed

+229
-0
lines changed
+188
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
# SPDX-FileCopyrightText: 2023 Tim C
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
"""
6+
`adafruit_display_text.outlined_label`
7+
====================================================
8+
9+
Subclass of BitmapLabel that adds outline color and stroke size
10+
functionalities.
11+
12+
* Author(s): Tim Cocks
13+
14+
Implementation Notes
15+
--------------------
16+
17+
**Hardware:**
18+
19+
**Software and Dependencies:**
20+
21+
* Adafruit CircuitPython firmware for the supported boards:
22+
https://circuitpython.org/downloads
23+
24+
"""
25+
__version__ = "0.0.0+auto.0"
26+
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Display_Text.git"
27+
28+
import bitmaptools
29+
from displayio import Palette, Bitmap
30+
from adafruit_display_text import bitmap_label
31+
32+
try:
33+
from typing import Optional, Tuple, Union
34+
from fontio import FontProtocol
35+
except ImportError:
36+
pass
37+
38+
39+
class OutlinedLabel(bitmap_label.Label):
40+
"""
41+
OutlinedLabel - A BitmapLabel subclass that includes arguments and properties for specifying
42+
outline_size and outline_color to get drawn as a stroke around the text.
43+
44+
:param Union[Tuple, int] outline_color: The color of the outline stroke as RGB tuple, or hex.
45+
:param int outline_size: The size in pixels of the outline stroke.
46+
47+
"""
48+
49+
# pylint: disable=too-many-arguments
50+
def __init__(
51+
self,
52+
font,
53+
outline_color: Union[int, Tuple] = 0x999999,
54+
outline_size: int = 1,
55+
padding_top: Optional[int] = None,
56+
padding_bottom: Optional[int] = None,
57+
padding_left: Optional[int] = None,
58+
padding_right: Optional[int] = None,
59+
**kwargs
60+
):
61+
if padding_top is None:
62+
padding_top = outline_size + 0
63+
if padding_bottom is None:
64+
padding_bottom = outline_size + 2
65+
if padding_left is None:
66+
padding_left = outline_size + 0
67+
if padding_right is None:
68+
padding_right = outline_size + 0
69+
70+
super().__init__(
71+
font,
72+
padding_top=padding_top,
73+
padding_bottom=padding_bottom,
74+
padding_left=padding_left,
75+
padding_right=padding_right,
76+
**kwargs
77+
)
78+
79+
_background_color = self._palette[0]
80+
_foreground_color = self._palette[1]
81+
_background_is_transparent = self._palette.is_transparent(0)
82+
self._palette = Palette(3)
83+
self._palette[0] = _background_color
84+
self._palette[1] = _foreground_color
85+
self._palette[2] = outline_color
86+
if _background_is_transparent:
87+
self._palette.make_transparent(0)
88+
89+
self._outline_size = outline_size
90+
self._stamp_source = Bitmap((outline_size * 2) + 1, (outline_size * 2) + 1, 3)
91+
self._stamp_source.fill(2)
92+
93+
self._bitmap = None
94+
95+
self._reset_text(
96+
font=font,
97+
text=self._text,
98+
line_spacing=self._line_spacing,
99+
scale=self.scale,
100+
)
101+
102+
def _add_outline(self):
103+
"""
104+
Blit the outline into the labels Bitmap. We will stamp self._stamp_source for each
105+
pixel of the foreground color but skip the foreground color when we blit.
106+
:return: None
107+
"""
108+
if hasattr(self, "_stamp_source"):
109+
for y in range(self.bitmap.height):
110+
for x in range(self.bitmap.width):
111+
if self.bitmap[x, y] == 1:
112+
try:
113+
bitmaptools.blit(
114+
self.bitmap,
115+
self._stamp_source,
116+
x - self._outline_size,
117+
y - self._outline_size,
118+
skip_dest_index=1,
119+
)
120+
except ValueError as value_error:
121+
raise ValueError(
122+
"Padding must be big enough to fit outline_size "
123+
"all the way around the text. "
124+
"Try using either larger padding sizes, or smaller outline_size."
125+
) from value_error
126+
127+
def _place_text(
128+
self,
129+
bitmap: Bitmap,
130+
text: str,
131+
font: FontProtocol,
132+
xposition: int,
133+
yposition: int,
134+
skip_index: int = 0, # set to None to write all pixels, other wise skip this palette index
135+
# when copying glyph bitmaps (this is important for slanted text
136+
# where rectangular glyph boxes overlap)
137+
) -> Tuple[int, int, int, int]:
138+
"""
139+
Copy the glpyphs that represent the value of the string into the labels Bitmap.
140+
:param bitmap: The bitmap to place text into
141+
:param text: The text to render
142+
:param font: The font to render the text in
143+
:param xposition: x location of the starting point within the bitmap
144+
:param yposition: y location of the starting point within the bitmap
145+
:param skip_index: Color index to skip during rendering instead of covering up
146+
:return Tuple bounding_box: tuple with x, y, width, height values of the bitmap
147+
"""
148+
parent_result = super()._place_text(
149+
bitmap, text, font, xposition, yposition, skip_index=skip_index
150+
)
151+
152+
self._add_outline()
153+
154+
return parent_result
155+
156+
@property
157+
def outline_color(self):
158+
"""Color of the outline to draw around the text."""
159+
return self._palette[2]
160+
161+
@outline_color.setter
162+
def outline_color(self, new_outline_color):
163+
self._palette[2] = new_outline_color
164+
165+
@property
166+
def outline_size(self):
167+
"""Stroke size of the outline to draw around the text."""
168+
return self._outline_size
169+
170+
@outline_size.setter
171+
def outline_size(self, new_outline_size):
172+
self._outline_size = new_outline_size
173+
174+
self._padding_top = new_outline_size + 0
175+
self._padding_bottom = new_outline_size + 2
176+
self._padding_left = new_outline_size + 0
177+
self._padding_right = new_outline_size + 0
178+
179+
self._stamp_source = Bitmap(
180+
(new_outline_size * 2) + 1, (new_outline_size * 2) + 1, 3
181+
)
182+
self._stamp_source.fill(2)
183+
self._reset_text(
184+
font=self._font,
185+
text=self._text,
186+
line_spacing=self._line_spacing,
187+
scale=self.scale,
188+
)

docs/api.rst

+3
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@
1616

1717
.. automodule:: adafruit_display_text.scrolling_label
1818
:members:
19+
20+
.. automodule:: adafruit_display_text.outlined_label
21+
:members:

docs/examples.rst

+9
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ Simple test using scrolling_label to display text
2525
:caption: examples/display_text_scrolling_label.py
2626
:linenos:
2727

28+
OutlinedLabel Simple Test
29+
-------------------------
30+
31+
Simple test using outlined_label to display text with a stroke outline
32+
33+
.. literalinclude:: ../examples/display_text_outlined_label_simpletest.py
34+
:caption: examples/display_text_outlined_label_simpletest.py
35+
:linenos:
36+
2837
Label vs Bitmap_label Comparison
2938
--------------------------------
3039

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# SPDX-FileCopyrightText: 2023 Tim C
2+
# SPDX-License-Identifier: MIT
3+
4+
import board
5+
import terminalio
6+
from adafruit_display_text import outlined_label
7+
8+
if board.DISPLAY.width <= 150:
9+
text = "Hello\nworld"
10+
else:
11+
text = "Hello world"
12+
13+
text_area = outlined_label.OutlinedLabel(
14+
terminalio.FONT,
15+
text=text,
16+
color=0xFF00FF,
17+
outline_color=0x00FF00,
18+
outline_size=1,
19+
padding_left=2,
20+
padding_right=2,
21+
padding_top=2,
22+
padding_bottom=2,
23+
scale=3,
24+
)
25+
text_area.anchor_point = (0, 0)
26+
text_area.anchored_position = (10, 10)
27+
board.DISPLAY.root_group = text_area
28+
while True:
29+
pass

0 commit comments

Comments
 (0)