]> Repositories - hackapet/Adafruit_Blinka_Displayio.git/blob - displayio/_shape.py
Slight optimization and uses CP font as default
[hackapet/Adafruit_Blinka_Displayio.git] / displayio / _shape.py
1 # SPDX-FileCopyrightText: 2020 Melissa LeBlanc-Williams for Adafruit Industries
2 #
3 # SPDX-License-Identifier: MIT
4
5
6 """
7 `displayio.shape`
8 ================================================================================
9
10 displayio for Blinka
11
12 **Software and Dependencies:**
13
14 * Adafruit Blinka:
15   https://github.com/adafruit/Adafruit_Blinka/releases
16
17 * Author(s): Melissa LeBlanc-Williams
18
19 """
20
21 from ._bitmap import Bitmap
22 from ._area import Area
23 from ._helpers import clamp
24
25 __version__ = "0.0.0+auto.0"
26 __repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
27
28
29 class Shape(Bitmap):
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
32     full row.
33     """
34
35     def __init__(
36         self, width: int, height: int, *, mirror_x: bool = False, mirror_y: bool = False
37     ):
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.
41         """
42         self._mirror_x = mirror_x
43         self._mirror_y = mirror_y
44         self._width = width
45         self._height = height
46         if self._mirror_x:
47             width //= 2
48             width += self._width % 2
49         self._half_width = width
50         if self._mirror_y:
51             height //= 2
52             height += self._height % 2
53         self._half_height = height
54         self._data = bytearray(height * 4)
55         for i in range(height):
56             self._data[2 * i] = 0
57             self._data[2 * i + 1] = width
58
59         self._dirty_area = Area(0, 0, width, height)
60         super().__init__(width, height, 2)
61
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
65         if self._mirror_y:
66             max_y = self._half_height - 1
67         y = clamp(y, 0, max_y)
68         max_x = self._width - 1
69         if self._mirror_x:
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)
73
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])
76
77         if self._mirror_x:
78             upper_x = (
79                 self._width - lower_x + 1
80             )  # dirty rectangles are treated with max value exclusive
81         else:
82             upper_x = max(
83                 end_x, self._data[2 * y + 1]
84             )  # dirty rectangles are treated with max value exclusive
85
86         # find y-boundaries based on y and mirror_y
87         lower_y = y
88
89         if self._mirror_y:
90             upper_y = (
91                 self._height - lower_y + 1
92             )  # dirty rectangles are treated with max value exclusive
93         else:
94             upper_y = y + 1  # dirty rectangles are treated with max value exclusive
95
96         self._data[2 * y] = start_x  # update the data array with the new boundaries
97         self._data[2 * y + 1] = end_x
98
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
104         else:
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)
109
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:
112             return 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:
120             return 0
121         return 1
122
123     def _finish_refresh(self):
124         self._dirty_area.x1 = 0
125         self._dirty_area.x2 = 0
126
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)