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
 
 192     def start_refresh(self) -> bool:
 
 193         # pylint: disable=protected-access
 
 194         """Mark the display core as currently being refreshed"""
 
 196         if self._refresh_in_progress:
 
 199         self._refresh_in_progress = True
 
 200         # self._last_refresh = _Supervisor()._ticks_ms64()
 
 203     def finish_refresh(self) -> None:
 
 204         # pylint: disable=protected-access
 
 205         """Unmark the display core as currently being refreshed"""
 
 207         if self._current_group is not None:
 
 208             self._current_group._finish_refresh()
 
 210         self._full_refresh = False
 
 211         self._refresh_in_progress = False
 
 212         # self._last_refresh = _Supervisor()._ticks_ms64()
 
 214     def get_refresh_areas(self) -> list:
 
 215         """Get a list of areas to be refreshed"""
 
 217         if self._current_group is not None:
 
 218             # Eventually calculate dirty rectangles here
 
 219             subrectangles.append(RectangleStruct(0, 0, self._width, self._height))
 
 222     def release(self) -> None:
 
 223         """Release the display from the current group"""
 
 224         # pylint: disable=protected-access
 
 226         if self._current_group is not None:
 
 227             self._current_group._in_group = False
 
 232         mask: circuitpython_typing.WriteableBuffer,
 
 233         buffer: circuitpython_typing.WriteableBuffer,
 
 235         # pylint: disable=protected-access
 
 236         """Call the current group's fill area function"""
 
 238         return self._current_group._fill_area(self._colorspace, area, mask, buffer)
 
 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
 
 244         overlaps = self._area._compute_overlap(area, clipped)
 
 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:
 
 251                 8 // self._colorspace.depth * self._colorspace.bytes_per_cell
 
 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
 
 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
 
 270         data: circuitpython_typing.ReadableBuffer,
 
 273         Send the data to the current bus
 
 275         self._send(data_type, chip_select, data)
 
 277     def begin_transaction(self) -> None:
 
 279         Begin Bus Transaction
 
 281         self._begin_transaction()
 
 283     def end_transaction(self) -> None:
 
 287         self._end_transaction()
 
 289     def get_width(self) -> int:
 
 291         Gets the width of the display in pixels.
 
 295     def get_height(self) -> int:
 
 297         Gets the height of the display in pixels.
 
 301     def get_rotation(self) -> int:
 
 303         Gets the rotation of the display as an int in degrees.
 
 305         return self._rotation
 
 307     def get_bus(self) -> Union[FourWire, ParallelBus, I2CDisplay]:
 
 309         The bus being used by the display. [readonly]