--- /dev/null
+# SPDX-FileCopyrightText: 2021 Melissa LeBlanc-Williams for Adafruit Industries
+# SPDX-FileCopyrightText: 2021 James Carr
+#
+# SPDX-License-Identifier: MIT
+
+"""
+`displayio._displaycore`
+================================================================================
+
+Super class of the display classes
+
+**Software and Dependencies:**
+
+* Adafruit Blinka:
+ https://github.com/adafruit/Adafruit_Blinka/releases
+
+* Author(s): James Carr, Melissa LeBlanc-Williams
+
+"""
+
+__version__ = "0.0.0-auto.0"
+__repo__ = "https://github.com/adafruit/Adafruit_Blinka_Displayio.git"
+
+
+from typing import Optional, Union
+import _typing
+from ._fourwire import FourWire
+from ._group import Group
+from ._i2cdisplay import I2CDisplay
+from ._structs import ColorspaceStruct, TransformStruct, RectangleStruct
+from ._area import Area
+
+from paralleldisplay import ParallelBus
+from ._constants import (
+ DISPLAY_COMMAND,
+ DISPLAY_DATA,
+ CHIP_SELECT_TOGGLE_EVERY_BYTE,
+ CHIP_SELECT_UNTOUCHED,
+)
+
+displays = []
+
+# Functions
+#* construct
+# show
+# set_dither
+#^ bus_reset
+# set_region_to_update
+#* release
+# fill_area
+# clip_area (_clip)
+
+class _DisplayCore:
+ # pylint: disable=too-many-arguments, too-many-instance-attributes
+
+ def __init__(
+ self,
+ bus,
+ width: int,
+ height: int,
+ ram_width: int,
+ ram_height: int,
+ colstart: int,
+ rowstart: int,
+ rotation: int,
+ color_depth: int,
+ grayscale: bool,
+ pixels_in_byte_share_row: bool,
+ bytes_per_cell: int,
+ reverse_pixels_in_byte: bool,
+ reverse_bytes_in_word: bool
+ ):
+ self._colorspace = ColorspaceStruct(
+ depth = color_depth,
+ grayscale = grayscale,
+ grayscale_bit = 8 - color_depth,
+ pixels_in_byte_share_row = pixels_in_byte_share_row,
+ bytes_per_cell = bytes_per_cell,
+ reverse_pixels_in_byte = reverse_pixels_in_byte,
+ reverse_bytes_in_word = reverse_bytes_in_word,
+ dither = False
+ )
+ self._current_group = None
+ self._colstart = colstart
+ self._rowstart = rowstart
+ self._last_refresh = 0
+ self._refresh_in_progress = False
+
+ if bus:
+ if isinstance(bus, (FourWire, I2CDisplay, ParallelBus)):
+ self._bus_reset = bus.reset
+ self._begin_transaction = bus._begin_transaction
+ self._send = bus._send
+ self._end_transaction = bus._end_transaction
+ else:
+ raise ValueError("Unsupported display bus type")
+
+ self._bus = bus
+ self._area = Area(0, 0, width, height)
+
+ self._width = width
+ self._height = height
+ self._ram_width = ram_width
+ self._ram_height = ram_height
+ self._rotation = rotation
+ self._transform = TransformStruct()
+
+ def set_rotation(self, rotation: int) -> None:
+ """
+ Sets the rotation of the display as an int in degrees.
+ """
+ # pylint: disable=protected-access
+ transposed = self._rotation in (90, 270)
+ will_be_transposed = rotation in (90, 270)
+ if transposed != will_be_transposed:
+ self._width, self._height = self._height, self._width
+
+ height = self._height
+ width = self._width
+
+ rotation %= 360
+ self._rotation = rotation
+ self._transform.x = 0
+ self._transform.y = 0
+ self._transform.scale = 1
+ self._transform.mirror_x = False
+ self._transform.mirror_y = False
+ self._transform.transpose_xy = False
+
+ if rotation in (0, 180):
+ if rotation == 180:
+ self._transform.mirror_x = True
+ self._transform.mirror_y = True
+ else:
+ self._transform.transpose_xy = True
+ if rotation == 270:
+ self._transform.mirror_y = True
+ else:
+ self._transform.mirror_x = True
+
+ self._area.x1 = 0
+ self._area.y1 = 0
+ self._area.next = None
+
+ self._transform.dx = 1
+ self._transform.dy = 1
+ if self._transform.transpose_xy:
+ self._area.x2 = height
+ self._area.y2 = width
+ if self._transform.mirror_x:
+ self._transform.x = height
+ self._transform.dx = -1
+ if self._transform.mirror_y:
+ self._transform.y = width
+ self._transform.dy = -1
+ else:
+ self._area.x2 = width
+ self._area.y2 = height
+ if self._transform.mirror_x:
+ self._transform.x = width
+ self._transform.dx = -1
+ if self._transform.mirror_y:
+ self._transform.y = height
+ self._transform.dy = -1
+
+ if self._current_group is not None:
+ self._current_group._update_transform(self._transform)
+
+ def show(self, root_group: Group) -> bool:
+ # pylint: disable=protected-access
+
+ """
+ Switches to displaying the given group of layers. When group is `None`, the
+ default CircuitPython terminal will be shown.
+
+ :param Optional[displayio.Group] root_group: The group to show.
+ """
+
+ """
+ # TODO: Implement Supervisor
+ if root_group is None:
+ circuitpython_splash = _Supervisor().circuitpython_splash
+ if not circuitpython_splash._in_group:
+ root_group = circuitpython_splash
+ elif self._current_group == circuitpython_splash:
+ return True
+ """
+
+ if root_group == self._current_group:
+ return True
+
+ if root_group is not None and root_group._in_group:
+ return False
+
+ if self._current_group is not None:
+ self._current_group._in_group = False
+
+ if root_group is not None:
+ root_group._update_transform(self._transform)
+ root_group._in_group = True
+
+ self._current_group = root_group
+ self._full_refresh = True
+
+ return True
+
+ def set_region_to_update(
+ self,
+ column_command: int,
+ row_command: int,
+ set_current_column_command: Optional[int],
+ set_current_row_command: Optional[int],
+ data_as_commands: bool,
+ always_toggle_chip_select: bool,
+ area: Area,
+ SH1107_addressing: bool,
+ ) -> None:
+ # pylint: disable=invalid-name, too-many-arguments, too-many-locals, too-many-branches,
+ # pylint: disable=too-many-statements
+
+ big_endian = True # default is True # TODO ????
+
+ x1 = area.x1 + self._colstart
+ x2 = area.x2 + self._colstart
+ y1 = area.y1 + self._rowstart
+ y2 = area.y2 + self._rowstart
+
+ # Collapse down the dimension where multiple pixels are in a byte.
+ if self._colorspace.depth < 8:
+ pixels_per_byte = 8 // self._colorspace.depth
+ if self._colorspace.pixels_in_byte_share_row:
+ x1 //= pixels_per_byte * self._colorspace.bytes_per_cell
+ x2 //= pixels_per_byte * self._colorspace.bytes_per_cell
+ else:
+ y1 //= pixels_per_byte * self._colorspace.bytes_per_cell
+ y2 //= pixels_per_byte * self._colorspace.bytes_per_cell
+
+ x2 -= 1
+ y2 -= 1
+
+ chip_select = CHIP_SELECT_UNTOUCHED
+ if always_toggle_chip_select or data_as_commands:
+ chip_select = CHIP_SELECT_TOGGLE_EVERY_BYTE
+
+ # Set column
+ self._begin_transaction()
+ data = bytearray(5)
+ data[0] = column_command
+ if not data_as_commands:
+ self._send(DISPLAY_COMMAND, CHIP_SELECT_UNTOUCHED, data, 1)
+ data_type = DISPLAY_DATA
+ data_length = 0
+ else:
+ data_type = DISPLAY_COMMAND
+ data_length = 1
+
+ if self._ram_width < 0x100:
+ data[data_length] = x1
+ data_length += 1
+ data[data_length] = x2
+ data_length += 1
+ else:
+ if big_endian:
+ data[data_length] = x1 >> 8
+ data_length += 1
+ data[data_length] = x1 & 0xFF
+ data_length += 1
+ data[data_length] = x2 >> 8
+ data_length += 1
+ data[data_length] = x2 & 0xFF
+ data_length += 1
+ else:
+ data[data_length] = x1 & 0xFF
+ data_length += 1
+ data[data_length] = x1 >> 8
+ data_length += 1
+ data[data_length] = x2 & 0xFF
+ data_length += 1
+ data[data_length] = x2 >> 8
+ data_length += 1
+
+ # Quirk for "SH1107_addressing"
+ # Column lower command = 0x00, Column upper command = 0x10
+ if SH1107_addressing:
+ data[0] = ((x1 >> 4) & 0x0F) | 0x10 # 0x10 to 0x17
+ data[1] = x1 & 0x0F # 0x00 to 0x0F
+ data_length = 2
+
+ self._send(data_type, chip_select, data, data_length)
+ self._end_transaction()
+
+ if set_current_column_command is not None:
+ command = bytearray(1)
+ command[0] = set_current_column_command
+ self._begin_transaction()
+ self._send(DISPLAY_COMMAND, chip_select, command, 1)
+ self._send(DISPLAY_DATA, chip_select, data, data_length // 2)
+ self._end_transaction()
+
+ # Set row
+ self._begin_transaction()
+ data[0] = row_command
+ data_length = 1
+ if not data_as_commands:
+ self._send(DISPLAY_COMMAND, CHIP_SELECT_UNTOUCHED, data, 1)
+ data_length = 0
+
+ if self._ram_height < 0x100:
+ data[data_length] = y1
+ data_length += 1
+ data[data_length] = y2
+ data_length += 1
+ else:
+ if big_endian:
+ data[data_length] = y1 >> 8
+ data_length += 1
+ data[data_length] = y1 & 0xFF
+ data_length += 1
+ data[data_length] = y2 >> 8
+ data_length += 1
+ data[data_length] = y2 & 0xFF
+ data_length += 1
+ # TODO Which is right? The core uses above
+ else:
+ data[data_length] = y1 & 0xFF
+ data_length += 1
+ data[data_length] = y1 >> 8
+ data_length += 1
+ data[data_length] = y2 & 0xFF
+ data_length += 1
+ data[data_length] = y2 >> 8
+ data_length += 1
+
+ # Quirk for "SH1107_addressing"
+ # Page address command = 0xB0
+ if SH1107_addressing:
+ # Set the page to out y value
+ data[0] = 0xB0 | y1
+ data_length = 1
+
+ self._send(data_type, chip_select, data, data_length)
+ self._end_transaction()
+
+ if set_current_row_command is not None:
+ command = bytearray(1)
+ command[0] = set_current_row_command
+ self._begin_transaction()
+ self._send(DISPLAY_COMMAND, chip_select, command, 1)
+ self._send(DISPLAY_DATA, chip_select, data, data_length // 2)
+ self._end_transaction()
+
+ def start_refresh(self) -> bool:
+ # pylint: disable=protected-access
+
+ if self._refresh_in_progress:
+ return False
+
+ self._refresh_in_progress = True
+ #self._last_refresh = _Supervisor()._ticks_ms64()
+ return True
+
+ def finish_refresh(self) -> None:
+ # pylint: disable=protected-access
+
+ if self._current_group is not None:
+ self._current_group._finish_refresh()
+
+ self._full_refresh = False
+ self._refresh_in_progress = False
+ #self._last_refresh = _Supervisor()._ticks_ms64()
+
+ def get_refresh_areas(self):
+ subrectangles = []
+ if self._current_group is not None:
+ # Eventually calculate dirty rectangles here
+ subrectangles.append(RectangleStruct(0, 0, self._width, self._height))
+ return subrectangles
+
+ def release(self) -> None:
+ # pylint: disable=protected-access
+
+ if self._current_group is not None:
+ self._current_group._in_group = False
+
+ def fill_area(
+ self, area: Area, mask: _typing.WriteableBuffer, buffer: _typing.WriteableBuffer
+ ) -> bool:
+ # pylint: disable=protected-access
+
+ return self._current_group._fill_area(self._colorspace, area, mask, buffer)
+
+ def clip_area(self, area: Area, clipped: Area) -> bool:
+ # pylint: disable=protected-access
+
+ overlaps = self._area._compute_overlap(area, clipped)
+ if not overlaps:
+ return False
+
+ # Expand the area if we have multiple pixels per byte and we need to byte align the bounds
+ if self._colorspace.depth < 8:
+ pixels_per_byte = (
+ 8 // self._colorspace.depth * self._colorspace.bytes_per_cell
+ )
+ if self._colorspace.pixels_in_byte_share_row:
+ if clipped.x1 % pixels_per_byte != 0:
+ clipped.x1 -= clipped.x1 % pixels_per_byte
+ if clipped.x2 % pixels_per_byte != 0:
+ clipped.x2 += pixels_per_byte - clipped.x2 % pixels_per_byte
+ else:
+ if clipped.y1 % pixels_per_byte != 0:
+ clipped.y1 -= clipped.y1 % pixels_per_byte
+ if clipped.y2 % pixels_per_byte != 0:
+ clipped.y2 += pixels_per_byte - clipped.y2 % pixels_per_byte
+
+ return True
+
+ def send(self, data_type: int, chip_select: int, data: _typing.ReadableBuffer) -> None:
+ """
+ Send the data to the current bus
+ """
+ self._send(data_type, chip_select, data)
+
+ def begin_transaction(self) -> None:
+ """
+ Begin Bus Transaction
+ """
+ self._begin_transaction()
+
+ def end_transaction(self) -> None:
+ """
+ End Bus Transaction
+ """
+ self._end_transaction()
+
+ def get_width(self) -> int:
+ """
+ Gets the width of the display in pixels.
+ """
+ return self._width
+
+ def get_height(self) -> int:
+ """
+ Gets the height of the display in pixels.
+ """
+ return self._height
+
+ def get_rotation(self) -> int:
+ """
+ Gets the rotation of the display as an int in degrees.
+ """
+ return self._rotation
+
+ def get_bus(self) -> Union[FourWire,ParallelBus,I2CDisplay]:
+ """
+ The bus being used by the display. [readonly]
+ """
+ return self._bus
\ No newline at end of file