From a53c930973a24a7c55db9448e45d7ca7974cb56e Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Tue, 26 Sep 2023 08:13:34 -0700 Subject: [PATCH] More bug fixes --- displayio/_bitmap.py | 2 +- displayio/_display.py | 32 +++++++++++++++++++++++++------- displayio/_displaycore.py | 16 ++-------------- displayio/_group.py | 38 +++++++++++++++++++++++++++++++++----- displayio/_palette.py | 27 +++++++++++++++++++-------- displayio/_tilegrid.py | 22 +++++++++++++++++++--- 6 files changed, 99 insertions(+), 38 deletions(-) diff --git a/displayio/_bitmap.py b/displayio/_bitmap.py index 30009e2..324475f 100644 --- a/displayio/_bitmap.py +++ b/displayio/_bitmap.py @@ -113,7 +113,7 @@ class Bitmap: self._x_shift += 1 power_of_two = power_of_two << 1 - self._x_mask = (1 << self._x_shift) - 1 # UUsed as a modulus on the x value + self._x_mask = (1 << self._x_shift) - 1 # Used as a modulus on the x value self._bitmask = (1 << bits_per_value) - 1 self._dirty_area = Area(0, 0, width, height) diff --git a/displayio/_display.py b/displayio/_display.py index e001577..74ee095 100644 --- a/displayio/_display.py +++ b/displayio/_display.py @@ -27,7 +27,7 @@ from circuitpython_typing import WriteableBuffer, ReadableBuffer from ._displaycore import _DisplayCore from ._displaybus import _DisplayBus from ._colorconverter import ColorConverter -from ._group import Group +from ._group import Group, circuitpython_splash from ._area import Area from ._constants import ( CHIP_SELECT_TOGGLE_EVERY_BYTE, @@ -157,11 +157,10 @@ class Display: self._auto_refresh = auto_refresh self._initialize(init_sequence) + self._current_group = None self._last_refresh_call = 0 self._refresh_thread = None - if self._auto_refresh: - self.auto_refresh = True self._colorconverter = ColorConverter() self._backlight_type = None @@ -179,7 +178,10 @@ class Display: self._backlight_type = BACKLIGHT_IN_OUT self._backlight = digitalio.DigitalInOut(backlight_pin) self._backlight.switch_to_output() - self.brightness = brightness + self.brightness = brightness + if not circuitpython_splash._in_group: + self._set_root_group(circuitpython_splash) + self.auto_refresh = auto_refresh def __new__(cls, *args, **kwargs): from . import ( # pylint: disable=import-outside-toplevel, cyclic-import @@ -235,7 +237,14 @@ class Display: """Switches to displaying the given group of layers. When group is None, the default CircuitPython terminal will be shown. """ - self._core.show(group) + if group is None: + group = circuitpython_splash + self._core.set_root_group(group) + + def _set_root_group(self, root_group: Group) -> None: + ok = self._core.set_root_group(root_group) + if not ok: + raise ValueError("Group already used") def refresh( self, @@ -323,6 +332,8 @@ class Display: buffer_size = 128 clipped = Area() + # Clip the area to the display by overlapping the areas. + # If there is no overlap then we're done. if not self._core.clip_area(area, clipped): return True @@ -331,7 +342,8 @@ class Display: pixels_per_buffer = clipped.size() subrectangles = 1 - + # for SH1107 and other boundary constrained controllers + # write one single row at a time if self._core.sh1107_addressing: subrectangles = rows_per_buffer // 8 rows_per_buffer = 8 @@ -339,6 +351,7 @@ class Display: rows_per_buffer = buffer_size * pixels_per_word // clipped.width() if rows_per_buffer == 0: rows_per_buffer = 1 + # If pixels are packed by column then ensure rows_per_buffer is on a byte boundary if ( self._core.colorspace.depth < 8 and self._core.colorspace.pixels_in_byte_share_row @@ -354,6 +367,7 @@ class Display: if pixels_per_buffer % pixels_per_word: buffer_size += 1 + # TODO: Optimize with memoryview buffer = bytearray([0] * (buffer_size * struct.calcsize("I"))) mask_length = (pixels_per_buffer // 32) + 1 mask = array("L", [0] * mask_length) @@ -368,6 +382,7 @@ class Display: ) if remaining_rows < rows_per_buffer: subrectangle.y2 = subrectangle.y1 + remaining_rows + remaining_rows -= rows_per_buffer self._core.set_region_to_update(subrectangle) if self._core.colorspace.depth >= 8: subrectangle_size_bytes = subrectangle.size() * ( @@ -379,7 +394,6 @@ class Display: ) self._core.fill_area(subrectangle, mask, buffer) - self._core.begin_transaction() self._send_pixels(buffer[:subrectangle_size_bytes]) self._core.end_transaction() @@ -411,6 +425,10 @@ class Display: def reset(self) -> None: """Reset the display""" self.auto_refresh = True + circuitpython_splash.x = 0 + circuitpython_splash.y = 0 + if not circuitpython_splash._in_group: # pylint: disable=protected-access + self._set_root_group(circuitpython_splash) @property def auto_refresh(self) -> bool: diff --git a/displayio/_displaycore.py b/displayio/_displaycore.py index 9bbb665..fd42b4d 100644 --- a/displayio/_displaycore.py +++ b/displayio/_displaycore.py @@ -178,25 +178,14 @@ class _DisplayCore: if self.current_group is not None: self.current_group._update_transform(self.transform) - def show(self, root_group: Group) -> bool: - # pylint: disable=protected-access - + def set_root_group(self, root_group: Group) -> bool: """ Switches to displaying the given group of layers. When group is `None`, the default CircuitPython terminal will be shown. :param Optional[displayio.Group] root_group: The group to show. """ - - """ - # TODO: Implement Supervisor - if root_group is None: - circuitpython_splash = _Supervisor().circuitpython_splash - if not circuitpython_splash._in_group: - root_group = circuitpython_splash - elif self.current_group == circuitpython_splash: - return True - """ + # pylint: disable=protected-access if root_group == self.current_group: return True @@ -405,7 +394,6 @@ class _DisplayCore: """ Send the data to the current bus """ - # print(data_type, chip_select, data) self._send(data_type, chip_select, data) def begin_transaction(self) -> None: diff --git a/displayio/_group.py b/displayio/_group.py index 833b95c..2c104f5 100644 --- a/displayio/_group.py +++ b/displayio/_group.py @@ -30,6 +30,7 @@ __repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git" class Group: + # pylint: disable=too-many-instance-attributes """ Manage a group of sprites and groups and how they are inter-related. @@ -50,6 +51,7 @@ class Group: self._group_x = x self._group_y = y self._hidden_group = False + self._hidden_by_parent = False self._layers = [] self._supported_types = (TileGrid, Group) self._in_group = False @@ -154,7 +156,6 @@ class Group: ) -> bool: if self._hidden_group: return False - for layer in self._layers: if isinstance(layer, (Group, TileGrid)): if layer._fill_area( # pylint: disable=protected-access @@ -173,10 +174,33 @@ class Group: layer._finish_refresh() # pylint: disable=protected-access def _get_refresh_areas(self, areas: list[Area]) -> None: + # pylint: disable=protected-access + for layer in self._layers: + if isinstance(layer, Group): + layer._get_refresh_areas(areas) + elif isinstance(layer, TileGrid): + if not layer._get_rendered_hidden(): + layer._get_refresh_areas(areas) + + def _set_hidden(self, hidden: bool) -> None: + if self._hidden_group == hidden: + return + self._hidden_group = hidden + if self._hidden_by_parent: + return for layer in self._layers: if isinstance(layer, (Group, TileGrid)): - if not layer.hidden: - layer._get_refresh_areas(areas) # pylint: disable=protected-access + layer._set_hidden_by_parent(hidden) # pylint: disable=protected-access + + def _set_hidden_by_parent(self, hidden: bool) -> None: + if self._hidden_by_parent == hidden: + return + self._hidden_by_parent = hidden + if self._hidden_group: + return + for layer in self._layers: + if isinstance(layer, (Group, TileGrid)): + layer._set_hidden_by_parent(hidden) # pylint: disable=protected-access @property def hidden(self) -> bool: @@ -186,10 +210,11 @@ class Group: return self._hidden_group @hidden.setter - def hidden(self, value: bool): + def hidden(self, value: bool) -> None: if not isinstance(value, (bool, int)): raise ValueError("Expecting a boolean or integer value") - self._hidden_group = bool(value) + value = bool(value) + self._set_hidden(value) @property def scale(self) -> int: @@ -257,3 +282,6 @@ class Group: self._absolute_transform.y += dy_value * (value - self._group_y) self._group_y = value self._update_child_transforms() + + +circuitpython_splash = Group(scale=2, x=0, y=0) diff --git a/displayio/_palette.py b/displayio/_palette.py index 63d98ee..c00d454 100644 --- a/displayio/_palette.py +++ b/displayio/_palette.py @@ -28,12 +28,17 @@ __repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git" class Palette: - """Map a pixel palette_index to a full color. Colors are transformed to the display’s + """Map a pixel palette_index to a full color. Colors are transformed to the display's format internally to save memory. """ - def __init__(self, color_count: int, dither: bool = False): - """Create a Palette object to store a set number of colors.""" + def __init__(self, color_count: int, *, dither: bool = False): + """Create a Palette object to store a set number of colors. + + :param int color_count: The number of colors in the Palette + :param bool dither: When true, dither the RGB color before converting to the + display's color space + """ self._needs_refresh = False self._dither = dither @@ -41,7 +46,8 @@ class Palette: for _ in range(color_count): self._colors.append(self._make_color(0)) - def _make_color(self, value, transparent=False): + @staticmethod + def _make_color(value, transparent=False): color = ColorStruct(transparent=transparent) if isinstance(value, (tuple, list, bytes, bytearray)): @@ -52,7 +58,6 @@ class Palette: else: raise TypeError("Color buffer must be a buffer, tuple, list, or int") color.rgb888 = value - self._needs_refresh = True return color @@ -72,8 +77,11 @@ class Palette: (to represent an RGB value). Value can be an int, bytes (3 bytes (RGB) or 4 bytes (RGB + pad byte)), bytearray, or a tuple or list of 3 integers. """ - if self._colors[index].rgb888 != value: - self._colors[index] = self._make_color(value) + if self._colors[index].rgb888 == value: + return + self._colors[index] = self._make_color(value) + self._colors[index].cached_colorspace = None + self._needs_refresh = True def __getitem__(self, index: int) -> Optional[int]: if not 0 <= index < len(self._colors): @@ -83,10 +91,12 @@ class Palette: def make_transparent(self, palette_index: int) -> None: """Set the palette index to be a transparent color""" self._colors[palette_index].transparent = True + self._needs_refresh = True def make_opaque(self, palette_index: int) -> None: """Set the palette index to be an opaque color""" self._colors[palette_index].transparent = False + self._needs_refresh = True def _get_palette(self): """Generate a palette for use with PIL""" @@ -126,6 +136,7 @@ class Palette: return rgb888_pixel = input_pixel + rgb888_pixel.pixel = self._colors[palette_index].rgb888 ColorConverter._convert_color( # pylint: disable=protected-access colorspace, self._dither, rgb888_pixel, output_color ) @@ -140,7 +151,7 @@ class Palette: return self._colors[palette_index].transparent def _finish_refresh(self): - pass + self._needs_refresh = False @property def dither(self) -> bool: diff --git a/displayio/_tilegrid.py b/displayio/_tilegrid.py index cd27662..2ee8d54 100644 --- a/displayio/_tilegrid.py +++ b/displayio/_tilegrid.py @@ -338,14 +338,14 @@ class TileGrid: struct.pack_into( "H", buffer, - offset, + offset * 2, output_pixel.pixel, ) elif colorspace.depth == 32: struct.pack_into( "I", buffer, - offset, + offset * 4, output_pixel.pixel, ) elif colorspace.depth == 8: @@ -477,6 +477,21 @@ class TileGrid: ) areas.append(self._dirty_area) + def _set_hidden(self, hidden: bool) -> None: + self._hidden_tilegrid = hidden + self._rendered_hidden = False + if not hidden: + self._full_change = True + + def _set_hidden_by_parent(self, hidden: bool) -> None: + self._hidden_by_parent = hidden + self._rendered_hidden = False + if not hidden: + self._full_change = True + + def _get_rendered_hidden(self) -> bool: + return self._rendered_hidden + @property def hidden(self) -> bool: """True when the TileGrid is hidden. This may be False even @@ -487,7 +502,8 @@ class TileGrid: def hidden(self, value: bool): if not isinstance(value, (bool, int)): raise ValueError("Expecting a boolean or integer value") - self._hidden_tilegrid = bool(value) + value = bool(value) + self._set_hidden(value) @property def x(self) -> int: -- 2.49.0