]> Repositories - hackapet/Adafruit_Blinka_Displayio.git/blob - displayio/_displaycore.py
Update to pyproject.toml
[hackapet/Adafruit_Blinka_Displayio.git] / displayio / _displaycore.py
1 # SPDX-FileCopyrightText: 2021 Melissa LeBlanc-Williams for Adafruit Industries
2 # SPDX-FileCopyrightText: 2021 James Carr
3 #
4 # SPDX-License-Identifier: MIT
5
6 """
7 `displayio._displaycore`
8 ================================================================================
9
10 Super class of the display classes
11
12 **Software and Dependencies:**
13
14 * Adafruit Blinka:
15   https://github.com/adafruit/Adafruit_Blinka/releases
16
17 * Author(s): James Carr, Melissa LeBlanc-Williams
18
19 """
20
21 __version__ = "0.0.0+auto.0"
22 __repo__ = "https://github.com/adafruit/Adafruit_Blinka_Displayio.git"
23
24
25 from typing import Union
26 import circuitpython_typing
27 from paralleldisplay import ParallelBus
28 from ._fourwire import FourWire
29 from ._group import Group
30 from ._i2cdisplay import I2CDisplay
31 from ._structs import ColorspaceStruct, TransformStruct, RectangleStruct
32 from ._area import Area
33
34 displays = []
35
36
37 class _DisplayCore:
38     # pylint: disable=too-many-arguments, too-many-instance-attributes
39
40     def __init__(
41         self,
42         bus,
43         width: int,
44         height: int,
45         ram_width: int,
46         ram_height: int,
47         colstart: int,
48         rowstart: int,
49         rotation: int,
50         color_depth: int,
51         grayscale: bool,
52         pixels_in_byte_share_row: bool,
53         bytes_per_cell: int,
54         reverse_pixels_in_byte: bool,
55         reverse_bytes_in_word: bool,
56     ):
57         self._colorspace = ColorspaceStruct(
58             depth=color_depth,
59             grayscale=grayscale,
60             grayscale_bit=8 - color_depth,
61             pixels_in_byte_share_row=pixels_in_byte_share_row,
62             bytes_per_cell=bytes_per_cell,
63             reverse_pixels_in_byte=reverse_pixels_in_byte,
64             reverse_bytes_in_word=reverse_bytes_in_word,
65             dither=False,
66         )
67         self._current_group = None
68         self._colstart = colstart
69         self._rowstart = rowstart
70         self._last_refresh = 0
71         self._refresh_in_progress = False
72         self._full_refresh = False
73
74         if bus:
75             if isinstance(bus, (FourWire, I2CDisplay, ParallelBus)):
76                 self._bus_reset = bus.reset
77                 self._begin_transaction = bus._begin_transaction
78                 self._send = bus._send
79                 self._end_transaction = bus._end_transaction
80             else:
81                 raise ValueError("Unsupported display bus type")
82
83         self._bus = bus
84         self._area = Area(0, 0, width, height)
85
86         self._width = width
87         self._height = height
88         self._ram_width = ram_width
89         self._ram_height = ram_height
90         self._rotation = rotation
91         self._transform = TransformStruct()
92
93     def set_rotation(self, rotation: int) -> None:
94         """
95         Sets the rotation of the display as an int in degrees.
96         """
97         # pylint: disable=protected-access, too-many-branches
98         transposed = self._rotation in (90, 270)
99         will_be_transposed = rotation in (90, 270)
100         if transposed != will_be_transposed:
101             self._width, self._height = self._height, self._width
102
103         height = self._height
104         width = self._width
105
106         rotation %= 360
107         self._rotation = rotation
108         self._transform.x = 0
109         self._transform.y = 0
110         self._transform.scale = 1
111         self._transform.mirror_x = False
112         self._transform.mirror_y = False
113         self._transform.transpose_xy = False
114
115         if rotation in (0, 180):
116             if rotation == 180:
117                 self._transform.mirror_x = True
118                 self._transform.mirror_y = True
119         else:
120             self._transform.transpose_xy = True
121             if rotation == 270:
122                 self._transform.mirror_y = True
123             else:
124                 self._transform.mirror_x = True
125
126         self._area.x1 = 0
127         self._area.y1 = 0
128         self._area.next = None
129
130         self._transform.dx = 1
131         self._transform.dy = 1
132         if self._transform.transpose_xy:
133             self._area.x2 = height
134             self._area.y2 = width
135             if self._transform.mirror_x:
136                 self._transform.x = height
137                 self._transform.dx = -1
138             if self._transform.mirror_y:
139                 self._transform.y = width
140                 self._transform.dy = -1
141         else:
142             self._area.x2 = width
143             self._area.y2 = height
144             if self._transform.mirror_x:
145                 self._transform.x = width
146                 self._transform.dx = -1
147             if self._transform.mirror_y:
148                 self._transform.y = height
149                 self._transform.dy = -1
150
151         if self._current_group is not None:
152             self._current_group._update_transform(self._transform)
153
154     def show(self, root_group: Group) -> bool:
155         # pylint: disable=protected-access
156
157         """
158         Switches to displaying the given group of layers. When group is `None`, the
159         default CircuitPython terminal will be shown.
160
161         :param Optional[displayio.Group] root_group: The group to show.
162         """
163
164         """
165         # TODO: Implement Supervisor
166         if root_group is None:
167             circuitpython_splash = _Supervisor().circuitpython_splash
168             if not circuitpython_splash._in_group:
169                 root_group = circuitpython_splash
170             elif self._current_group == circuitpython_splash:
171                 return True
172         """
173
174         if root_group == self._current_group:
175             return True
176
177         if root_group is not None and root_group._in_group:
178             return False
179
180         if self._current_group is not None:
181             self._current_group._in_group = False
182
183         if root_group is not None:
184             root_group._update_transform(self._transform)
185             root_group._in_group = True
186
187         self._current_group = root_group
188         self._full_refresh = True
189
190         return True
191
192     def start_refresh(self) -> bool:
193         # pylint: disable=protected-access
194         """Mark the display core as currently being refreshed"""
195
196         if self._refresh_in_progress:
197             return False
198
199         self._refresh_in_progress = True
200         # self._last_refresh = _Supervisor()._ticks_ms64()
201         return True
202
203     def finish_refresh(self) -> None:
204         # pylint: disable=protected-access
205         """Unmark the display core as currently being refreshed"""
206
207         if self._current_group is not None:
208             self._current_group._finish_refresh()
209
210         self._full_refresh = False
211         self._refresh_in_progress = False
212         # self._last_refresh = _Supervisor()._ticks_ms64()
213
214     def get_refresh_areas(self) -> list:
215         """Get a list of areas to be refreshed"""
216         subrectangles = []
217         if self._current_group is not None:
218             # Eventually calculate dirty rectangles here
219             subrectangles.append(RectangleStruct(0, 0, self._width, self._height))
220         return subrectangles
221
222     def release(self) -> None:
223         """Release the display from the current group"""
224         # pylint: disable=protected-access
225
226         if self._current_group is not None:
227             self._current_group._in_group = False
228
229     def fill_area(
230         self,
231         area: Area,
232         mask: circuitpython_typing.WriteableBuffer,
233         buffer: circuitpython_typing.WriteableBuffer,
234     ) -> bool:
235         # pylint: disable=protected-access
236         """Call the current group's fill area function"""
237
238         return self._current_group._fill_area(self._colorspace, area, mask, buffer)
239
240     def clip_area(self, area: Area, clipped: Area) -> bool:
241         """Shrink the area to the region shared by the two areas"""
242         # pylint: disable=protected-access
243
244         overlaps = self._area._compute_overlap(area, clipped)
245         if not overlaps:
246             return False
247
248         # Expand the area if we have multiple pixels per byte and we need to byte align the bounds
249         if self._colorspace.depth < 8:
250             pixels_per_byte = (
251                 8 // self._colorspace.depth * self._colorspace.bytes_per_cell
252             )
253             if self._colorspace.pixels_in_byte_share_row:
254                 if clipped.x1 % pixels_per_byte != 0:
255                     clipped.x1 -= clipped.x1 % pixels_per_byte
256                 if clipped.x2 % pixels_per_byte != 0:
257                     clipped.x2 += pixels_per_byte - clipped.x2 % pixels_per_byte
258             else:
259                 if clipped.y1 % pixels_per_byte != 0:
260                     clipped.y1 -= clipped.y1 % pixels_per_byte
261                 if clipped.y2 % pixels_per_byte != 0:
262                     clipped.y2 += pixels_per_byte - clipped.y2 % pixels_per_byte
263
264         return True
265
266     def send(
267         self,
268         data_type: int,
269         chip_select: int,
270         data: circuitpython_typing.ReadableBuffer,
271     ) -> None:
272         """
273         Send the data to the current bus
274         """
275         self._send(data_type, chip_select, data)
276
277     def begin_transaction(self) -> None:
278         """
279         Begin Bus Transaction
280         """
281         self._begin_transaction()
282
283     def end_transaction(self) -> None:
284         """
285         End Bus Transaction
286         """
287         self._end_transaction()
288
289     def get_width(self) -> int:
290         """
291         Gets the width of the display in pixels.
292         """
293         return self._width
294
295     def get_height(self) -> int:
296         """
297         Gets the height of the display in pixels.
298         """
299         return self._height
300
301     def get_rotation(self) -> int:
302         """
303         Gets the rotation of the display as an int in degrees.
304         """
305         return self._rotation
306
307     def get_bus(self) -> Union[FourWire, ParallelBus, I2CDisplay]:
308         """
309         The bus being used by the display. [readonly]
310         """
311         return self._bus