]> 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_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)
 
         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 ._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,
 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._auto_refresh = auto_refresh
 
         self._initialize(init_sequence)
+
         self._current_group = None
         self._last_refresh_call = 0
         self._refresh_thread = None
         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
         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._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
 
     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.
         """
         """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,
 
     def refresh(
         self,
@@ -323,6 +332,8 @@ class Display:
         buffer_size = 128
 
         clipped = Area()
         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
 
         if not self._core.clip_area(area, clipped):
             return True
 
@@ -331,7 +342,8 @@ class Display:
         pixels_per_buffer = clipped.size()
 
         subrectangles = 1
         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
         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
             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
             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
 
             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)
         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
             )
             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() * (
             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.fill_area(subrectangle, mask, buffer)
-
             self._core.begin_transaction()
             self._send_pixels(buffer[:subrectangle_size_bytes])
             self._core.end_transaction()
             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
     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:
 
     @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)
 
         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.
         """
         """
         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
 
         if root_group == self.current_group:
             return True
@@ -405,7 +394,6 @@ class _DisplayCore:
         """
         Send the data to the current bus
         """
         """
         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:
         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:
 
 
 class Group:
+    # pylint: disable=too-many-instance-attributes
     """
     Manage a group of sprites and groups and how they are inter-related.
 
     """
     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._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
         self._layers = []
         self._supported_types = (TileGrid, Group)
         self._in_group = False
@@ -154,7 +156,6 @@ class Group:
     ) -> bool:
         if self._hidden_group:
             return False
     ) -> 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
         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:
                 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)):
         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:
 
     @property
     def hidden(self) -> bool:
@@ -186,10 +210,11 @@ class Group:
         return self._hidden_group
 
     @hidden.setter
         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")
         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:
 
     @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()
                 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:
 
 
 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.
     """
 
     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
 
         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))
 
         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)):
         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
         else:
             raise TypeError("Color buffer must be a buffer, tuple, list, or int")
         color.rgb888 = value
-        self._needs_refresh = True
 
         return color
 
 
         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.
         """
         (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):
 
     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
     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
 
     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"""
 
     def _get_palette(self):
         """Generate a palette for use with PIL"""
@@ -126,6 +136,7 @@ class Palette:
             return
 
         rgb888_pixel = input_pixel
             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
         )
         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):
         return self._colors[palette_index].transparent
 
     def _finish_refresh(self):
-        pass
+        self._needs_refresh = False
 
     @property
     def dither(self) -> bool:
 
     @property
     def dither(self) -> bool:
index cd2766263c42904870de9e67b023cdb685ca081d..2ee8d543f0b4e078c7ec634182117ff45389d857 100644 (file)
@@ -338,14 +338,14 @@ class TileGrid:
                         struct.pack_into(
                             "H",
                             buffer,
                         struct.pack_into(
                             "H",
                             buffer,
-                            offset,
+                            offset * 2,
                             output_pixel.pixel,
                         )
                     elif colorspace.depth == 32:
                         struct.pack_into(
                             "I",
                             buffer,
                             output_pixel.pixel,
                         )
                     elif colorspace.depth == 32:
                         struct.pack_into(
                             "I",
                             buffer,
-                            offset,
+                            offset * 4,
                             output_pixel.pixel,
                         )
                     elif colorspace.depth == 8:
                             output_pixel.pixel,
                         )
                     elif colorspace.depth == 8:
@@ -477,6 +477,21 @@ class TileGrid:
                 )
             areas.append(self._dirty_area)
 
                 )
             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
     @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")
     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:
 
     @property
     def x(self) -> int: