1 # SPDX-FileCopyrightText: 2020 Melissa LeBlanc-Williams for Adafruit Industries
 
   3 # SPDX-License-Identifier: MIT
 
   8 ================================================================================
 
  12 **Software and Dependencies:**
 
  15   https://github.com/adafruit/Adafruit_Blinka/releases
 
  17 * Author(s): Melissa LeBlanc-Williams
 
  22 from ._bitmap import Bitmap
 
  23 from ._area import Area
 
  24 from ._helpers import clamp
 
  26 __version__ = "0.0.0+auto.0"
 
  27 __repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
 
  31     """Create a Shape object with the given fixed size. Each pixel is one bit and is stored
 
  32     by the column boundaries of the shape on each row. Each row’s boundary defaults to the
 
  37         self, width: int, height: int, *, mirror_x: bool = False, mirror_y: bool = False
 
  39         """Create a Shape object with the given fixed size. Each pixel is one bit and is
 
  40         stored by the column boundaries of the shape on each row. Each row’s boundary
 
  41         defaults to the full row.
 
  43         self._mirror_x = mirror_x
 
  44         self._mirror_y = mirror_y
 
  49             width += self._width % 2
 
  50         self._half_width = width
 
  53             height += self._height % 2
 
  54         self._half_height = height
 
  55         self._data = bytearray(height * struct.calcsize("HH"))
 
  56         for i in range(height):
 
  58             self._data[2 * i + 1] = width
 
  60         self._dirty_area = Area(0, 0, width, height)
 
  61         super().__init__(width, height, 2)
 
  63     def set_boundary(self, y: int, start_x: int, end_x: int) -> None:
 
  64         """Loads pre-packed data into the given row."""
 
  65         max_y = self._height - 1
 
  67             max_y = self._half_height - 1
 
  68         y = clamp(y, 0, max_y)
 
  69         max_x = self._width - 1
 
  71             max_x = self._half_width - 1
 
  72         start_x = clamp(start_x, 0, max_x)
 
  73         end_x = clamp(end_x, 0, max_x)
 
  75         # find x-boundaries for updating based on current data and start_x, end_x, and mirror_x
 
  76         lower_x = min(start_x, self._data[2 * y])
 
  80                 self._width - lower_x + 1
 
  81             )  # dirty rectangles are treated with max value exclusive
 
  84                 end_x, self._data[2 * y + 1]
 
  85             )  # dirty rectangles are treated with max value exclusive
 
  87         # find y-boundaries based on y and mirror_y
 
  92                 self._height - lower_y + 1
 
  93             )  # dirty rectangles are treated with max value exclusive
 
  95             upper_y = y + 1  # dirty rectangles are treated with max value exclusive
 
  97         self._data[2 * y] = start_x  # update the data array with the new boundaries
 
  98         self._data[2 * y + 1] = end_x
 
 100         if self._dirty_area.x1 == self._dirty_area.x2:  # dirty region is empty
 
 101             self._dirty_area.x1 = lower_x
 
 102             self._dirty_area.x2 = upper_x
 
 103             self._dirty_area.y1 = lower_y
 
 104             self._dirty_area.y2 = upper_y
 
 106             self._dirty_area.x1 = min(lower_x, self._dirty_area.x1)
 
 107             self._dirty_area.x2 = max(upper_x, self._dirty_area.x2)
 
 108             self._dirty_area.y1 = min(lower_y, self._dirty_area.y1)
 
 109             self._dirty_area.y2 = max(upper_y, self._dirty_area.y2)
 
 111     def _get_pixel(self, x: int, y: int) -> int:
 
 112         if x >= self._width or x < 0 or y >= self._height or y < 0:
 
 114         if self._mirror_x and x >= self._half_width:
 
 115             x = self._width - x - 1
 
 116         if self._mirror_y and y >= self._half_height:
 
 117             y = self._height - y - 1
 
 118         start_x = self._data[2 * y]
 
 119         end_x = self._data[2 * y + 1]
 
 120         if x < start_x or x >= end_x:
 
 124     def _finish_refresh(self):
 
 125         self._dirty_area.x1 = 0
 
 126         self._dirty_area.x2 = 0
 
 128     def _get_refresh_areas(self, areas: list[Area]) -> None:
 
 129         if self._dirty_area.x1 != self._dirty_area.x2:
 
 130             areas.append(self._dirty_area)