]> Repositories - hackapet/Adafruit_Blinka_Displayio.git/commitdiff
More bug fixes
authorMelissa LeBlanc-Williams <melissa@adafruit.com>
Tue, 26 Sep 2023 15:13:34 +0000 (08:13 -0700)
committerMelissa LeBlanc-Williams <melissa@adafruit.com>
Tue, 26 Sep 2023 15:13:34 +0000 (08:13 -0700)
displayio/_bitmap.py
displayio/_display.py
displayio/_displaycore.py
displayio/_group.py
displayio/_palette.py
displayio/_tilegrid.py

index 30009e29df5a579acf2a1962b0ab24f93a3a348c..324475f4662dc7fc86c543867e858593aaca7791 100644 (file)
@@ -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)
 
index e001577f3e38a5b2c1848d296e19b3aa5921716d..74ee0957b697e3a494dfec6fa29ed009916fac3a 100644 (file)
@@ -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:
index 9bbb66518c9966134bd465a72db4412ca7b25e86..fd42b4d4200c53649b17a19441bacfdae3263963 100644 (file)
@@ -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:
index 833b95c4aec3f65e6e56768f7efe420379f42727..2c104f5a368cfb9533d5177a15c7000c86655548 100644 (file)
@@ -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)
index 63d98eec3385ceea9e19ed6c556565abfb4f84c4..c00d45491be85caf5392e7eea46c699c66a63436 100644 (file)
@@ -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 displays
+    """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:
index cd2766263c42904870de9e67b023cdb685ca081d..2ee8d543f0b4e078c7ec634182117ff45389d857 100644 (file)
@@ -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: