Skip to content

Commit

Permalink
Merge pull request #115 from makermelissa/add-einks
Browse files Browse the repository at this point in the history
Add E-Ink Display Support
  • Loading branch information
makermelissa authored Oct 2, 2023
2 parents 7e3f147 + 6321a12 commit 61075a9
Show file tree
Hide file tree
Showing 6 changed files with 565 additions and 88 deletions.
24 changes: 13 additions & 11 deletions displayio/_colorconverter.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def _compute_luma(color_rgb888: int):
red8 = color_rgb888 >> 16
grn8 = (color_rgb888 >> 8) & 0xFF
blu8 = color_rgb888 & 0xFF
return (red8 * 19 + grn8 * 182 + blu8 + 54) // 255
return (red8 * 19 + grn8 * 182 + blu8 * 54) // 255

@staticmethod
def _compute_chroma(color_rgb888: int):
Expand Down Expand Up @@ -144,9 +144,7 @@ def _compute_sevencolor(color_rgb888: int):
return 0x0 # Black

@staticmethod
def _compute_tricolor(
colorspace: ColorspaceStruct, pixel_hue: int, color: int
) -> int:
def _compute_tricolor(colorspace: ColorspaceStruct, pixel_hue: int) -> int:
hue_diff = colorspace.tricolor_hue - pixel_hue
if -10 <= hue_diff <= 10 or hue_diff <= -220 or hue_diff >= 220:
if colorspace.grayscale:
Expand All @@ -169,7 +167,7 @@ def convert(self, color: int) -> int:
else:
raise ValueError("Color must be an integer or 3 or 4 value tuple")

input_pixel = InputPixelStruct(color)
input_pixel = InputPixelStruct(pixel=color)
output_pixel = OutputPixelStruct()

self._convert(self._output_colorspace, input_pixel, output_pixel)
Expand Down Expand Up @@ -251,9 +249,15 @@ def _convert_color(
# pylint: disable=too-many-return-statements, too-many-branches, too-many-statements
pixel = input_pixel.pixel
if dither:
rand_red = ColorConverter._dither_noise_2(input_pixel.x, input_pixel.y)
rand_grn = ColorConverter._dither_noise_2(input_pixel.x + 33, input_pixel.y)
rand_blu = ColorConverter._dither_noise_2(input_pixel.x, input_pixel.y + 33)
rand_red = ColorConverter._dither_noise_2(
input_pixel.tile_x, input_pixel.tile_y
)
rand_grn = ColorConverter._dither_noise_2(
input_pixel.tile_x + 33, input_pixel.tile_y
)
rand_blu = ColorConverter._dither_noise_2(
input_pixel.tile_x, input_pixel.tile_y + 33
)

red8 = pixel >> 16
grn8 = (pixel >> 8) & 0xFF
Expand Down Expand Up @@ -287,9 +291,7 @@ def _convert_color(
output_color.opaque = True
return
pixel_hue = ColorConverter._compute_hue(pixel)
output_color.pixel = ColorConverter._compute_tricolor(
colorspace, pixel_hue, output_color.pixel
)
output_color.pixel = ColorConverter._compute_tricolor(colorspace, pixel_hue)
return
if colorspace.grayscale and colorspace.depth <= 8:
bitmask = (1 << colorspace.depth) - 1
Expand Down
47 changes: 37 additions & 10 deletions displayio/_display.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
"""

import time
from array import array
from typing import Optional
import digitalio
import microcontroller
Expand Down Expand Up @@ -244,8 +243,14 @@ def _send_pixels(self, pixels):
self._core.send(DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, pixels)

def show(self, group: Group) -> None:
"""Switches to displaying the given group of layers. When group is None, the
"""
.. note:: `show()` is deprecated and will be removed when CircuitPython 9.0.0
is released. Use ``.root_group = group`` instead.
Switches to displaying the given group of layers. When group is None, the
default CircuitPython terminal will be shown.
:param Group group: The group to show.
"""
if group is None:
group = circuitpython_splash
Expand Down Expand Up @@ -378,15 +383,15 @@ def _refresh_area(self, area) -> bool:
buffer_size = pixels_per_buffer // pixels_per_word
if pixels_per_buffer % pixels_per_word:
buffer_size += 1
mask_length = (pixels_per_buffer // 8) + 1 # 1 bit per pixel + 1
mask_length = (pixels_per_buffer // 32) + 1 # 1 bit per pixel + 1
remaining_rows = clipped.height()

for subrect_index in range(subrectangles):
subrectangle = Area(
clipped.x1,
clipped.y1 + rows_per_buffer * subrect_index,
clipped.x2,
clipped.y1 + rows_per_buffer * (subrect_index + 1),
x1=clipped.x1,
y1=clipped.y1 + rows_per_buffer * subrect_index,
x2=clipped.x2,
y2=clipped.y1 + rows_per_buffer * (subrect_index + 1),
)
if remaining_rows < rows_per_buffer:
subrectangle.y2 = subrectangle.y1 + remaining_rows
Expand All @@ -410,7 +415,7 @@ def _refresh_area(self, area) -> bool:
return False

self._core.begin_transaction()
self._send_pixels(buffer[:subrectangle_size_bytes])
self._send_pixels(buffer.tobytes()[:subrectangle_size_bytes])
self._core.end_transaction()
return True

Expand All @@ -426,9 +431,9 @@ def fill_row(self, y: int, buffer: WriteableBuffer) -> WriteableBuffer:
if pixels_per_buffer % pixels_per_word:
buffer_size += 1

buffer = bytearray([0] * (buffer_size * 4))
buffer = memoryview(bytearray([0] * (buffer_size * 4))).cast("I")
mask_length = (pixels_per_buffer // 32) + 1
mask = array("L", [0x00000000] * mask_length)
mask = memoryview(bytearray([0] * (mask_length * 4))).cast("I")
self._core.fill_area(area, mask, buffer)
return buffer

Expand Down Expand Up @@ -512,9 +517,31 @@ def rotation(self) -> int:
def rotation(self, value: int):
if value % 90 != 0:
raise ValueError("Display rotation must be in 90 degree increments")
transposed = self._core.rotation in (90, 270)
will_transposed = value in (90, 270)
if transposed != will_transposed:
self._core.width, self._core.height = self._core.height, self._core.width
self._core.set_rotation(value)
if self._core.current_group is not None:
self._core.current_group._update_transform( # pylint: disable=protected-access
self._core.transform
)

@property
def bus(self) -> _DisplayBus:
"""Current Display Bus"""
return self._core.get_bus()

@property
def root_group(self) -> Group:
"""
The root group on the display.
If the root group is set to `displayio.CIRCUITPYTHON_TERMINAL`, the default
CircuitPython terminal will be shown.
If the root group is set to ``None``, no output will be shown.
"""
return self._core.current_group

@root_group.setter
def root_group(self, new_group: Group) -> None:
self._set_root_group(new_group)
11 changes: 2 additions & 9 deletions displayio/_displaycore.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,13 @@ def __init__(
self.rotation = rotation
self.transform = TransformStruct()

self.set_rotation(rotation)

def set_rotation(self, rotation: int) -> None:
"""
Sets the rotation of the display as an int in degrees.
"""
# pylint: disable=protected-access, too-many-branches
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

Expand Down Expand Up @@ -176,9 +173,6 @@ def set_rotation(self, rotation: int) -> None:
self.transform.y = height
self.transform.dy = -1

if self.current_group is not None:
self.current_group._update_transform(self.transform)

def set_root_group(self, root_group: Group) -> bool:
"""
Switches to displaying the given group of layers. When group is `None`, the
Expand Down Expand Up @@ -289,7 +283,6 @@ def set_region_to_update(self, area: Area) -> None:
else:
region_y1 //= pixels_per_byte * self.colorspace.bytes_per_cell
region_y2 //= pixels_per_byte * self.colorspace.bytes_per_cell

region_x2 -= 1
region_y2 -= 1

Expand Down
Loading

0 comments on commit 61075a9

Please sign in to comment.