Skip to content

Update to CP 10 with busdisplay #38

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 3 commits into from
Apr 4, 2025
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
29 changes: 18 additions & 11 deletions adafruit_bitmapsaver.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
================================================================================

Save a displayio.Bitmap (and associated displayio.Palette) in a BMP file.
Make a screenshot (the contents of a displayio.Display) and save in a BMP file.
Make a screenshot (the contents of a busdisplay.BusDisplay) and save in a BMP file.


* Author(s): Dave Astels, Matt Land
Expand All @@ -31,11 +31,14 @@
import gc
import struct
import board
from displayio import Bitmap, Palette, Display, ColorConverter
from displayio import Bitmap, Palette, ColorConverter


try:
from typing import Tuple, Optional, Union
from io import BufferedWriter
from busdisplay import BusDisplay
from framebufferio import FramebufferDisplay
except ImportError:
pass

Expand Down Expand Up @@ -67,9 +70,11 @@ def _bytes_per_row(source_width: int) -> int:
return pixel_bytes + padding_bytes


def _rotated_height_and_width(pixel_source: Union[Bitmap, Display]) -> Tuple[int, int]:
def _rotated_height_and_width(
pixel_source: Union[Bitmap, BusDisplay, FramebufferDisplay]
) -> Tuple[int, int]:
# flip axis if the display is rotated
if isinstance(pixel_source, Display) and (pixel_source.rotation % 180 != 0):
if hasattr(pixel_source, "rotation") and (pixel_source.rotation % 180 != 0):
return pixel_source.height, pixel_source.width
return pixel_source.width, pixel_source.height

Expand Down Expand Up @@ -111,7 +116,7 @@ def rgb565_to_rgb888(rgb565):
# pylint:disable=too-many-locals
def _write_pixels(
output_file: BufferedWriter,
pixel_source: Union[Bitmap, Display],
pixel_source: Union[Bitmap, BusDisplay, FramebufferDisplay],
palette: Optional[Union[Palette, ColorConverter]],
) -> None:
saving_bitmap = isinstance(pixel_source, Bitmap)
Expand All @@ -136,7 +141,7 @@ def _write_pixels(
color >>= 8
buffer_index += 1
else:
# pixel_source: Display
# pixel_source: display
result_buffer = bytearray(2048)
data = pixel_source.fill_row(y - 1, result_buffer)
for i in range(width):
Expand All @@ -156,15 +161,17 @@ def _write_pixels(

def save_pixels(
file_or_filename: Union[str, BufferedWriter],
pixel_source: Union[Display, Bitmap] = None,
pixel_source: Union[BusDisplay, FramebufferDisplay, Bitmap] = None,
palette: Optional[Union[Palette, ColorConverter]] = None,
) -> None:
"""Save pixels to a 24 bit per pixel BMP file.
If pixel_source if a displayio.Bitmap, save it's pixels through palette.
If it's a displayio.Display, a palette isn't required.
If it's a displayio display, a palette isn't required. To be supported,
a display must implement `busdisplay.BusDisplay.fill_row`. Known supported
display types are `busdisplay.BusDisplay` and `framebufferio.FramebufferDisplay`.

:param file_or_filename: either the file to save to, or it's absolute name
:param pixel_source: the Bitmap or Display to save
:param pixel_source: the Bitmap or display to save
:param palette: the Palette to use for looking up colors in the bitmap
"""
if not pixel_source:
Expand All @@ -177,8 +184,8 @@ def save_pixels(
raise ValueError(
"Third argument must be a Palette or ColorConverter for a Bitmap save"
)
elif not isinstance(pixel_source, Display):
raise ValueError("Second argument must be a Bitmap or Display")
elif not hasattr(pixel_source, "fill_row"):
raise ValueError("Second argument must be a Bitmap or supported display type")
try:
if isinstance(file_or_filename, str):
output_file = open( # pylint: disable=consider-using-with
Expand Down
6 changes: 2 additions & 4 deletions docs/api.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@

.. If you created a package, create one automodule per module in the package.

.. If your library file(s) are nested in a directory (e.g. /adafruit_foo/foo.py)
.. use this format as the module name: "adafruit_foo.foo"
API Reference
#############

.. automodule:: adafruit_bitmapsaver
:members:
9 changes: 8 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,14 @@
# Uncomment the below if you use native CircuitPython modules such as
# digitalio, micropython and busio. List the modules you use. Without it, the
# autodoc module docs will fail to generate with a warning.
autodoc_mock_imports = ["displayio", "digitalio", "busio", "board"]
autodoc_mock_imports = [
"busdisplay",
"displayio",
"digitalio",
"busio",
"board",
"framebufferio",
]


intersphinx_mapping = {
Expand Down