1 # SPDX-FileCopyrightText: 2021 Melissa LeBlanc-Williams for Adafruit Industries
2 # SPDX-FileCopyrightText: 2021 James Carr
4 # SPDX-License-Identifier: MIT
7 `displayio._displaycore`
8 ================================================================================
10 Super class of the display classes
12 **Software and Dependencies:**
15 https://github.com/adafruit/Adafruit_Blinka/releases
17 * Author(s): James Carr, Melissa LeBlanc-Williams
21 __version__ = "0.0.0+auto.0"
22 __repo__ = "https://github.com/adafruit/Adafruit_Blinka_Displayio.git"
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
38 # pylint: disable=too-many-arguments, too-many-instance-attributes
52 pixels_in_byte_share_row: bool,
54 reverse_pixels_in_byte: bool,
55 reverse_bytes_in_word: bool,
57 self._colorspace = ColorspaceStruct(
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,
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
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
81 raise ValueError("Unsupported display bus type")
84 self._area = Area(0, 0, width, height)
88 self._ram_width = ram_width
89 self._ram_height = ram_height
90 self._rotation = rotation
91 self._transform = TransformStruct()
93 def set_rotation(self, rotation: int) -> None:
95 Sets the rotation of the display as an int in degrees.
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
103 height = self._height
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
115 if rotation in (0, 180):
117 self._transform.mirror_x = True
118 self._transform.mirror_y = True
120 self._transform.transpose_xy = True
122 self._transform.mirror_y = True
124 self._transform.mirror_x = True
128 self._area.next = None
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
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
151 if self._current_group is not None:
152 self._current_group._update_transform(self._transform)
154 def show(self, root_group: Group) -> bool:
155 # pylint: disable=protected-access
158 Switches to displaying the given group of layers. When group is `None`, the
159 default CircuitPython terminal will be shown.
161 :param Optional[displayio.Group] root_group: The group to show.
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:
174 if root_group == self._current_group:
177 if root_group is not None and root_group._in_group:
180 if self._current_group is not None:
181 self._current_group._in_group = False
183 if root_group is not None:
184 root_group._update_transform(self._transform)
185 root_group._in_group = True
187 self._current_group = root_group
188 self._full_refresh = True
190 print(f"end of show: {root_group}")
193 def set_root_group(self, root_group: Group) -> bool:
194 # pylint: disable=protected-access
197 Switches to displaying the given group of layers. When group is `None`, the
198 default CircuitPython terminal will be shown.
200 :param Optional[displayio.Group] root_group: The group to show.
204 # TODO: Implement Supervisor
205 if root_group is None:
206 circuitpython_splash = _Supervisor().circuitpython_splash
207 if not circuitpython_splash._in_group:
208 root_group = circuitpython_splash
209 elif self._current_group == circuitpython_splash:
213 if root_group == self._current_group:
216 if root_group is not None and root_group._in_group:
219 if self._current_group is not None:
220 self._current_group._in_group = False
222 if root_group is not None:
223 root_group._update_transform(self._transform)
224 root_group._in_group = True
226 self._current_group = root_group
227 self._full_refresh = True
231 def start_refresh(self) -> bool:
232 # pylint: disable=protected-access
233 """Mark the display core as currently being refreshed"""
235 if self._refresh_in_progress:
238 self._refresh_in_progress = True
239 # self._last_refresh = _Supervisor()._ticks_ms64()
242 def finish_refresh(self) -> None:
243 # pylint: disable=protected-access
244 """Unmark the display core as currently being refreshed"""
246 if self._current_group is not None:
247 self._current_group._finish_refresh()
249 self._full_refresh = False
250 self._refresh_in_progress = False
251 # self._last_refresh = _Supervisor()._ticks_ms64()
253 def get_refresh_areas(self) -> list:
254 """Get a list of areas to be refreshed"""
256 if self._current_group is not None:
257 # Eventually calculate dirty rectangles here
258 subrectangles.append(RectangleStruct(0, 0, self._width, self._height))
261 def release(self) -> None:
262 """Release the display from the current group"""
263 # pylint: disable=protected-access
265 if self._current_group is not None:
266 self._current_group._in_group = False
271 mask: circuitpython_typing.WriteableBuffer,
272 buffer: circuitpython_typing.WriteableBuffer,
274 # pylint: disable=protected-access
275 """Call the current group's fill area function"""
277 return self._current_group._fill_area(self._colorspace, area, mask, buffer)
279 def clip_area(self, area: Area, clipped: Area) -> bool:
280 """Shrink the area to the region shared by the two areas"""
281 # pylint: disable=protected-access
283 overlaps = self._area._compute_overlap(area, clipped)
287 # Expand the area if we have multiple pixels per byte and we need to byte align the bounds
288 if self._colorspace.depth < 8:
290 8 // self._colorspace.depth * self._colorspace.bytes_per_cell
292 if self._colorspace.pixels_in_byte_share_row:
293 if clipped.x1 % pixels_per_byte != 0:
294 clipped.x1 -= clipped.x1 % pixels_per_byte
295 if clipped.x2 % pixels_per_byte != 0:
296 clipped.x2 += pixels_per_byte - clipped.x2 % pixels_per_byte
298 if clipped.y1 % pixels_per_byte != 0:
299 clipped.y1 -= clipped.y1 % pixels_per_byte
300 if clipped.y2 % pixels_per_byte != 0:
301 clipped.y2 += pixels_per_byte - clipped.y2 % pixels_per_byte
309 data: circuitpython_typing.ReadableBuffer,
312 Send the data to the current bus
314 self._send(data_type, chip_select, data)
316 def begin_transaction(self) -> None:
318 Begin Bus Transaction
320 self._begin_transaction()
322 def end_transaction(self) -> None:
326 self._end_transaction()
328 def get_width(self) -> int:
330 Gets the width of the display in pixels.
334 def get_height(self) -> int:
336 Gets the height of the display in pixels.
340 def get_rotation(self) -> int:
342 Gets the rotation of the display as an int in degrees.
344 return self._rotation
346 def get_bus(self) -> Union[FourWire, ParallelBus, I2CDisplay]:
348 The bus being used by the display. [readonly]