Skip to content

Commit

Permalink
Merge pull request #114 from makermelissa/add-grayscale
Browse files Browse the repository at this point in the history
Revamp Displayio and Add grayscale
  • Loading branch information
makermelissa authored Sep 29, 2023
2 parents 157aade + d99b13f commit cfee25c
Show file tree
Hide file tree
Showing 28 changed files with 2,045 additions and 689 deletions.
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -396,4 +396,4 @@ min-public-methods=1

# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=Exception
overgeneral-exceptions=builtins.Exception
57 changes: 53 additions & 4 deletions displayio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* Author(s): Melissa LeBlanc-Williams
"""

import threading
from typing import Union
from ._fourwire import FourWire
from ._i2cdisplay import I2CDisplay
Expand All @@ -30,19 +30,68 @@
from ._palette import Palette
from ._shape import Shape
from ._tilegrid import TileGrid
from ._display import displays
from ._displaybus import _DisplayBus
from ._constants import CIRCUITPY_DISPLAY_LIMIT

__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"


displays = []
display_buses = []


def _background():
"""Main thread function to loop through all displays and update them"""
while True:
for display in displays:
display._background() # pylint: disable=protected-access


def release_displays() -> None:
"""Releases any actively used displays so their busses and pins can be used again.
Use this once in your code.py if you initialize a display. Place it right before the
initialization so the display is active as long as possible.
"""
for _disp in displays:
_disp._release() # pylint: disable=protected-access
for display in displays:
display._release() # pylint: disable=protected-access
displays.clear()

for display_bus in display_buses:
display_bus.deinit()
display_buses.clear()


def allocate_display(new_display: Union[Display, EPaperDisplay]) -> None:
"""Add a display to the displays pool and return the new display"""
if len(displays) >= CIRCUITPY_DISPLAY_LIMIT:
raise RuntimeError("Too many displays")
displays.append(new_display)


def allocate_display_bus(new_display_bus: _DisplayBus) -> None:
"""Add a display bus to the display_buses pool and return the new display bus"""
if len(display_buses) >= CIRCUITPY_DISPLAY_LIMIT:
raise RuntimeError(
"Too many display busses; forgot displayio.release_displays() ?"
)
display_buses.append(new_display_bus)


background_thread = threading.Thread(target=_background, daemon=True)


# Start the background thread
def _start_background():
if not background_thread.is_alive():
background_thread.start()


def _stop_background():
if background_thread.is_alive():
# Stop the thread
background_thread.join()


_start_background()
40 changes: 25 additions & 15 deletions displayio/_area.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@


class Area:
# pylint: disable=invalid-name,missing-function-docstring
"""Area Class to represent an area to be updated. Currently not used."""
"""Area Class to represent an area to be updated."""

# pylint: disable=invalid-name
def __init__(self, x1: int = 0, y1: int = 0, x2: int = 0, y2: int = 0):
self.x1 = x1
self.y1 = y1
Expand All @@ -38,25 +38,29 @@ def __init__(self, x1: int = 0, y1: int = 0, x2: int = 0, y2: int = 0):
def __str__(self):
return f"Area TL({self.x1},{self.y1}) BR({self.x2},{self.y2})"

def _copy_into(self, dst) -> None:
def copy_into(self, dst) -> None:
"""Copy the area into another area."""
dst.x1 = self.x1
dst.y1 = self.y1
dst.x2 = self.x2
dst.y2 = self.y2

def _scale(self, scale: int) -> None:
def scale(self, scale: int) -> None:
"""Scale the area by scale."""
self.x1 *= scale
self.y1 *= scale
self.x2 *= scale
self.y2 *= scale

def _shift(self, dx: int, dy: int) -> None:
def shift(self, dx: int, dy: int) -> None:
"""Shift the area by dx and dy."""
self.x1 += dx
self.y1 += dy
self.x2 += dx
self.y2 += dy

def _compute_overlap(self, other, overlap) -> bool:
def compute_overlap(self, other, overlap) -> bool:
"""Compute the overlap between two areas. Returns True if there is an overlap."""
a = self
overlap.x1 = max(a.x1, other.x1)
overlap.x2 = min(a.x2, other.x2)
Expand All @@ -69,22 +73,24 @@ def _compute_overlap(self, other, overlap) -> bool:

return overlap.y1 < overlap.y2

def _empty(self):
def empty(self):
"""Return True if the area is empty."""
return (self.x1 == self.x2) or (self.y1 == self.y2)

def _canon(self):
def canon(self):
"""Make sure the area is in canonical form."""
if self.x1 > self.x2:
self.x1, self.x2 = self.x2, self.x1
if self.y1 > self.y2:
self.y1, self.y2 = self.y2, self.y1

def _union(self, other, union):
# pylint: disable=protected-access
if self._empty():
self._copy_into(union)
def union(self, other, union):
"""Combine this area along with another into union"""
if self.empty():
self.copy_into(union)
return
if other._empty():
other._copy_into(union)
if other.empty():
other.copy_into(union)
return

union.x1 = min(self.x1, other.x1)
Expand All @@ -93,12 +99,15 @@ def _union(self, other, union):
union.y2 = max(self.y2, other.y2)

def width(self) -> int:
"""Return the width of the area."""
return self.x2 - self.x1

def height(self) -> int:
"""Return the height of the area."""
return self.y2 - self.y1

def size(self) -> int:
"""Return the size of the area."""
return self.width() * self.height()

def __eq__(self, other):
Expand All @@ -113,14 +122,15 @@ def __eq__(self, other):
)

@staticmethod
def _transform_within(
def transform_within(
mirror_x: bool,
mirror_y: bool,
transpose_xy: bool,
original: Area,
whole: Area,
transformed: Area,
):
"""Transform an area within a larger area."""
# pylint: disable=too-many-arguments
# Original and whole must be in the same coordinate space.
if mirror_x:
Expand Down
Loading

0 comments on commit cfee25c

Please sign in to comment.