]> Repositories - hackapet/Adafruit_Blinka_Displayio.git/commitdiff
Remove more of PIL
authorMelissa LeBlanc-Williams <melissa@adafruit.com>
Sat, 23 Sep 2023 05:36:30 +0000 (22:36 -0700)
committerMelissa LeBlanc-Williams <melissa@adafruit.com>
Sat, 23 Sep 2023 05:36:30 +0000 (22:36 -0700)
displayio/_bitmap.py
displayio/_display.py
displayio/_displaycore.py
displayio/_epaperdisplay.py
displayio/_fourwire.py
displayio/_group.py
displayio/_i2cdisplay.py
displayio/_palette.py
displayio/_structs.py
displayio/_tilegrid.py

index 03dd75bf483c8f0e17e1910470156114fa591d9e..30009e29df5a579acf2a1962b0ab24f93a3a348c 100644 (file)
@@ -18,17 +18,39 @@ displayio for Blinka
 """
 
 from __future__ import annotations
 """
 
 from __future__ import annotations
+import struct
+from array import array
 from typing import Union, Tuple
 from typing import Union, Tuple
-from PIL import Image
-from ._structs import RectangleStruct
+from circuitpython_typing import WriteableBuffer
 from ._area import Area
 
 __version__ = "0.0.0+auto.0"
 __repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
 
 from ._area import Area
 
 __version__ = "0.0.0+auto.0"
 __repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
 
+ALIGN_BITS = 8 * struct.calcsize("I")
+
+
+def stride(width: int, bits_per_pixel: int) -> int:
+    """Return the number of bytes per row of a bitmap with the given width and bits per pixel."""
+    row_width = width * bits_per_pixel
+    return (row_width + (ALIGN_BITS - 1)) // ALIGN_BITS
+
 
 class Bitmap:
 
 class Bitmap:
-    """Stores values of a certain size in a 2D array"""
+    """Stores values of a certain size in a 2D array
+
+    Bitmaps can be treated as read-only buffers. If the number of bits in a pixel is 8, 16,
+    or 32; and the number of bytes per row is a multiple of 4, then the resulting memoryview
+    will correspond directly with the bitmap's contents. Otherwise, the bitmap data is packed
+    into the memoryview with unspecified padding.
+
+    A Bitmap can be treated as a buffer, allowing its content to be
+    viewed and modified using e.g., with ``ulab.numpy.frombuffer``,
+    but the `displayio.Bitmap.dirty` method must be used to inform
+    displayio when a bitmap was modified through the buffer interface.
+
+    `bitmaptools.arrayblit` can also be useful to move data efficiently
+    into a Bitmap."""
 
     def __init__(self, width: int, height: int, value_count: int):
         """Create a Bitmap object with the given fixed size. Each pixel stores a value that is
 
     def __init__(self, width: int, height: int, value_count: int):
         """Create a Bitmap object with the given fixed size. Each pixel stores a value that is
@@ -36,12 +58,9 @@ class Bitmap:
         share the underlying Bitmap. value_count is used to minimize the memory used to store
         the Bitmap.
         """
         share the underlying Bitmap. value_count is used to minimize the memory used to store
         the Bitmap.
         """
-        self._bmp_width = width
-        self._bmp_height = height
-        self._read_only = False
 
 
-        if value_count < 0:
-            raise ValueError("value_count must be > 0")
+        if not 1 <= value_count <= 65535:
+            raise ValueError("value_count must be in the range of 1-65535")
 
         bits = 1
         while (value_count - 1) >> bits:
 
         bits = 1
         while (value_count - 1) >> bits:
@@ -50,7 +69,28 @@ class Bitmap:
             else:
                 bits += 8
 
             else:
                 bits += 8
 
-        self._bits_per_value = bits
+        self._from_buffer(width, height, bits, None, False)
+
+    def _from_buffer(
+        self,
+        width: int,
+        height: int,
+        bits_per_value: int,
+        data: WriteableBuffer,
+        read_only: bool,
+    ) -> None:
+        # pylint: disable=too-many-arguments
+        self._bmp_width = width
+        self._bmp_height = height
+        self._stride = stride(width, bits_per_value)
+        self._data_alloc = False
+
+        if data is None or len(data) == 0:
+            data = array("L", [0] * self._stride * height)
+            self._data_alloc = True
+        self._data = data
+        self._read_only = read_only
+        self._bits_per_value = bits_per_value
 
         if (
             self._bits_per_value > 8
 
         if (
             self._bits_per_value > 8
@@ -59,8 +99,23 @@ class Bitmap:
         ):
             raise NotImplementedError("Invalid bits per value")
 
         ):
             raise NotImplementedError("Invalid bits per value")
 
-        self._image = Image.new("P", (width, height), 0)
-        self._dirty_area = RectangleStruct(0, 0, width, height)
+        # Division and modulus can be slow because it has to handle any integer. We know
+        # bits_per_value is a power of two. We divide and mod by bits_per_value to compute
+        # the offset into the byte array. So, we can the offset computation to simplify to
+        # a shift for division and mask for mod.
+
+        # Used to divide the index by the number of pixels per word. It's
+        # used in a shift which effectively divides by 2 ** x_shift.
+        self._x_shift = 0
+
+        power_of_two = 1
+        while power_of_two < ALIGN_BITS // bits_per_value:
+            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._bitmask = (1 << bits_per_value) - 1
+        self._dirty_area = Area(0, 0, width, height)
 
     def __getitem__(self, index: Union[Tuple[int, int], int]) -> int:
         """
 
     def __getitem__(self, index: Union[Tuple[int, int], int]) -> int:
         """
@@ -75,12 +130,32 @@ class Bitmap:
         else:
             raise TypeError("Index is not an int, list, or tuple")
 
         else:
             raise TypeError("Index is not an int, list, or tuple")
 
-        if x > self._image.width or y > self._image.height:
+        if x > self._bmp_width or x < 0 or y > self._bmp_height or y < 0:
             raise ValueError(f"Index {index} is out of range")
         return self._get_pixel(x, y)
 
     def _get_pixel(self, x: int, y: int) -> int:
             raise ValueError(f"Index {index} is out of range")
         return self._get_pixel(x, y)
 
     def _get_pixel(self, x: int, y: int) -> int:
-        return self._image.getpixel((x, y))
+        if x >= self._bmp_width or x < 0 or y >= self._bmp_height or y < 0:
+            return 0
+        row_start = y * self._stride
+        bytes_per_value = self._bits_per_value // 8
+        if bytes_per_value < 1:
+            word = self._data[row_start + (x >> self._x_shift)]
+            return (
+                word
+                >> (
+                    struct.calcsize("I") * 8
+                    - ((x & self._x_mask) + 1) * self._bits_per_value
+                )
+            ) & self._bitmask
+        row = memoryview(self._data)[row_start : row_start + self._stride]
+        if bytes_per_value == 1:
+            return row[x]
+        if bytes_per_value == 2:
+            return struct.unpack_from("<H", row, x * 2)[0]
+        if bytes_per_value == 4:
+            return struct.unpack_from("<I", row, x * 4)[0]
+        return 0
 
     def __setitem__(self, index: Union[Tuple[int, int], int], value: int) -> None:
         """
 
     def __setitem__(self, index: Union[Tuple[int, int], int], value: int) -> None:
         """
@@ -96,21 +171,42 @@ class Bitmap:
         elif isinstance(index, int):
             x = index % self._bmp_width
             y = index // self._bmp_width
         elif isinstance(index, int):
             x = index % self._bmp_width
             y = index // self._bmp_width
-        self._image.putpixel((x, y), value)
-        if self._dirty_area.x1 == self._dirty_area.x2:
-            self._dirty_area.x1 = x
-            self._dirty_area.x2 = x + 1
-            self._dirty_area.y1 = y
-            self._dirty_area.y2 = y + 1
+        # update the dirty region
+        self._set_dirty_area(Area(x, y, x + 1, y + 1))
+        self._write_pixel(x, y, value)
+
+    def _write_pixel(self, x: int, y: int, value: int) -> None:
+        if self._read_only:
+            raise RuntimeError("Read-only")
+
+        # Writes the color index value into a pixel position
+        # Must update the dirty area separately
+
+        # Don't write if out of area
+        if x < 0 or x >= self._bmp_width or y < 0 or y >= self._bmp_height:
+            return
+
+        # Update one pixel of data
+        row_start = y * self._stride
+        bytes_per_value = self._bits_per_value // 8
+        if bytes_per_value < 1:
+            bit_position = (
+                struct.calcsize("I") * 8
+                - ((x & self._x_mask) + 1) * self._bits_per_value
+            )
+            index = row_start + (x >> self._x_shift)
+            word = self._data[index]
+            word &= ~(self._bitmask << bit_position)
+            word |= (value & self._bitmask) << bit_position
+            self._data[index] = word
         else:
         else:
-            if x < self._dirty_area.x1:
-                self._dirty_area.x1 = x
-            elif x >= self._dirty_area.x2:
-                self._dirty_area.x2 = x + 1
-            if y < self._dirty_area.y1:
-                self._dirty_area.y1 = y
-            elif y >= self._dirty_area.y2:
-                self._dirty_area.y2 = y + 1
+            row = memoryview(self._data)[row_start : row_start + self._stride]
+            if bytes_per_value == 1:
+                row[x] = value
+            elif bytes_per_value == 2:
+                struct.pack_into("<H", row, x * 2, value)
+            elif bytes_per_value == 4:
+                struct.pack_into("<I", row, x * 4, value)
 
     def _finish_refresh(self):
         self._dirty_area.x1 = 0
 
     def _finish_refresh(self):
         self._dirty_area.x1 = 0
@@ -118,8 +214,18 @@ class Bitmap:
 
     def fill(self, value: int) -> None:
         """Fills the bitmap with the supplied palette index value."""
 
     def fill(self, value: int) -> None:
         """Fills the bitmap with the supplied palette index value."""
-        self._image = Image.new("P", (self._bmp_width, self._bmp_height), value)
-        self._dirty_area = RectangleStruct(0, 0, self._bmp_width, self._bmp_height)
+        if self._read_only:
+            raise RuntimeError("Read-only")
+        self._set_dirty_area(Area(0, 0, self._bmp_width, self._bmp_height))
+
+        # build the packed word
+        word = 0
+        for i in range(32 // self._bits_per_value):
+            word |= (value & self._bitmask) << (32 - ((i + 1) * self._bits_per_value))
+
+        # copy it in
+        for i in range(self._stride * self._bmp_height):
+            self._data[i] = word
 
     def blit(
         self,
 
     def blit(
         self,
@@ -182,7 +288,13 @@ class Bitmap:
             x2 = self._bmp_width
         if y2 == -1:
             y2 = self._bmp_height
             x2 = self._bmp_width
         if y2 == -1:
             y2 = self._bmp_height
-        area = Area(x1, y1, x2, y2)
+        self._set_dirty_area(Area(x1, y1, x2, y2))
+
+    def _set_dirty_area(self, dirty_area: Area) -> None:
+        if self._read_only:
+            raise RuntimeError("Read-only")
+
+        area = dirty_area
         area.canon()
         area.union(self._dirty_area, area)
         bitmap_area = Area(0, 0, self._bmp_width, self._bmp_height)
         area.canon()
         area.union(self._dirty_area, area)
         bitmap_area = Area(0, 0, self._bmp_width, self._bmp_height)
index ac719817987fc5e586d9922ff1370b1279ece781..e001577f3e38a5b2c1848d296e19b3aa5921716d 100644 (file)
@@ -22,14 +22,12 @@ import struct
 from array import array
 from typing import Optional
 import digitalio
 from array import array
 from typing import Optional
 import digitalio
-from PIL import Image
 import microcontroller
 import microcontroller
-import circuitpython_typing
+from circuitpython_typing import WriteableBuffer, ReadableBuffer
 from ._displaycore import _DisplayCore
 from ._displaybus import _DisplayBus
 from ._colorconverter import ColorConverter
 from ._group import Group
 from ._displaycore import _DisplayCore
 from ._displaybus import _DisplayBus
 from ._colorconverter import ColorConverter
 from ._group import Group
-from ._structs import RectangleStruct
 from ._area import Area
 from ._constants import (
     CHIP_SELECT_TOGGLE_EVERY_BYTE,
 from ._area import Area
 from ._constants import (
     CHIP_SELECT_TOGGLE_EVERY_BYTE,
@@ -58,7 +56,7 @@ class Display:
     def __init__(
         self,
         display_bus: _DisplayBus,
     def __init__(
         self,
         display_bus: _DisplayBus,
-        init_sequence: circuitpython_typing.ReadableBuffer,
+        init_sequence: ReadableBuffer,
         *,
         width: int,
         height: int,
         *,
         width: int,
         height: int,
@@ -77,16 +75,14 @@ class Display:
         backlight_pin: Optional[microcontroller.Pin] = None,
         brightness_command: Optional[int] = None,
         brightness: float = 1.0,
         backlight_pin: Optional[microcontroller.Pin] = None,
         brightness_command: Optional[int] = None,
         brightness: float = 1.0,
-        auto_brightness: bool = False,
         single_byte_bounds: bool = False,
         data_as_commands: bool = False,
         auto_refresh: bool = True,
         native_frames_per_second: int = 60,
         backlight_on_high: bool = True,
         SH1107_addressing: bool = False,
         single_byte_bounds: bool = False,
         data_as_commands: bool = False,
         auto_refresh: bool = True,
         native_frames_per_second: int = 60,
         backlight_on_high: bool = True,
         SH1107_addressing: bool = False,
-        set_vertical_scroll: int = 0,
     ):
     ):
-        # pylint: disable=unused-argument,too-many-locals,invalid-name
+        # pylint: disable=too-many-locals,invalid-name
         """Create a Display object on the given display bus (`displayio.FourWire` or
         `paralleldisplay.ParallelBus`).
 
         """Create a Display object on the given display bus (`displayio.FourWire` or
         `paralleldisplay.ParallelBus`).
 
@@ -157,12 +153,10 @@ class Display:
         self._native_frames_per_second = native_frames_per_second
         self._native_ms_per_frame = 1000 // native_frames_per_second
 
         self._native_frames_per_second = native_frames_per_second
         self._native_ms_per_frame = 1000 // native_frames_per_second
 
-        self._auto_brightness = auto_brightness
         self._brightness = brightness
         self._auto_refresh = auto_refresh
 
         self._initialize(init_sequence)
         self._brightness = brightness
         self._auto_refresh = auto_refresh
 
         self._initialize(init_sequence)
-        self._buffer = Image.new("RGB", (width, height))
         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
@@ -295,20 +289,7 @@ class Display:
         if not self._core.start_refresh():
             return False
 
         if not self._core.start_refresh():
             return False
 
-        # TODO: Likely move this to _refresh_area()
-        # Go through groups and and add each to buffer
-        """
-        if self._core.current_group is not None:
-            buffer = Image.new("RGBA", (self._core.width, self._core.height))
-            # Recursively have everything draw to the image
-            self._core.current_group._fill_area(
-                buffer
-            )  # pylint: disable=protected-access
-            # save image to buffer (or probably refresh buffer so we can compare)
-            self._buffer.paste(buffer)
-        """
         areas_to_refresh = self._get_refresh_areas()
         areas_to_refresh = self._get_refresh_areas()
-
         for area in areas_to_refresh:
             self._refresh_area(area)
 
         for area in areas_to_refresh:
             self._refresh_area(area)
 
@@ -404,39 +385,22 @@ class Display:
             self._core.end_transaction()
         return True
 
             self._core.end_transaction()
         return True
 
-    def _apply_rotation(self, rectangle):
-        """Adjust the rectangle coordinates based on rotation"""
-        if self._core.rotation == 90:
-            return RectangleStruct(
-                self._core.height - rectangle.y2,
-                rectangle.x1,
-                self._core.height - rectangle.y1,
-                rectangle.x2,
-            )
-        if self._core.rotation == 180:
-            return RectangleStruct(
-                self._core.width - rectangle.x2,
-                self._core.height - rectangle.y2,
-                self._core.width - rectangle.x1,
-                self._core.height - rectangle.y1,
-            )
-        if self._core.rotation == 270:
-            return RectangleStruct(
-                rectangle.y1,
-                self._core.width - rectangle.x2,
-                rectangle.y2,
-                self._core.width - rectangle.x1,
-            )
-        return rectangle
-
-    def fill_row(
-        self, y: int, buffer: circuitpython_typing.WriteableBuffer
-    ) -> circuitpython_typing.WriteableBuffer:
+    def fill_row(self, y: int, buffer: WriteableBuffer) -> WriteableBuffer:
         """Extract the pixels from a single row"""
         """Extract the pixels from a single row"""
-        for x in range(0, self._core.width):
-            _rgb_565 = self._colorconverter.convert(self._buffer.getpixel((x, y)))
-            buffer[x * 2] = (_rgb_565 >> 8) & 0xFF
-            buffer[x * 2 + 1] = _rgb_565 & 0xFF
+        if self._core.colorspace.depth != 16:
+            raise ValueError("Display must have a 16 bit colorspace.")
+
+        area = Area(0, y, self._core.width, y + 1)
+        pixels_per_word = (struct.calcsize("I") * 8) // self._core.colorspace.depth
+        buffer_size = self._core.width // pixels_per_word
+        pixels_per_buffer = area.size()
+        if pixels_per_buffer % pixels_per_word:
+            buffer_size += 1
+
+        buffer = bytearray([0] * (buffer_size * struct.calcsize("I")))
+        mask_length = (pixels_per_buffer // 32) + 1
+        mask = array("L", [0x00000000] * mask_length)
+        self._core.fill_area(area, mask, buffer)
         return buffer
 
     def release(self) -> None:
         return buffer
 
     def release(self) -> None:
@@ -460,10 +424,7 @@ class Display:
 
     @property
     def brightness(self) -> float:
 
     @property
     def brightness(self) -> float:
-        """The brightness of the display as a float. 0.0 is off and 1.0 is full `brightness`.
-        When `auto_brightness` is True, the value of `brightness` will change automatically.
-        If `brightness` is set, `auto_brightness` will be disabled and will be set to False.
-        """
+        """The brightness of the display as a float. 0.0 is off and 1.0 is full `brightness`."""
         return self._brightness
 
     @brightness.setter
         return self._brightness
 
     @brightness.setter
@@ -498,19 +459,6 @@ class Display:
         else:
             raise ValueError("Brightness must be between 0.0 and 1.0")
 
         else:
             raise ValueError("Brightness must be between 0.0 and 1.0")
 
-    @property
-    def auto_brightness(self) -> bool:
-        """True when the display brightness is adjusted automatically, based on an ambient
-        light sensor or other method. Note that some displays may have this set to True by
-        default, but not actually implement automatic brightness adjustment.
-        `auto_brightness` is set to False if `brightness` is set manually.
-        """
-        return self._auto_brightness
-
-    @auto_brightness.setter
-    def auto_brightness(self, value: bool):
-        self._auto_brightness = value
-
     @property
     def width(self) -> int:
         """Display Width"""
     @property
     def width(self) -> int:
         """Display Width"""
index 8db49e01892ba21bb4829879a3fe314fded23e2a..9bbb66518c9966134bd465a72db4412ca7b25e86 100644 (file)
@@ -24,7 +24,7 @@ __repo__ = "https://github.com/adafruit/Adafruit_Blinka_Displayio.git"
 
 import time
 import struct
 
 import time
 import struct
-import circuitpython_typing
+from circuitpython_typing import WriteableBuffer, ReadableBuffer
 from paralleldisplay import ParallelBus
 from ._fourwire import FourWire
 from ._group import Group
 from paralleldisplay import ParallelBus
 from ._fourwire import FourWire
 from ._group import Group
@@ -248,8 +248,8 @@ class _DisplayCore:
     def fill_area(
         self,
         area: Area,
     def fill_area(
         self,
         area: Area,
-        mask: circuitpython_typing.WriteableBuffer,
-        buffer: circuitpython_typing.WriteableBuffer,
+        mask: WriteableBuffer,
+        buffer: WriteableBuffer,
     ) -> bool:
         """Call the current group's fill area function"""
         if self.current_group is not None:
     ) -> bool:
         """Call the current group's fill area function"""
         if self.current_group is not None:
@@ -400,7 +400,7 @@ class _DisplayCore:
         self,
         data_type: int,
         chip_select: int,
         self,
         data_type: int,
         chip_select: int,
-        data: circuitpython_typing.ReadableBuffer,
+        data: ReadableBuffer,
     ) -> None:
         """
         Send the data to the current bus
     ) -> None:
         """
         Send the data to the current bus
index 686f8bb12d58aae1bdc3356352e0c2d6a7421601..114506c4af915a483befb54d95261ed7f23c6203 100644 (file)
@@ -19,7 +19,7 @@ displayio for Blinka
 
 from typing import Optional
 import microcontroller
 
 from typing import Optional
 import microcontroller
-import circuitpython_typing
+from circuitpython_typing import ReadableBuffer
 from ._group import Group
 from ._displaybus import _DisplayBus
 
 from ._group import Group
 from ._displaybus import _DisplayBus
 
@@ -42,8 +42,8 @@ class EPaperDisplay:
     def __init__(
         self,
         display_bus: _DisplayBus,
     def __init__(
         self,
         display_bus: _DisplayBus,
-        start_sequence: circuitpython_typing.ReadableBuffer,
-        stop_sequence: circuitpython_typing.ReadableBuffer,
+        start_sequence: ReadableBuffer,
+        stop_sequence: ReadableBuffer,
         *,
         width: int,
         height: int,
         *,
         width: int,
         height: int,
@@ -84,9 +84,9 @@ class EPaperDisplay:
 
         :param display_bus: The bus that the display is connected to
         :type _DisplayBus: displayio.FourWire or displayio.ParallelBus
 
         :param display_bus: The bus that the display is connected to
         :type _DisplayBus: displayio.FourWire or displayio.ParallelBus
-        :param ~circuitpython_typing.ReadableBuffer start_sequence: Byte-packed
+        :param ~ReadableBuffer start_sequence: Byte-packed
             initialization sequence.
             initialization sequence.
-        :param ~circuitpython_typing.ReadableBuffer stop_sequence: Byte-packed
+        :param ~ReadableBuffer stop_sequence: Byte-packed
             initialization sequence.
         :param int width: Width in pixels
         :param int height: Height in pixels
             initialization sequence.
         :param int width: Width in pixels
         :param int height: Height in pixels
index 31b1be5e5a1ea1d578d9ab22640b966742f1145a..142e308cef10b13b01df1b285acb0b59942dcb35 100644 (file)
@@ -22,7 +22,7 @@ from typing import Optional
 import digitalio
 import busio
 import microcontroller
 import digitalio
 import busio
 import microcontroller
-import circuitpython_typing
+from circuitpython_typing import ReadableBuffer
 from ._constants import (
     CHIP_SELECT_TOGGLE_EVERY_BYTE,
     CHIP_SELECT_UNTOUCHED,
 from ._constants import (
     CHIP_SELECT_TOGGLE_EVERY_BYTE,
     CHIP_SELECT_UNTOUCHED,
@@ -95,7 +95,7 @@ class FourWire:
     def send(
         self,
         command,
     def send(
         self,
         command,
-        data: circuitpython_typing.ReadableBuffer,
+        data: ReadableBuffer,
         *,
         toggle_every_byte: bool = False,
     ) -> None:
         *,
         toggle_every_byte: bool = False,
     ) -> None:
@@ -120,7 +120,7 @@ class FourWire:
         self,
         data_type: int,
         chip_select: int,
         self,
         data_type: int,
         chip_select: int,
-        data: circuitpython_typing.ReadableBuffer,
+        data: ReadableBuffer,
     ):
         self._dc.value = data_type == DISPLAY_DATA
         if chip_select == CHIP_SELECT_TOGGLE_EVERY_BYTE:
     ):
         self._dc.value = data_type == DISPLAY_DATA
         if chip_select == CHIP_SELECT_TOGGLE_EVERY_BYTE:
index ed90f8e105a1f705f9bb7d6190ddb4be4587bedb..833b95c4aec3f65e6e56768f7efe420379f42727 100644 (file)
@@ -19,7 +19,7 @@ displayio for Blinka
 
 from __future__ import annotations
 from typing import Union, Callable
 
 from __future__ import annotations
 from typing import Union, Callable
-import circuitpython_typing
+from circuitpython_typing import WriteableBuffer
 from ._structs import TransformStruct
 from ._tilegrid import TileGrid
 from ._colorspace import Colorspace
 from ._structs import TransformStruct
 from ._tilegrid import TileGrid
 from ._colorspace import Colorspace
@@ -149,8 +149,8 @@ class Group:
         self,
         colorspace: Colorspace,
         area: Area,
         self,
         colorspace: Colorspace,
         area: Area,
-        mask: circuitpython_typing.WriteableBuffer,
-        buffer: circuitpython_typing.WriteableBuffer,
+        mask: WriteableBuffer,
+        buffer: WriteableBuffer,
     ) -> bool:
         if self._hidden_group:
             return False
     ) -> bool:
         if self._hidden_group:
             return False
index 67022f5c213048f4704d398d7a72f3429d270cf4..e37eb8a659be5b584b28e71777f7b7131b571426 100644 (file)
@@ -23,7 +23,7 @@ displayio for Blinka
 import time
 import busio
 import digitalio
 import time
 import busio
 import digitalio
-import circuitpython_typing
+from circuitpython_typing import ReadableBuffer
 from ._constants import CHIP_SELECT_UNTOUCHED, DISPLAY_COMMAND
 
 __version__ = "0.0.0+auto.0"
 from ._constants import CHIP_SELECT_UNTOUCHED, DISPLAY_COMMAND
 
 __version__ = "0.0.0+auto.0"
@@ -85,7 +85,7 @@ class I2CDisplay:
         while not self._i2c.try_lock():
             pass
 
         while not self._i2c.try_lock():
             pass
 
-    def send(self, command: int, data: circuitpython_typing.ReadableBuffer) -> None:
+    def send(self, command: int, data: ReadableBuffer) -> None:
         """
         Sends the given command value followed by the full set of data. Display state,
         such as vertical scroll, set via ``send`` may or may not be reset once the code is
         """
         Sends the given command value followed by the full set of data. Display state,
         such as vertical scroll, set via ``send`` may or may not be reset once the code is
@@ -99,7 +99,7 @@ class I2CDisplay:
         self,
         data_type: int,
         _chip_select: int,  # Chip select behavior
         self,
         data_type: int,
         _chip_select: int,  # Chip select behavior
-        data: circuitpython_typing.ReadableBuffer,
+        data: ReadableBuffer,
     ):
         if data_type == DISPLAY_COMMAND:
             n = len(data)
     ):
         if data_type == DISPLAY_COMMAND:
             n = len(data)
index af72dcbf4c1b216fa10eeec6c3207b77c8aa5d13..63d98eec3385ceea9e19ed6c556565abfb4f84c4 100644 (file)
@@ -18,7 +18,7 @@ displayio for Blinka
 """
 
 from typing import Optional, Union, Tuple
 """
 
 from typing import Optional, Union, Tuple
-import circuitpython_typing
+from circuitpython_typing import ReadableBuffer
 from ._colorconverter import ColorConverter
 from ._colorspace import Colorspace
 from ._structs import InputPixelStruct, OutputPixelStruct, ColorStruct
 from ._colorconverter import ColorConverter
 from ._colorspace import Colorspace
 from ._structs import InputPixelStruct, OutputPixelStruct, ColorStruct
@@ -63,7 +63,7 @@ class Palette:
     def __setitem__(
         self,
         index: int,
     def __setitem__(
         self,
         index: int,
-        value: Union[int, circuitpython_typing.ReadableBuffer, Tuple[int, int, int]],
+        value: Union[int, ReadableBuffer, Tuple[int, int, int]],
     ) -> None:
         """Sets the pixel color at the given index. The index should be
         an integer in the range 0 to color_count-1.
     ) -> None:
         """Sets the pixel color at the given index. The index should be
         an integer in the range 0 to color_count-1.
index a46fb48204529403ee4e8f8273fdf3396f86ce8a..3f0c2e1ccb6c7766e1df594c82f72c8af00660dc 100644 (file)
@@ -23,16 +23,6 @@ __version__ = "0.0.0+auto.0"
 __repo__ = "https://github.com/adafruit/Adafruit_Blinka_Displayio.git"
 
 
 __repo__ = "https://github.com/adafruit/Adafruit_Blinka_Displayio.git"
 
 
-@dataclass
-class RectangleStruct:
-    # pylint: disable=invalid-name
-    """Rectangle Struct Dataclass. To eventually be replaced by Area."""
-    x1: int
-    y1: int
-    x2: int
-    y2: int
-
-
 @dataclass
 class TransformStruct:
     # pylint: disable=invalid-name
 @dataclass
 class TransformStruct:
     # pylint: disable=invalid-name
index ec5e45f0640c171c3b102bb18d86d9d3ad6387cf..cd2766263c42904870de9e67b023cdb685ca081d 100644 (file)
@@ -19,7 +19,7 @@ displayio for Blinka
 
 import struct
 from typing import Union, Optional, Tuple
 
 import struct
 from typing import Union, Optional, Tuple
-import circuitpython_typing
+from circuitpython_typing import WriteableBuffer
 from ._bitmap import Bitmap
 from ._colorconverter import ColorConverter
 from ._ondiskbitmap import OnDiskBitmap
 from ._bitmap import Bitmap
 from ._colorconverter import ColorConverter
 from ._ondiskbitmap import OnDiskBitmap
@@ -215,8 +215,8 @@ class TileGrid:
         self,
         colorspace: Colorspace,
         area: Area,
         self,
         colorspace: Colorspace,
         area: Area,
-        mask: circuitpython_typing.WriteableBuffer,
-        buffer: circuitpython_typing.WriteableBuffer,
+        mask: WriteableBuffer,
+        buffer: WriteableBuffer,
     ) -> bool:
         """Draw onto the image"""
         # pylint: disable=too-many-locals,too-many-branches,too-many-statements
     ) -> bool:
         """Draw onto the image"""
         # pylint: disable=too-many-locals,too-many-branches,too-many-statements
@@ -335,16 +335,18 @@ class TileGrid:
                 else:
                     mask[offset // 32] |= 1 << (offset % 32)
                     if colorspace.depth == 16:
                 else:
                     mask[offset // 32] |= 1 << (offset % 32)
                     if colorspace.depth == 16:
-                        buffer = (
-                            buffer[:offset]
-                            + struct.pack("H", output_pixel.pixel)
-                            + buffer[offset + 2 :]
+                        struct.pack_into(
+                            "H",
+                            buffer,
+                            offset,
+                            output_pixel.pixel,
                         )
                     elif colorspace.depth == 32:
                         )
                     elif colorspace.depth == 32:
-                        buffer = (
-                            buffer[:offset]
-                            + struct.pack("I", output_pixel.pixel)
-                            + buffer[offset + 4 :]
+                        struct.pack_into(
+                            "I",
+                            buffer,
+                            offset,
+                            output_pixel.pixel,
                         )
                     elif colorspace.depth == 8:
                         buffer[offset] = output_pixel.pixel & 0xFF
                         )
                     elif colorspace.depth == 8:
                         buffer[offset] = output_pixel.pixel & 0xFF