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"
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 from ._displaybus import _DisplayBus
37 # pylint: disable=too-many-arguments, too-many-instance-attributes, too-many-locals
51 pixels_in_byte_share_row: bool,
53 reverse_pixels_in_byte: bool,
54 reverse_bytes_in_word: bool,
57 set_current_column_command: int,
58 set_current_row_command: int,
59 data_as_commands: bool,
60 always_toggle_chip_select: bool,
61 sh1107_addressing: bool,
62 address_little_endian: bool,
64 self.colorspace = ColorspaceStruct(
67 grayscale_bit=8 - color_depth,
68 pixels_in_byte_share_row=pixels_in_byte_share_row,
69 bytes_per_cell=bytes_per_cell,
70 reverse_pixels_in_byte=reverse_pixels_in_byte,
71 reverse_bytes_in_word=reverse_bytes_in_word,
74 self.current_group = None
75 self.colstart = colstart
76 self.rowstart = rowstart
79 self.column_command = column_command
80 self.row_command = row_command
81 self.set_current_column_command = set_current_column_command
82 self.set_current_row_command = set_current_row_command
83 self.data_as_commands = data_as_commands
84 self.always_toggle_chip_select = always_toggle_chip_select
85 self.sh1107_addressing = sh1107_addressing
86 self.address_little_endian = address_little_endian
88 self.refresh_in_progress = False
89 self.full_refresh = False
93 if isinstance(bus, (FourWire, I2CDisplay, ParallelBus)):
94 self._bus_reset = bus.reset
95 self._begin_transaction = bus._begin_transaction
96 self._send = bus._send
97 self._end_transaction = bus._end_transaction
99 raise ValueError("Unsupported display bus type")
102 self.area = Area(0, 0, width, height)
106 self.ram_width = ram_width
107 self.ram_height = ram_height
108 self.rotation = rotation
109 self.transform = TransformStruct()
111 def set_rotation(self, rotation: int) -> None:
113 Sets the rotation of the display as an int in degrees.
115 # pylint: disable=protected-access, too-many-branches
116 transposed = self.rotation in (90, 270)
117 will_be_transposed = rotation in (90, 270)
118 if transposed != will_be_transposed:
119 self.width, self.height = self.height, self.width
125 self.rotation = rotation
128 self.transform.scale = 1
129 self.transform.mirror_x = False
130 self.transform.mirror_y = False
131 self.transform.transpose_xy = False
133 if rotation in (0, 180):
135 self.transform.mirror_x = True
136 self.transform.mirror_y = True
138 self.transform.transpose_xy = True
140 self.transform.mirror_y = True
142 self.transform.mirror_x = True
146 self.area.next = None
148 self.transform.dx = 1
149 self.transform.dy = 1
150 if self.transform.transpose_xy:
151 self.area.x2 = height
153 if self.transform.mirror_x:
154 self.transform.x = height
155 self.transform.dx = -1
156 if self.transform.mirror_y:
157 self.transform.y = width
158 self.transform.dy = -1
161 self.area.y2 = height
162 if self.transform.mirror_x:
163 self.transform.x = width
164 self.transform.dx = -1
165 if self.transform.mirror_y:
166 self.transform.y = height
167 self.transform.dy = -1
169 if self.current_group is not None:
170 self.current_group._update_transform(self.transform)
172 def show(self, root_group: Group) -> bool:
173 # pylint: disable=protected-access
176 Switches to displaying the given group of layers. When group is `None`, the
177 default CircuitPython terminal will be shown.
179 :param Optional[displayio.Group] root_group: The group to show.
183 # TODO: Implement Supervisor
184 if root_group is None:
185 circuitpython_splash = _Supervisor().circuitpython_splash
186 if not circuitpython_splash._in_group:
187 root_group = circuitpython_splash
188 elif self.current_group == circuitpython_splash:
192 if root_group == self.current_group:
195 if root_group is not None and root_group._in_group:
198 if self.current_group is not None:
199 self.current_group._in_group = False
201 if root_group is not None:
202 root_group._update_transform(self.transform)
203 root_group._in_group = True
205 self.current_group = root_group
206 self.full_refresh = True
210 def start_refresh(self) -> bool:
211 # pylint: disable=protected-access
212 """Mark the display core as currently being refreshed"""
214 if self.refresh_in_progress:
217 self.refresh_in_progress = True
218 self.last_refresh = time.monotonic() * 1000
221 def finish_refresh(self) -> None:
222 # pylint: disable=protected-access
223 """Unmark the display core as currently being refreshed"""
225 if self.current_group is not None:
226 self.current_group._finish_refresh()
228 self.full_refresh = False
229 self.refresh_in_progress = False
230 self.last_refresh = time.monotonic() * 1000
232 def get_refresh_areas(self) -> list:
233 """Get a list of areas to be refreshed"""
235 if self.current_group is not None:
236 # Eventually calculate dirty rectangles here
237 subrectangles.append(RectangleStruct(0, 0, self.width, self.height))
240 def release(self) -> None:
241 """Release the display from the current group"""
242 # pylint: disable=protected-access
244 if self.current_group is not None:
245 self.current_group._in_group = False
250 mask: circuitpython_typing.WriteableBuffer,
251 buffer: circuitpython_typing.WriteableBuffer,
253 # pylint: disable=protected-access
254 """Call the current group's fill area function"""
256 return self.current_group._fill_area(self.colorspace, area, mask, buffer)
258 def clip_area(self, area: Area, clipped: Area) -> bool:
259 """Shrink the area to the region shared by the two areas"""
260 # pylint: disable=protected-access
262 overlaps = self.area._compute_overlap(area, clipped)
266 # Expand the area if we have multiple pixels per byte and we need to byte align the bounds
267 if self.colorspace.depth < 8:
269 8 // self.colorspace.depth * self.colorspace.bytes_per_cell
271 if self.colorspace.pixels_in_byte_share_row:
272 if clipped.x1 % pixels_per_byte != 0:
273 clipped.x1 -= clipped.x1 % pixels_per_byte
274 if clipped.x2 % pixels_per_byte != 0:
275 clipped.x2 += pixels_per_byte - clipped.x2 % pixels_per_byte
277 if clipped.y1 % pixels_per_byte != 0:
278 clipped.y1 -= clipped.y1 % pixels_per_byte
279 if clipped.y2 % pixels_per_byte != 0:
280 clipped.y2 += pixels_per_byte - clipped.y2 % pixels_per_byte
288 data: circuitpython_typing.ReadableBuffer,
291 Send the data to the current bus
293 self._send(data_type, chip_select, data)
295 def begin_transaction(self) -> None:
297 Begin Bus Transaction
299 self._begin_transaction()
301 def end_transaction(self) -> None:
305 self._end_transaction()
307 def get_width(self) -> int:
309 Gets the width of the display in pixels.
313 def get_height(self) -> int:
315 Gets the height of the display in pixels.
319 def get_rotation(self) -> int:
321 Gets the rotation of the display as an int in degrees.
325 def get_bus(self) -> _DisplayBus:
327 The bus being used by the display. [readonly]