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)