Animated Message Board Working
This commit is contained in:
parent
b5eb25620a
commit
3ca8f9a715
26 changed files with 21497 additions and 0 deletions
23
Matrix_Portal_S3_Message_Board/code.py
Executable file
23
Matrix_Portal_S3_Message_Board/code.py
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import time
|
||||
from adafruit_matrixportal.matrix import Matrix
|
||||
from messageboard import MessageBoard
|
||||
from messageboard.fontpool import FontPool
|
||||
from messageboard.message import Message
|
||||
|
||||
matrix = Matrix(width=128, height=16, bit_depth=5)
|
||||
messageboard = MessageBoard(matrix)
|
||||
messageboard.set_background("images/background.bmp")
|
||||
fontpool = FontPool()
|
||||
fontpool.add_font("arial", "fonts/Arial-10.pcf")
|
||||
|
||||
while True:
|
||||
message = Message(fontpool.find_font("arial"), mask_color=0xFF00FF, opacity=0.8)
|
||||
message.add_image("images/maskedstar.bmp")
|
||||
message.add_text("Hello World!", color=0xFFFF00, x_offset=2, y_offset=2)
|
||||
messageboard.animate(message, "Scroll", "in_from_right")
|
||||
time.sleep(1)
|
||||
messageboard.animate(message, "Scroll", "out_to_left")
|
||||
81
Matrix_Portal_S3_Message_Board/demo.py
Executable file
81
Matrix_Portal_S3_Message_Board/demo.py
Executable file
|
|
@ -0,0 +1,81 @@
|
|||
# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import time
|
||||
from adafruit_matrixportal.matrix import Matrix
|
||||
from messageboard import MessageBoard
|
||||
from messageboard.fontpool import FontPool
|
||||
from messageboard.message import Message
|
||||
|
||||
matrix = Matrix(width=128, height=16, bit_depth=5)
|
||||
messageboard = MessageBoard(matrix)
|
||||
messageboard.set_background("images/background.bmp")
|
||||
|
||||
fontpool = FontPool()
|
||||
fontpool.add_font("arial", "fonts/Arial-10.pcf")
|
||||
fontpool.add_font("comic", "fonts/Comic-10.pcf")
|
||||
fontpool.add_font("dejavu", "fonts/DejaVuSans-10.pcf")
|
||||
|
||||
message = Message(fontpool.find_font("terminal"), opacity=0.8)
|
||||
message.add_image("images/maskedstar.bmp")
|
||||
message.add_text("Hello World!", color=0xFFFF00, x_offset=2, y_offset=2)
|
||||
|
||||
message1 = Message(fontpool.find_font("dejavu"))
|
||||
|
||||
message2 = Message(fontpool.find_font("comic"), mask_color=0x00FF00)
|
||||
print("add blinka")
|
||||
message2.add_image("images/maskedblinka.bmp")
|
||||
print("add text")
|
||||
message2.add_text("CircuitPython", color=0xFFFF00, y_offset=-2)
|
||||
|
||||
message3 = Message(fontpool.find_font("dejavu"))
|
||||
message3.add_text("circuitpython.com", color=0xFF0000)
|
||||
|
||||
message4 = Message(fontpool.find_font("arial"))
|
||||
message4.add_text("Buy Electronics", color=0xFFFFFF)
|
||||
|
||||
while True:
|
||||
message1.clear()
|
||||
message1.add_text("Scroll Text In", color=0xFF0000)
|
||||
|
||||
messageboard.animate(message1, "Scroll", "in_from_left")
|
||||
time.sleep(1)
|
||||
message1.clear()
|
||||
message1.add_text("Change Messages")
|
||||
messageboard.animate(message1, "Static", "show")
|
||||
time.sleep(1)
|
||||
message1.clear()
|
||||
message1.add_text("And Scroll Out")
|
||||
|
||||
messageboard.animate(message1, "Static", "show")
|
||||
messageboard.animate(message1, "Scroll", "out_to_right")
|
||||
time.sleep(1)
|
||||
|
||||
message1.clear()
|
||||
message1.add_text("Or more effects like looping ", color=0xFFFF00)
|
||||
messageboard.animate(
|
||||
message1, "Split", "in_vertically"
|
||||
) # Split never completely joins
|
||||
messageboard.animate(
|
||||
message1, "Loop", "left"
|
||||
) # Text too high (probably from split)
|
||||
messageboard.animate(
|
||||
message1, "Static", "flash", count=3
|
||||
) # Flashes in weird positions
|
||||
|
||||
messageboard.animate(message1, "Split", "out_vertically")
|
||||
time.sleep(1)
|
||||
|
||||
messageboard.animate(message2, "Static", "fade_in")
|
||||
time.sleep(1)
|
||||
messageboard.animate(message2, "Static", "fade_out")
|
||||
|
||||
messageboard.set_background(0x00FF00)
|
||||
messageboard.animate(message3, "Scroll", "in_from_top")
|
||||
time.sleep(1)
|
||||
messageboard.animate(message3, "Scroll", "out_to_bottom")
|
||||
messageboard.set_background("images/background.bmp")
|
||||
|
||||
messageboard.animate(message4, "Scroll", "in_from_right")
|
||||
time.sleep(1)
|
||||
BIN
Matrix_Portal_S3_Message_Board/fonts/Arial-10.pcf
Executable file
BIN
Matrix_Portal_S3_Message_Board/fonts/Arial-10.pcf
Executable file
Binary file not shown.
BIN
Matrix_Portal_S3_Message_Board/fonts/Arial-12.pcf
Executable file
BIN
Matrix_Portal_S3_Message_Board/fonts/Arial-12.pcf
Executable file
Binary file not shown.
BIN
Matrix_Portal_S3_Message_Board/fonts/Arial-14.pcf
Executable file
BIN
Matrix_Portal_S3_Message_Board/fonts/Arial-14.pcf
Executable file
Binary file not shown.
20343
Matrix_Portal_S3_Message_Board/fonts/Arial-8.bdf
Executable file
20343
Matrix_Portal_S3_Message_Board/fonts/Arial-8.bdf
Executable file
File diff suppressed because it is too large
Load diff
BIN
Matrix_Portal_S3_Message_Board/fonts/Comic-10.pcf
Executable file
BIN
Matrix_Portal_S3_Message_Board/fonts/Comic-10.pcf
Executable file
Binary file not shown.
BIN
Matrix_Portal_S3_Message_Board/fonts/Comic-12.pcf
Executable file
BIN
Matrix_Portal_S3_Message_Board/fonts/Comic-12.pcf
Executable file
Binary file not shown.
BIN
Matrix_Portal_S3_Message_Board/fonts/Comic-14.pcf
Executable file
BIN
Matrix_Portal_S3_Message_Board/fonts/Comic-14.pcf
Executable file
Binary file not shown.
BIN
Matrix_Portal_S3_Message_Board/fonts/DejaVuSans-10.pcf
Executable file
BIN
Matrix_Portal_S3_Message_Board/fonts/DejaVuSans-10.pcf
Executable file
Binary file not shown.
BIN
Matrix_Portal_S3_Message_Board/fonts/DejaVuSans-12.pcf
Executable file
BIN
Matrix_Portal_S3_Message_Board/fonts/DejaVuSans-12.pcf
Executable file
Binary file not shown.
BIN
Matrix_Portal_S3_Message_Board/fonts/DejaVuSans-14.pcf
Executable file
BIN
Matrix_Portal_S3_Message_Board/fonts/DejaVuSans-14.pcf
Executable file
Binary file not shown.
BIN
Matrix_Portal_S3_Message_Board/images/adafruit_star.bmp
Executable file
BIN
Matrix_Portal_S3_Message_Board/images/adafruit_star.bmp
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 824 B |
BIN
Matrix_Portal_S3_Message_Board/images/background.bmp
Executable file
BIN
Matrix_Portal_S3_Message_Board/images/background.bmp
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 9.1 KiB |
BIN
Matrix_Portal_S3_Message_Board/images/logo.bmp
Executable file
BIN
Matrix_Portal_S3_Message_Board/images/logo.bmp
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 696 B |
BIN
Matrix_Portal_S3_Message_Board/images/maskedblinka.bmp
Executable file
BIN
Matrix_Portal_S3_Message_Board/images/maskedblinka.bmp
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 824 B |
BIN
Matrix_Portal_S3_Message_Board/images/maskedstar.bmp
Executable file
BIN
Matrix_Portal_S3_Message_Board/images/maskedstar.bmp
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 824 B |
158
Matrix_Portal_S3_Message_Board/lib/messageboard/__init__.py
Executable file
158
Matrix_Portal_S3_Message_Board/lib/messageboard/__init__.py
Executable file
|
|
@ -0,0 +1,158 @@
|
|||
# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import bitmaptools
|
||||
import displayio
|
||||
import adafruit_imageload
|
||||
from .doublebuffer import DoubleBuffer
|
||||
from .message import Message
|
||||
|
||||
|
||||
class MessageBoard:
|
||||
def __init__(self, matrix):
|
||||
self.fonts = {}
|
||||
self.display = matrix.display
|
||||
self._buffer_width = self.display.width * 2
|
||||
self._buffer_height = self.display.height * 2
|
||||
self._dbl_buf = DoubleBuffer(
|
||||
self.display, self._buffer_width, self._buffer_height
|
||||
)
|
||||
self._background = None
|
||||
self.set_background() # Set to black
|
||||
self._position = (0, 0)
|
||||
|
||||
def set_background(self, file_or_color=0x000000):
|
||||
"""The background image to a bitmap file."""
|
||||
if isinstance(file_or_color, str): # its a filenme:
|
||||
background, bg_shader = adafruit_imageload.load(file_or_color)
|
||||
self._dbl_buf.shader = bg_shader
|
||||
self._background = background
|
||||
elif isinstance(file_or_color, int):
|
||||
# Make a background color fill
|
||||
bg_shader = displayio.ColorConverter(
|
||||
input_colorspace=displayio.Colorspace.RGB565
|
||||
)
|
||||
background = displayio.Bitmap(
|
||||
self.display.width, self.display.height, 65535
|
||||
)
|
||||
background.fill(displayio.ColorConverter().convert(file_or_color))
|
||||
self._dbl_buf.shader = bg_shader
|
||||
self._background = background
|
||||
else:
|
||||
raise RuntimeError("Unknown type of background")
|
||||
|
||||
def animate(self, message, animation_class, animation_function, **kwargs):
|
||||
anim_class = __import__(
|
||||
f"{self.__module__}.animations.{animation_class.lower()}"
|
||||
)
|
||||
anim_class = getattr(anim_class, "animations")
|
||||
anim_class = getattr(anim_class, animation_class.lower())
|
||||
anim_class = getattr(anim_class, animation_class)
|
||||
animation = anim_class(
|
||||
self.display, self._draw, self._position
|
||||
) # Instantiate the class
|
||||
# Call the animation function and pass kwargs along with the message (positional)
|
||||
anim_func = getattr(animation, animation_function)
|
||||
anim_func(message, **kwargs)
|
||||
|
||||
def _draw(
|
||||
self,
|
||||
image,
|
||||
x,
|
||||
y,
|
||||
opacity=None,
|
||||
mask_color=0xFF00FF,
|
||||
blendmode=bitmaptools.BlendMode.Normal,
|
||||
post_draw_position=None,
|
||||
):
|
||||
"""Draws a message to the buffer taking its current settings into account.
|
||||
It also sets the current position and performs a swap.
|
||||
"""
|
||||
self._position = (x, y)
|
||||
buffer_x_offset = self._buffer_width - self.display.width
|
||||
buffer_y_offset = self._buffer_height - self.display.height
|
||||
|
||||
# Image can be a message in which case its properties will be used
|
||||
if isinstance(image, Message):
|
||||
if opacity is None:
|
||||
opacity = image.opacity
|
||||
mask_color = image.mask_color
|
||||
blendmode = image.blendmode
|
||||
image = image.buffer
|
||||
if opacity is None:
|
||||
opacity = 1.0
|
||||
|
||||
if mask_color > 65535:
|
||||
mask_color = displayio.ColorConverter().convert(mask_color)
|
||||
|
||||
# Blit the background
|
||||
bitmaptools.blit(
|
||||
self._dbl_buf.active_buffer,
|
||||
self._background,
|
||||
buffer_x_offset,
|
||||
buffer_y_offset,
|
||||
)
|
||||
|
||||
# If the image is wider than the display buffer, we need to shrink it
|
||||
if x + buffer_x_offset < 0:
|
||||
new_image = displayio.Bitmap(
|
||||
image.width - self.display.width, image.height, 65535
|
||||
)
|
||||
bitmaptools.blit(
|
||||
new_image,
|
||||
image,
|
||||
0,
|
||||
0,
|
||||
x1=self.display.width,
|
||||
y1=0,
|
||||
x2=image.width,
|
||||
y2=image.height,
|
||||
)
|
||||
x += self.display.width
|
||||
image = new_image
|
||||
|
||||
# If the image is taller than the display buffer, we need to shrink it
|
||||
if y + buffer_y_offset < 0:
|
||||
new_image = displayio.Bitmap(
|
||||
image.width, image.height - self.display.height, 65535
|
||||
)
|
||||
bitmaptools.blit(
|
||||
new_image,
|
||||
image,
|
||||
0,
|
||||
0,
|
||||
x1=0,
|
||||
y1=self.display.height,
|
||||
x2=image.width,
|
||||
y2=image.height,
|
||||
)
|
||||
y += self.display.height
|
||||
image = new_image
|
||||
|
||||
# Clear the foreground buffer
|
||||
foreground_buffer = displayio.Bitmap(
|
||||
self._buffer_width, self._buffer_height, 65535
|
||||
)
|
||||
foreground_buffer.fill(mask_color)
|
||||
|
||||
bitmaptools.blit(
|
||||
foreground_buffer, image, x + buffer_x_offset, y + buffer_y_offset
|
||||
)
|
||||
|
||||
# Blend the foreground buffer into the main buffer
|
||||
bitmaptools.alphablend(
|
||||
self._dbl_buf.active_buffer,
|
||||
self._dbl_buf.active_buffer,
|
||||
foreground_buffer,
|
||||
displayio.Colorspace.RGB565,
|
||||
1.0,
|
||||
opacity,
|
||||
blendmode=blendmode,
|
||||
skip_source2_index=mask_color,
|
||||
)
|
||||
self._dbl_buf.show()
|
||||
|
||||
# Allow for an override of the position after drawing (needed for split effects)
|
||||
if post_draw_position is not None and isinstance(post_draw_position, tuple):
|
||||
self._position = post_draw_position
|
||||
24
Matrix_Portal_S3_Message_Board/lib/messageboard/animations/__init__.py
Executable file
24
Matrix_Portal_S3_Message_Board/lib/messageboard/animations/__init__.py
Executable file
|
|
@ -0,0 +1,24 @@
|
|||
# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import time
|
||||
|
||||
|
||||
class Animation:
|
||||
def __init__(self, display, draw_callback, starting_position=(0, 0)):
|
||||
self._display = display
|
||||
self._position = starting_position
|
||||
self._draw = draw_callback
|
||||
|
||||
@staticmethod
|
||||
def _wait(start_time, duration):
|
||||
"""Uses time.monotonic() to wait from the start time for a specified duration"""
|
||||
while time.monotonic() < (start_time + duration):
|
||||
pass
|
||||
return time.monotonic()
|
||||
|
||||
def _get_centered_position(self, message):
|
||||
return int(self._display.width / 2 - message.buffer.width / 2), int(
|
||||
self._display.height / 2 - message.buffer.height / 2
|
||||
)
|
||||
146
Matrix_Portal_S3_Message_Board/lib/messageboard/animations/loop.py
Executable file
146
Matrix_Portal_S3_Message_Board/lib/messageboard/animations/loop.py
Executable file
|
|
@ -0,0 +1,146 @@
|
|||
# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import time
|
||||
import displayio
|
||||
import bitmaptools
|
||||
from . import Animation
|
||||
|
||||
|
||||
class Loop(Animation):
|
||||
def _create_loop_image(self, image, x_offset, y_offset, mask_color):
|
||||
"""Attach a copy of an image by a certain offset so it can be looped."""
|
||||
if 0 < x_offset < self._display.width:
|
||||
x_offset = self._display.width
|
||||
if 0 < y_offset < self._display.height:
|
||||
y_offset = self._display.height
|
||||
|
||||
loop_image = displayio.Bitmap(
|
||||
image.width + x_offset, image.height + y_offset, 65535
|
||||
)
|
||||
loop_image.fill(mask_color)
|
||||
bitmaptools.blit(loop_image, image, 0, 0)
|
||||
bitmaptools.blit(loop_image, image, x_offset, y_offset)
|
||||
|
||||
return loop_image
|
||||
|
||||
def left(self, message, duration=1, count=1):
|
||||
"""Loop a message towards the left side of the display over a certain period of time by a
|
||||
certain number of times. The message will re-enter from the right and end up back a the
|
||||
starting position.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float count: (optional) The number of times to loop. (default=1)
|
||||
:param float duration: (optional) The period of time to perform the animation
|
||||
over. (default=1)
|
||||
:type message: Message
|
||||
"""
|
||||
current_x, current_y = self._position
|
||||
distance = max(message.buffer.width, self._display.width)
|
||||
loop_image = self._create_loop_image(
|
||||
message.buffer, distance, 0, message.mask_color
|
||||
)
|
||||
for _ in range(count):
|
||||
for _ in range(distance):
|
||||
start_time = time.monotonic()
|
||||
current_x -= 1
|
||||
if current_x < 0 - message.buffer.width:
|
||||
current_x += distance
|
||||
self._draw(
|
||||
loop_image,
|
||||
current_x,
|
||||
current_y,
|
||||
message.opacity,
|
||||
)
|
||||
self._wait(start_time, duration / distance / count)
|
||||
|
||||
def right(self, message, duration=1, count=1):
|
||||
"""Loop a message towards the right side of the display over a certain period of time by a
|
||||
certain number of times. The message will re-enter from the left and end up back a the
|
||||
starting position.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float count: (optional) The number of times to loop. (default=1)
|
||||
:param float duration: (optional) The period of time to perform the animation
|
||||
over. (default=1)
|
||||
:type message: Message
|
||||
"""
|
||||
current_x, current_y = self._position
|
||||
distance = max(message.buffer.width, self._display.width)
|
||||
loop_image = self._create_loop_image(
|
||||
message.buffer, distance, 0, message.mask_color
|
||||
)
|
||||
for _ in range(count):
|
||||
for _ in range(distance):
|
||||
start_time = time.monotonic()
|
||||
current_x += 1
|
||||
if current_x > 0:
|
||||
current_x -= distance
|
||||
self._draw(
|
||||
loop_image,
|
||||
current_x,
|
||||
current_y,
|
||||
message.opacity,
|
||||
)
|
||||
self._wait(start_time, duration / distance / count)
|
||||
|
||||
def up(self, message, duration=0.5, count=1):
|
||||
"""Loop a message towards the top side of the display over a certain period of time by a
|
||||
certain number of times. The message will re-enter from the bottom and end up back a the
|
||||
starting position.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float count: (optional) The number of times to loop. (default=1)
|
||||
:param float duration: (optional) The period of time to perform the animation
|
||||
over. (default=1)
|
||||
:type message: Message
|
||||
"""
|
||||
current_x, current_y = self._position
|
||||
distance = max(message.buffer.height, self._display.height)
|
||||
loop_image = self._create_loop_image(
|
||||
message.buffer, 0, distance, message.mask_color
|
||||
)
|
||||
for _ in range(count):
|
||||
for _ in range(distance):
|
||||
start_time = time.monotonic()
|
||||
current_y -= 1
|
||||
if current_y < 0 - message.buffer.height:
|
||||
current_y += distance
|
||||
self._draw(
|
||||
loop_image,
|
||||
current_x,
|
||||
current_y,
|
||||
message.opacity,
|
||||
)
|
||||
self._wait(start_time, duration / distance / count)
|
||||
|
||||
def down(self, message, duration=0.5, count=1):
|
||||
"""Loop a message towards the bottom side of the display over a certain period of time by a
|
||||
certain number of times. The message will re-enter from the top and end up back a the
|
||||
starting position.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float count: (optional) The number of times to loop. (default=1)
|
||||
:param float duration: (optional) The period of time to perform the animation
|
||||
over. (default=1)
|
||||
:type message: Message
|
||||
"""
|
||||
current_x, current_y = self._position
|
||||
distance = max(message.buffer.height, self._display.height)
|
||||
loop_image = self._create_loop_image(
|
||||
message.buffer, 0, distance, message.mask_color
|
||||
)
|
||||
for _ in range(count):
|
||||
for _ in range(distance):
|
||||
start_time = time.monotonic()
|
||||
current_y += 1
|
||||
if current_y > 0:
|
||||
current_y -= distance
|
||||
self._draw(
|
||||
loop_image,
|
||||
current_x,
|
||||
current_y,
|
||||
message.opacity,
|
||||
)
|
||||
self._wait(start_time, duration / distance / count)
|
||||
170
Matrix_Portal_S3_Message_Board/lib/messageboard/animations/scroll.py
Executable file
170
Matrix_Portal_S3_Message_Board/lib/messageboard/animations/scroll.py
Executable file
|
|
@ -0,0 +1,170 @@
|
|||
# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import time
|
||||
from . import Animation
|
||||
|
||||
|
||||
class Scroll(Animation):
|
||||
def scroll_from_to(self, message, duration, start_x, start_y, end_x, end_y):
|
||||
"""
|
||||
Scroll the message from one position to another over a certain period of
|
||||
time.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float duration: The period of time to perform the animation over in seconds.
|
||||
:param int start_x: The Starting X Position
|
||||
:param int start_yx: The Starting Y Position
|
||||
:param int end_x: The Ending X Position
|
||||
:param int end_y: The Ending Y Position
|
||||
:type message: Message
|
||||
"""
|
||||
steps = max(abs(end_x - start_x), abs(end_y - start_y))
|
||||
if not steps:
|
||||
return
|
||||
increment_x = (end_x - start_x) / steps
|
||||
increment_y = (end_y - start_y) / steps
|
||||
for i in range(steps + 1):
|
||||
start_time = time.monotonic()
|
||||
current_x = start_x + round(i * increment_x)
|
||||
current_y = start_y + round(i * increment_y)
|
||||
self._draw(message, current_x, current_y)
|
||||
if i <= steps:
|
||||
self._wait(start_time, duration / steps)
|
||||
|
||||
def out_to_left(self, message, duration=1):
|
||||
"""Scroll a message off the display from its current position towards the left
|
||||
over a certain period of time.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float duration: (optional) The period of time to perform the animation
|
||||
over in seconds. (default=1)
|
||||
:type message: Message
|
||||
"""
|
||||
current_x, current_y = self._position
|
||||
self.scroll_from_to(
|
||||
message, duration, current_x, current_y, 0 - message.buffer.width, current_y
|
||||
)
|
||||
|
||||
def in_from_left(self, message, duration=1, x=0):
|
||||
"""Scroll a message in from the left side of the display over a certain period of
|
||||
time. The final position is centered.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float duration: (optional) The period of time to perform the animation
|
||||
over in seconds. (default=1)
|
||||
:param int x: (optional) The amount of x-offset from the center position (default=0)
|
||||
:type message: Message
|
||||
"""
|
||||
center_x, center_y = self._get_centered_position(message)
|
||||
self.scroll_from_to(
|
||||
message,
|
||||
duration,
|
||||
0 - message.buffer.width,
|
||||
center_y,
|
||||
center_x + x,
|
||||
center_y,
|
||||
)
|
||||
|
||||
def in_from_right(self, message, duration=1, x=0):
|
||||
"""Scroll a message in from the right side of the display over a certain period of
|
||||
time. The final position is centered.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float duration: (optional) The period of time to perform the animation
|
||||
over in seconds. (default=1)
|
||||
:param int x: (optional) The amount of x-offset from the center position (default=0)
|
||||
:type message: Message
|
||||
"""
|
||||
center_x, center_y = self._get_centered_position(message)
|
||||
self.scroll_from_to(
|
||||
message, duration, self._display.width - 1, center_y, center_x + x, center_y
|
||||
)
|
||||
|
||||
def in_from_top(self, message, duration=1, y=0):
|
||||
"""Scroll a message in from the top side of the display over a certain period of
|
||||
time. The final position is centered.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float duration: (optional) The period of time to perform the animation
|
||||
over in seconds. (default=1)
|
||||
:param int y: (optional) The amount of y-offset from the center position (default=0)
|
||||
:type message: Message
|
||||
"""
|
||||
center_x, center_y = self._get_centered_position(message)
|
||||
self.scroll_from_to(
|
||||
message,
|
||||
duration,
|
||||
center_x,
|
||||
0 - message.buffer.height,
|
||||
center_x,
|
||||
center_y + y,
|
||||
)
|
||||
|
||||
def in_from_bottom(self, message, duration=1, y=0):
|
||||
"""Scroll a message in from the bottom side of the display over a certain period of
|
||||
time. The final position is centered.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float duration: (optional) The period of time to perform the animation
|
||||
over in seconds. (default=1)
|
||||
:param int y: (optional) The amount of y-offset from the center position (default=0)
|
||||
:type message: Message
|
||||
"""
|
||||
center_x, center_y = self._get_centered_position(message)
|
||||
self.scroll_from_to(
|
||||
message,
|
||||
duration,
|
||||
center_x,
|
||||
self._display.height - 1,
|
||||
center_x,
|
||||
center_y + y,
|
||||
)
|
||||
|
||||
def out_to_right(self, message, duration=1):
|
||||
"""Scroll a message off the display from its current position towards the right
|
||||
over a certain period of time.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float duration: (optional) The period of time to perform the animation
|
||||
over in seconds. (default=1)
|
||||
:type message: Message
|
||||
"""
|
||||
current_x, current_y = self._position
|
||||
self.scroll_from_to(
|
||||
message, duration, current_x, current_y, self._display.width - 1, current_y
|
||||
)
|
||||
|
||||
def out_to_top(self, message, duration=1):
|
||||
"""Scroll a message off the display from its current position towards the top
|
||||
over a certain period of time.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float duration: (optional) The period of time to perform the animation
|
||||
over in seconds. (default=1)
|
||||
:type message: Message
|
||||
"""
|
||||
current_x, current_y = self._position
|
||||
self.scroll_from_to(
|
||||
message,
|
||||
duration,
|
||||
current_x,
|
||||
current_y,
|
||||
current_x,
|
||||
0 - message.buffer.height,
|
||||
)
|
||||
|
||||
def out_to_bottom(self, message, duration=1):
|
||||
"""Scroll a message off the display from its current position towards the bottom
|
||||
over a certain period of time.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float duration: (optional) The period of time to perform the animation
|
||||
over in seconds. (default=1)
|
||||
:type message: Message
|
||||
"""
|
||||
current_x, current_y = self._position
|
||||
self.scroll_from_to(
|
||||
message, duration, current_x, current_y, current_x, self._display.height - 1
|
||||
)
|
||||
222
Matrix_Portal_S3_Message_Board/lib/messageboard/animations/split.py
Executable file
222
Matrix_Portal_S3_Message_Board/lib/messageboard/animations/split.py
Executable file
|
|
@ -0,0 +1,222 @@
|
|||
# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import time
|
||||
import displayio
|
||||
import bitmaptools
|
||||
from . import Animation
|
||||
|
||||
|
||||
class Split(Animation):
|
||||
def out_horizontally(self, message, duration=0.5):
|
||||
"""Show the effect of a message splitting horizontally
|
||||
over a certain period of time.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float duration: (optional) The period of time to perform the animation
|
||||
over. (default=0.5)
|
||||
:type message: Message
|
||||
"""
|
||||
current_x, current_y = self._position
|
||||
image = message.buffer
|
||||
left_image = displayio.Bitmap(image.width // 2, image.height, 65535)
|
||||
bitmaptools.blit(
|
||||
left_image, image, 0, 0, x1=0, y1=0, x2=image.width // 2, y2=image.height
|
||||
)
|
||||
|
||||
right_image = displayio.Bitmap(image.width // 2, image.height, 65535)
|
||||
bitmaptools.blit(
|
||||
right_image,
|
||||
image,
|
||||
0,
|
||||
0,
|
||||
x1=image.width // 2,
|
||||
y1=0,
|
||||
x2=image.width,
|
||||
y2=image.height,
|
||||
)
|
||||
|
||||
distance = self._display.width // 2
|
||||
for i in range(distance + 1):
|
||||
start_time = time.monotonic()
|
||||
effect_buffer = displayio.Bitmap(
|
||||
self._display.width + image.width, image.height, 65535
|
||||
)
|
||||
effect_buffer.fill(message.mask_color)
|
||||
bitmaptools.blit(effect_buffer, left_image, distance - i, 0)
|
||||
bitmaptools.blit(
|
||||
effect_buffer, right_image, distance + image.width // 2 + i, 0
|
||||
)
|
||||
|
||||
self._draw(
|
||||
effect_buffer,
|
||||
current_x - self._display.width // 2,
|
||||
current_y,
|
||||
message.opacity,
|
||||
post_draw_position=(current_x - self._display.width // 2, current_y),
|
||||
)
|
||||
self._wait(start_time, duration / distance)
|
||||
|
||||
def out_vertically(self, message, duration=0.5):
|
||||
"""Show the effect of a message splitting vertically
|
||||
over a certain period of time.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float duration: (optional) The period of time to perform the animation
|
||||
over. (default=0.5)
|
||||
:type message: Message
|
||||
"""
|
||||
current_x, current_y = self._position
|
||||
image = message.buffer
|
||||
|
||||
top_image = displayio.Bitmap(image.width, image.height // 2, 65535)
|
||||
bitmaptools.blit(
|
||||
top_image, image, 0, 0, x1=0, y1=0, x2=image.width, y2=image.height // 2
|
||||
)
|
||||
|
||||
bottom_image = displayio.Bitmap(image.width, image.height // 2, 65535)
|
||||
bitmaptools.blit(
|
||||
bottom_image,
|
||||
image,
|
||||
0,
|
||||
0,
|
||||
x1=0,
|
||||
y1=image.height // 2,
|
||||
x2=image.width,
|
||||
y2=image.height,
|
||||
)
|
||||
|
||||
distance = self._display.height // 2
|
||||
effect_buffer_width = self._display.width
|
||||
if current_x < 0:
|
||||
effect_buffer_width -= current_x
|
||||
for i in range(distance + 1):
|
||||
start_time = time.monotonic()
|
||||
effect_buffer = displayio.Bitmap(
|
||||
effect_buffer_width, self._display.height + image.height, 65535
|
||||
)
|
||||
effect_buffer.fill(message.mask_color)
|
||||
bitmaptools.blit(effect_buffer, top_image, 0, distance - i)
|
||||
bitmaptools.blit(
|
||||
effect_buffer, bottom_image, 0, distance + image.height // 2 + i + 1
|
||||
)
|
||||
|
||||
self._draw(
|
||||
effect_buffer,
|
||||
current_x,
|
||||
current_y - self._display.height // 2,
|
||||
message.opacity,
|
||||
post_draw_position=(current_x, current_y - self._display.height // 2),
|
||||
)
|
||||
self._wait(start_time, duration / distance)
|
||||
|
||||
def in_horizontally(self, message, duration=0.5):
|
||||
"""Show the effect of a split message joining horizontally
|
||||
over a certain period of time.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float duration: (optional) The period of time to perform the animation
|
||||
over. (default=0.5)
|
||||
:type message: Message
|
||||
"""
|
||||
current_x = int(self._display.width / 2 - message.buffer.width / 2)
|
||||
current_y = int(self._display.height / 2 - message.buffer.height / 2)
|
||||
image = message.buffer
|
||||
left_image = displayio.Bitmap(image.width // 2, image.height, 65535)
|
||||
bitmaptools.blit(
|
||||
left_image, image, 0, 0, x1=0, y1=0, x2=image.width // 2, y2=image.height
|
||||
)
|
||||
|
||||
right_image = displayio.Bitmap(image.width // 2, image.height, 65535)
|
||||
bitmaptools.blit(
|
||||
right_image,
|
||||
image,
|
||||
0,
|
||||
0,
|
||||
x1=image.width // 2,
|
||||
y1=0,
|
||||
x2=image.width,
|
||||
y2=image.height,
|
||||
)
|
||||
|
||||
distance = self._display.width // 2
|
||||
effect_buffer = displayio.Bitmap(
|
||||
self._display.width + image.width, image.height, 65535
|
||||
)
|
||||
effect_buffer.fill(message.mask_color)
|
||||
for i in range(distance + 1):
|
||||
start_time = time.monotonic()
|
||||
bitmaptools.blit(effect_buffer, left_image, i, 0)
|
||||
bitmaptools.blit(
|
||||
effect_buffer,
|
||||
right_image,
|
||||
self._display.width + image.width // 2 - i + 1,
|
||||
0,
|
||||
)
|
||||
self._draw(
|
||||
effect_buffer,
|
||||
current_x - self._display.width // 2,
|
||||
current_y,
|
||||
message.opacity,
|
||||
post_draw_position=(current_x, current_y),
|
||||
)
|
||||
self._wait(start_time, duration / distance)
|
||||
|
||||
def in_vertically(self, message, duration=0.5):
|
||||
"""Show the effect of a split message joining vertically
|
||||
over a certain period of time.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float duration: (optional) The period of time to perform the animation
|
||||
over. (default=0.5)
|
||||
:type message: Message
|
||||
"""
|
||||
current_x = int(self._display.width / 2 - message.buffer.width / 2)
|
||||
current_y = int(self._display.height / 2 - message.buffer.height / 2)
|
||||
|
||||
image = message.buffer
|
||||
top_image = displayio.Bitmap(image.width, image.height // 2, 65535)
|
||||
bitmaptools.blit(
|
||||
top_image, image, 0, 0, x1=0, y1=0, x2=image.width, y2=image.height // 2
|
||||
)
|
||||
|
||||
bottom_image = displayio.Bitmap(image.width, image.height // 2, 65535)
|
||||
bitmaptools.blit(
|
||||
bottom_image,
|
||||
image,
|
||||
0,
|
||||
0,
|
||||
x1=0,
|
||||
y1=image.height // 2,
|
||||
x2=image.width,
|
||||
y2=image.height,
|
||||
)
|
||||
|
||||
distance = self._display.height // 2
|
||||
effect_buffer_width = self._display.width
|
||||
if current_x < 0:
|
||||
effect_buffer_width -= current_x
|
||||
|
||||
effect_buffer = displayio.Bitmap(
|
||||
effect_buffer_width, self._display.height + image.height, 65535
|
||||
)
|
||||
effect_buffer.fill(message.mask_color)
|
||||
for i in range(distance + 1):
|
||||
start_time = time.monotonic()
|
||||
bitmaptools.blit(effect_buffer, top_image, 0, i + 1)
|
||||
bitmaptools.blit(
|
||||
effect_buffer,
|
||||
bottom_image,
|
||||
0,
|
||||
self._display.height + image.height // 2 - i + 1,
|
||||
)
|
||||
|
||||
self._draw(
|
||||
effect_buffer,
|
||||
current_x,
|
||||
current_y - self._display.height // 2,
|
||||
message.opacity,
|
||||
post_draw_position=(current_x, current_y),
|
||||
)
|
||||
self._wait(start_time, duration / distance)
|
||||
101
Matrix_Portal_S3_Message_Board/lib/messageboard/animations/static.py
Executable file
101
Matrix_Portal_S3_Message_Board/lib/messageboard/animations/static.py
Executable file
|
|
@ -0,0 +1,101 @@
|
|||
# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import time
|
||||
from . import Animation
|
||||
|
||||
|
||||
class Static(Animation):
|
||||
def show(self, message):
|
||||
"""Show the message at its current position.
|
||||
|
||||
:param message: The message to show.
|
||||
:type message: Message
|
||||
"""
|
||||
x, y = self._position
|
||||
self._draw(message, x, y)
|
||||
|
||||
def hide(self, message):
|
||||
"""Hide the message at its current position.
|
||||
|
||||
:param message: The message to hide.
|
||||
:type message: Message
|
||||
"""
|
||||
x, y = self._position
|
||||
self._draw(message, x, y, opacity=0)
|
||||
|
||||
def blink(self, message, count=3, duration=1):
|
||||
"""Blink the foreground on and off a centain number of
|
||||
times over a certain period of time.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float count: (optional) The number of times to blink. (default=3)
|
||||
:param float duration: (optional) The period of time to perform the animation
|
||||
over. (default=1)
|
||||
:type message: Message
|
||||
"""
|
||||
delay = duration / count / 2
|
||||
for _ in range(count):
|
||||
start_time = time.monotonic()
|
||||
self.hide(message)
|
||||
start_time = self._wait(start_time, delay)
|
||||
self.show(message)
|
||||
self._wait(start_time, delay)
|
||||
|
||||
def flash(self, message, count=3, duration=1):
|
||||
"""Fade the foreground in and out a centain number of
|
||||
times over a certain period of time.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float count: (optional) The number of times to flash. (default=3)
|
||||
:param float duration: (optional) The period of time to perform the animation
|
||||
over. (default=1)
|
||||
:type message: Message
|
||||
"""
|
||||
delay = duration / count / 2
|
||||
steps = 50 // count
|
||||
for _ in range(count):
|
||||
self.fade_out(message, duration=delay, steps=steps)
|
||||
self.fade_in(message, duration=delay, steps=steps)
|
||||
|
||||
def fade_in(self, message, duration=1, steps=50):
|
||||
"""Fade the foreground in over a certain period of time
|
||||
by a certain number of steps. More steps is smoother, but too high
|
||||
of a number may slow down the animation too much.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float duration: (optional) The period of time to perform the animation
|
||||
over. (default=1)
|
||||
:param float steps: (optional) The number of steps to perform the animation. (default=50)
|
||||
:type message: Message
|
||||
"""
|
||||
current_x = int(self._display.width / 2 - message.buffer.width / 2)
|
||||
current_y = int(self._display.height / 2 - message.buffer.height / 2)
|
||||
delay = duration / (steps + 1)
|
||||
for opacity in range(steps + 1):
|
||||
start_time = time.monotonic()
|
||||
self._draw(message, current_x, current_y, opacity=opacity / steps)
|
||||
self._wait(start_time, delay)
|
||||
|
||||
def fade_out(self, message, duration=1, steps=50):
|
||||
"""Fade the foreground out over a certain period of time
|
||||
by a certain number of steps. More steps is smoother, but too high
|
||||
of a number may slow down the animation too much.
|
||||
|
||||
:param message: The message to animate.
|
||||
:param float duration: (optional) The period of time to perform the animation
|
||||
over. (default=1)
|
||||
:param float steps: (optional) The number of steps to perform the animation. (default=50)
|
||||
:type message: Message
|
||||
"""
|
||||
delay = duration / (steps + 1)
|
||||
for opacity in range(steps + 1):
|
||||
start_time = time.monotonic()
|
||||
self._draw(
|
||||
message,
|
||||
self._position[0],
|
||||
self._position[1],
|
||||
opacity=(steps - opacity) / steps,
|
||||
)
|
||||
self._wait(start_time, delay)
|
||||
58
Matrix_Portal_S3_Message_Board/lib/messageboard/doublebuffer.py
Executable file
58
Matrix_Portal_S3_Message_Board/lib/messageboard/doublebuffer.py
Executable file
|
|
@ -0,0 +1,58 @@
|
|||
# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import displayio
|
||||
|
||||
|
||||
class DoubleBuffer:
|
||||
def __init__(self, display, width, height, shader=None, bit_depth=16):
|
||||
self._buffer_group = (displayio.Group(), displayio.Group())
|
||||
self._buffer = (
|
||||
displayio.Bitmap(width, height, 2**bit_depth - 1),
|
||||
displayio.Bitmap(width, height, 2**bit_depth - 1),
|
||||
)
|
||||
self._x_offset = display.width - width
|
||||
self._y_offset = display.height - height
|
||||
self.display = display
|
||||
self._active_buffer = 0 # The buffer we are updating
|
||||
|
||||
if shader is None:
|
||||
shader = displayio.ColorConverter()
|
||||
|
||||
buffer0_sprite = displayio.TileGrid(
|
||||
self._buffer[0],
|
||||
pixel_shader=shader,
|
||||
x=self._x_offset,
|
||||
y=self._y_offset,
|
||||
)
|
||||
self._buffer_group[0].append(buffer0_sprite)
|
||||
|
||||
buffer1_sprite = displayio.TileGrid(
|
||||
self._buffer[1],
|
||||
pixel_shader=shader,
|
||||
x=self._x_offset,
|
||||
y=self._y_offset,
|
||||
)
|
||||
self._buffer_group[1].append(buffer1_sprite)
|
||||
|
||||
def show(self, swap=True):
|
||||
self.display.show(self._buffer_group[self._active_buffer])
|
||||
if swap:
|
||||
self.swap()
|
||||
|
||||
def swap(self):
|
||||
self._active_buffer = 0 if self._active_buffer else 1
|
||||
|
||||
@property
|
||||
def active_buffer(self):
|
||||
return self._buffer[self._active_buffer]
|
||||
|
||||
@property
|
||||
def shader(self):
|
||||
return self._buffer_group[0][0].pixel_shader
|
||||
|
||||
@shader.setter
|
||||
def shader(self, shader):
|
||||
self._buffer_group[0][0].pixel_shader = shader
|
||||
self._buffer_group[1][0].pixel_shader = shader
|
||||
27
Matrix_Portal_S3_Message_Board/lib/messageboard/fontpool.py
Executable file
27
Matrix_Portal_S3_Message_Board/lib/messageboard/fontpool.py
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import terminalio
|
||||
from adafruit_bitmap_font import bitmap_font
|
||||
|
||||
|
||||
class FontPool:
|
||||
def __init__(self):
|
||||
"""Create a pool of fonts for reuse to avoid loading duplicates"""
|
||||
self._fonts = {}
|
||||
self.add_font("terminal")
|
||||
|
||||
def add_font(self, name, file=None):
|
||||
if name in self._fonts:
|
||||
return
|
||||
if name == "terminal":
|
||||
font = terminalio.FONT
|
||||
else:
|
||||
font = bitmap_font.load_font(file)
|
||||
self._fonts[name] = font
|
||||
|
||||
def find_font(self, name):
|
||||
if name in self._fonts:
|
||||
return self._fonts[name]
|
||||
return None
|
||||
144
Matrix_Portal_S3_Message_Board/lib/messageboard/message.py
Executable file
144
Matrix_Portal_S3_Message_Board/lib/messageboard/message.py
Executable file
|
|
@ -0,0 +1,144 @@
|
|||
# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import bitmaptools
|
||||
import displayio
|
||||
import adafruit_imageload
|
||||
from adafruit_display_text import bitmap_label
|
||||
|
||||
|
||||
class Message:
|
||||
def __init__(
|
||||
self,
|
||||
font,
|
||||
opacity=1.0,
|
||||
mask_color=0xFF00FF,
|
||||
blendmode=bitmaptools.BlendMode.Normal,
|
||||
):
|
||||
self._current_font = font
|
||||
self._current_color = 0xFF0000
|
||||
self._buffer = displayio.Bitmap(0, 0, 65535)
|
||||
self._cursor = [0, 0]
|
||||
self.opacity = opacity
|
||||
self._blendmode = blendmode
|
||||
self._mask_color = 0
|
||||
self.mask_color = mask_color
|
||||
self._width = 0
|
||||
self._height = 0
|
||||
|
||||
def _enlarge_buffer(self, width, height):
|
||||
"""Resize the message buffer to grow as necessary"""
|
||||
new_width = self._width
|
||||
if self._cursor[0] + width >= self._width:
|
||||
new_width = self._cursor[0] + width
|
||||
|
||||
new_height = self._height
|
||||
if self._cursor[1] + height >= self._height:
|
||||
new_height = self._cursor[1] + height
|
||||
|
||||
if new_width > self._width or new_height > self._height:
|
||||
new_buffer = displayio.Bitmap(new_width, new_height, 65535)
|
||||
if self._mask_color is not None:
|
||||
bitmaptools.fill_region(
|
||||
new_buffer, 0, 0, new_width, new_height, self._mask_color
|
||||
)
|
||||
bitmaptools.blit(new_buffer, self._buffer, 0, 0)
|
||||
self._buffer = new_buffer
|
||||
self._width = new_width
|
||||
self._height = new_height
|
||||
|
||||
def _add_bitmap(self, bitmap, x_offset=0, y_offset=0):
|
||||
new_width, new_height = (
|
||||
self._cursor[0] + bitmap.width + x_offset,
|
||||
self._cursor[1] + bitmap.height + y_offset,
|
||||
)
|
||||
# Resize the buffer if necessary
|
||||
self._enlarge_buffer(new_width, new_height)
|
||||
# Blit the image into the buffer
|
||||
source_left, source_top = 0, 0
|
||||
if self._cursor[0] + x_offset < 0:
|
||||
source_left = 0 - (self._cursor[0] + x_offset)
|
||||
x_offset = 0
|
||||
if self._cursor[1] + y_offset < 0:
|
||||
source_top = 0 - (self._cursor[1] + y_offset)
|
||||
y_offset = 0
|
||||
bitmaptools.blit(
|
||||
self._buffer,
|
||||
bitmap,
|
||||
self._cursor[0] + x_offset,
|
||||
self._cursor[1] + y_offset,
|
||||
x1=source_left,
|
||||
y1=source_top,
|
||||
)
|
||||
# Move the cursor
|
||||
self._cursor[0] += bitmap.width + x_offset
|
||||
|
||||
def add_text(
|
||||
self,
|
||||
text,
|
||||
color=None,
|
||||
font=None,
|
||||
x_offset=0,
|
||||
y_offset=0,
|
||||
):
|
||||
if font is None:
|
||||
font = self._current_font
|
||||
if color is None:
|
||||
color = self._current_color
|
||||
color_565value = displayio.ColorConverter().convert(color)
|
||||
# Create a bitmap label and add it to the buffer
|
||||
bmp_label = bitmap_label.Label(font, text=text)
|
||||
color_overlay = displayio.Bitmap(
|
||||
bmp_label.bitmap.width, bmp_label.bitmap.height, 65535
|
||||
)
|
||||
color_overlay.fill(color_565value)
|
||||
mask_overlay = displayio.Bitmap(
|
||||
bmp_label.bitmap.width, bmp_label.bitmap.height, 65535
|
||||
)
|
||||
mask_overlay.fill(self._mask_color)
|
||||
bitmaptools.blit(color_overlay, bmp_label.bitmap, 0, 0, skip_source_index=1)
|
||||
bitmaptools.blit(
|
||||
color_overlay, mask_overlay, 0, 0, skip_dest_index=color_565value
|
||||
)
|
||||
bmp_label = None
|
||||
|
||||
self._add_bitmap(color_overlay, x_offset, y_offset)
|
||||
|
||||
def add_image(self, image, x_offset=0, y_offset=0):
|
||||
# Load the image with imageload and add it to the buffer
|
||||
bmp_image, _ = adafruit_imageload.load(image)
|
||||
self._add_bitmap(bmp_image, x_offset, y_offset)
|
||||
|
||||
def clear(self):
|
||||
"""Clear the canvas content, but retain all of the style settings"""
|
||||
self._buffer = displayio.Bitmap(0, 0, 65535)
|
||||
self._cursor = [0, 0]
|
||||
self._width = 0
|
||||
self._height = 0
|
||||
|
||||
@property
|
||||
def buffer(self):
|
||||
"""Return the current buffer"""
|
||||
if self._width == 0 or self._height == 0:
|
||||
raise RuntimeError("No content in the message")
|
||||
return self._buffer
|
||||
|
||||
@property
|
||||
def mask_color(self):
|
||||
"""Get or Set the mask color"""
|
||||
return self._mask_color
|
||||
|
||||
@mask_color.setter
|
||||
def mask_color(self, value):
|
||||
self._mask_color = displayio.ColorConverter().convert(value)
|
||||
|
||||
@property
|
||||
def blendmode(self):
|
||||
"""Get or Set the blendmode"""
|
||||
return self._blendmode
|
||||
|
||||
@blendmode.setter
|
||||
def blendmode(self, value):
|
||||
if value in bitmaptools.BlendMode:
|
||||
self._blendmode = value
|
||||
Loading…
Reference in a new issue