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
 
  21 from ._bitmap import Bitmap
 
  22 from ._area import Area
 
  23 from ._helpers import clamp
 
  25 __version__ = "0.0.0+auto.0"
 
  26 __repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
 
  30     """Create a Shape object with the given fixed size. Each pixel is one bit and is stored
 
  31     by the column boundaries of the shape on each row. Each row’s boundary defaults to the
 
  36         self, width: int, height: int, *, mirror_x: bool = False, mirror_y: bool = False
 
  38         """Create a Shape object with the given fixed size. Each pixel is one bit and is
 
  39         stored by the column boundaries of the shape on each row. Each row’s boundary
 
  40         defaults to the full row.
 
  42         self._mirror_x = mirror_x
 
  43         self._mirror_y = mirror_y
 
  48             width += self._width % 2
 
  49         self._half_width = width
 
  52             height += self._height % 2
 
  53         self._half_height = height
 
  54         self._data = bytearray(height * 4)
 
  55         for i in range(height):
 
  57             self._data[2 * i + 1] = width
 
  59         self._dirty_area = Area(0, 0, width, height)
 
  60         super().__init__(width, height, 2)
 
  62     def set_boundary(self, y: int, start_x: int, end_x: int) -> None:
 
  63         """Loads pre-packed data into the given row."""
 
  64         max_y = self._height - 1
 
  66             max_y = self._half_height - 1
 
  67         y = clamp(y, 0, max_y)
 
  68         max_x = self._width - 1
 
  70             max_x = self._half_width - 1
 
  71         start_x = clamp(start_x, 0, max_x)
 
  72         end_x = clamp(end_x, 0, max_x)
 
  74         # find x-boundaries for updating based on current data and start_x, end_x, and mirror_x
 
  75         lower_x = min(start_x, self._data[2 * y])
 
  79                 self._width - lower_x + 1
 
  80             )  # dirty rectangles are treated with max value exclusive
 
  83                 end_x, self._data[2 * y + 1]
 
  84             )  # dirty rectangles are treated with max value exclusive
 
  86         # find y-boundaries based on y and mirror_y
 
  91                 self._height - lower_y + 1
 
  92             )  # dirty rectangles are treated with max value exclusive
 
  94             upper_y = y + 1  # dirty rectangles are treated with max value exclusive
 
  96         self._data[2 * y] = start_x  # update the data array with the new boundaries
 
  97         self._data[2 * y + 1] = end_x
 
  99         if self._dirty_area.x1 == self._dirty_area.x2:  # dirty region is empty
 
 100             self._dirty_area.x1 = lower_x
 
 101             self._dirty_area.x2 = upper_x
 
 102             self._dirty_area.y1 = lower_y
 
 103             self._dirty_area.y2 = upper_y
 
 105             self._dirty_area.x1 = min(lower_x, self._dirty_area.x1)
 
 106             self._dirty_area.x2 = max(upper_x, self._dirty_area.x2)
 
 107             self._dirty_area.y1 = min(lower_y, self._dirty_area.y1)
 
 108             self._dirty_area.y2 = max(upper_y, self._dirty_area.y2)
 
 110     def _get_pixel(self, x: int, y: int) -> int:
 
 111         if x >= self._width or x < 0 or y >= self._height or y < 0:
 
 113         if self._mirror_x and x >= self._half_width:
 
 114             x = self._width - x - 1
 
 115         if self._mirror_y and y >= self._half_height:
 
 116             y = self._height - y - 1
 
 117         start_x = self._data[2 * y]
 
 118         end_x = self._data[2 * y + 1]
 
 119         if x < start_x or x >= end_x:
 
 123     def _finish_refresh(self):
 
 124         self._dirty_area.x1 = 0
 
 125         self._dirty_area.x2 = 0
 
 127     def _get_refresh_areas(self, areas: list[Area]) -> None:
 
 128         if self._dirty_area.x1 != self._dirty_area.x2:
 
 129             areas.append(self._dirty_area)