Skip to content

Outlined label #203

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 188 additions & 0 deletions adafruit_display_text/outlined_label.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
# SPDX-FileCopyrightText: 2023 Tim C
#
# SPDX-License-Identifier: MIT

"""
`adafruit_display_text.outlined_label`
====================================================

Subclass of BitmapLabel that adds outline color and stroke size
functionalities.

* Author(s): Tim Cocks

Implementation Notes
--------------------

**Hardware:**

**Software and Dependencies:**

* Adafruit CircuitPython firmware for the supported boards:
https://circuitpython.org/downloads

"""
__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Display_Text.git"

import bitmaptools
from displayio import Palette, Bitmap
from adafruit_display_text import bitmap_label

try:
from typing import Optional, Tuple, Union
from fontio import FontProtocol
except ImportError:
pass


class OutlinedLabel(bitmap_label.Label):
"""
OutlinedLabel - A BitmapLabel subclass that includes arguments and properties for specifying
outline_size and outline_color to get drawn as a stroke around the text.

:param Union[Tuple, int] outline_color: The color of the outline stroke as RGB tuple, or hex.
:param int outline_size: The size in pixels of the outline stroke.

"""

# pylint: disable=too-many-arguments
def __init__(
self,
font,
outline_color: Union[int, Tuple] = 0x999999,
outline_size: int = 1,
padding_top: Optional[int] = None,
padding_bottom: Optional[int] = None,
padding_left: Optional[int] = None,
padding_right: Optional[int] = None,
**kwargs
):
if padding_top is None:
padding_top = outline_size + 0
if padding_bottom is None:
padding_bottom = outline_size + 2
if padding_left is None:
padding_left = outline_size + 0
if padding_right is None:
padding_right = outline_size + 0

super().__init__(
font,
padding_top=padding_top,
padding_bottom=padding_bottom,
padding_left=padding_left,
padding_right=padding_right,
**kwargs
)

_background_color = self._palette[0]
_foreground_color = self._palette[1]
_background_is_transparent = self._palette.is_transparent(0)
self._palette = Palette(3)
self._palette[0] = _background_color
self._palette[1] = _foreground_color
self._palette[2] = outline_color
if _background_is_transparent:
self._palette.make_transparent(0)

self._outline_size = outline_size
self._stamp_source = Bitmap((outline_size * 2) + 1, (outline_size * 2) + 1, 3)
self._stamp_source.fill(2)

self._bitmap = None

self._reset_text(
font=font,
text=self._text,
line_spacing=self._line_spacing,
scale=self.scale,
)

def _add_outline(self):
"""
Blit the outline into the labels Bitmap. We will stamp self._stamp_source for each
pixel of the foreground color but skip the foreground color when we blit.
:return: None
"""
if hasattr(self, "_stamp_source"):
for y in range(self.bitmap.height):
for x in range(self.bitmap.width):
if self.bitmap[x, y] == 1:
try:
bitmaptools.blit(
self.bitmap,
self._stamp_source,
x - self._outline_size,
y - self._outline_size,
skip_dest_index=1,
)
except ValueError as value_error:
raise ValueError(
"Padding must be big enough to fit outline_size "
"all the way around the text. "
"Try using either larger padding sizes, or smaller outline_size."
) from value_error

def _place_text(
self,
bitmap: Bitmap,
text: str,
font: FontProtocol,
xposition: int,
yposition: int,
skip_index: int = 0, # set to None to write all pixels, other wise skip this palette index
# when copying glyph bitmaps (this is important for slanted text
# where rectangular glyph boxes overlap)
) -> Tuple[int, int, int, int]:
"""
Copy the glpyphs that represent the value of the string into the labels Bitmap.
:param bitmap: The bitmap to place text into
:param text: The text to render
:param font: The font to render the text in
:param xposition: x location of the starting point within the bitmap
:param yposition: y location of the starting point within the bitmap
:param skip_index: Color index to skip during rendering instead of covering up
:return Tuple bounding_box: tuple with x, y, width, height values of the bitmap
"""
parent_result = super()._place_text(
bitmap, text, font, xposition, yposition, skip_index=skip_index
)

self._add_outline()

return parent_result

@property
def outline_color(self):
"""Color of the outline to draw around the text."""
return self._palette[2]

@outline_color.setter
def outline_color(self, new_outline_color):
self._palette[2] = new_outline_color

@property
def outline_size(self):
"""Stroke size of the outline to draw around the text."""
return self._outline_size

@outline_size.setter
def outline_size(self, new_outline_size):
self._outline_size = new_outline_size

self._padding_top = new_outline_size + 0
self._padding_bottom = new_outline_size + 2
self._padding_left = new_outline_size + 0
self._padding_right = new_outline_size + 0

self._stamp_source = Bitmap(
(new_outline_size * 2) + 1, (new_outline_size * 2) + 1, 3
)
self._stamp_source.fill(2)
self._reset_text(
font=self._font,
text=self._text,
line_spacing=self._line_spacing,
scale=self.scale,
)
3 changes: 3 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@

.. automodule:: adafruit_display_text.scrolling_label
:members:

.. automodule:: adafruit_display_text.outlined_label
:members:
9 changes: 9 additions & 0 deletions docs/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ Simple test using scrolling_label to display text
:caption: examples/display_text_scrolling_label.py
:linenos:

OutlinedLabel Simple Test
-------------------------

Simple test using outlined_label to display text with a stroke outline

.. literalinclude:: ../examples/display_text_outlined_label_simpletest.py
:caption: examples/display_text_outlined_label_simpletest.py
:linenos:

Label vs Bitmap_label Comparison
--------------------------------

Expand Down
29 changes: 29 additions & 0 deletions examples/display_text_outlined_label_simpletest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# SPDX-FileCopyrightText: 2023 Tim C
# SPDX-License-Identifier: MIT

import board
import terminalio
from adafruit_display_text import outlined_label

if board.DISPLAY.width <= 150:
text = "Hello\nworld"
else:
text = "Hello world"

text_area = outlined_label.OutlinedLabel(
terminalio.FONT,
text=text,
color=0xFF00FF,
outline_color=0x00FF00,
outline_size=1,
padding_left=2,
padding_right=2,
padding_top=2,
padding_bottom=2,
scale=3,
)
text_area.anchor_point = (0, 0)
text_area.anchored_position = (10, 10)
board.DISPLAY.root_group = text_area
while True:
pass