X-Git-Url: https://git.ayoreis.com/hackapet/Adafruit_Blinka_Displayio.git/blobdiff_plain/16ddfbdfd05c3052a0733883e6a507ea9fcc0e73..03b2e1008f33585652fad5ecedad65d44694b2bc:/displayio/_displaycore.py diff --git a/displayio/_displaycore.py b/displayio/_displaycore.py new file mode 100644 index 0000000..acedf6e --- /dev/null +++ b/displayio/_displaycore.py @@ -0,0 +1,457 @@ +# 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