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)