Skip to content

Splash

splash

Full-screen ("splash") image preparation for the Stream Deck LCD.

This module owns the device-level, non-DUI path for uploading a single image covering the entire back-panel LCD (HID Report 0x02, Command 0x08). The hardware behaviour of that command is one-shot: any subsequent per-key or per-window write paints over the image, so this feature is intended for startup splashes, lock screens, and loading screens — not as a persistent background for a :class:~deux.ui.screen.Screen.

The async :class:~deux.runtime.deck.Deck wrapper lives on the deck itself; this module exposes the pure, testable preparation pipeline: input → resize/fit → rotate → JPEG-encode.

Accepted inputs
  • :class:PIL.Image.Image — used directly.
  • :class:str or :class:pathlib.Path — loaded from disk. Files with an .svg suffix (or whose content sniffs as SVG) are rasterised via :func:~deux.render.svg_rasterize._svg_to_image at the target logical LCD size.
  • :class:bytes — raw image bytes. SVG is sniffed via the leading bytes; otherwise the bytes are decoded with Pillow. Pre-encoded JPEG bytes whose dimensions and orientation already match the device transmit size are passed through untouched.
Fit modes
  • "cover" — scale to fill, crop overflow (default).
  • "contain" — scale to fit, letterbox with the given background.
  • "stretch" — scale to the exact target size, ignoring aspect.
Notes

Rotation is applied after sizing and before JPEG encoding so that the bytes handed to the HID layer are already in the device's transmit orientation.

SplashError

Bases: DeuxError

Raised when a full-screen image cannot be prepared.

Source code in src/deux/runtime/splash.py
class SplashError(DeuxError):
    """Raised when a full-screen image cannot be prepared."""

prepare_full_screen_jpeg

prepare_full_screen_jpeg(image: Image | str | Path | bytes, *, logical_size: tuple[int, int], rotation: ImageRotation = ImageRotation.NONE, fit: FitMode = 'cover', background: tuple[int, int, int] = (0, 0, 0), jpeg_quality: int = 90) -> bytes

Prepare a JPEG ready for HID command 0x08 (full-screen image).

Loads image (from any supported input type), resizes it to logical_size using fit, rotates by rotation to match the device transmit orientation, and JPEG-encodes the result.

Parameters:

Name Type Description Default
image Image.Image, str, Path, or bytes

The source image. See the module docstring for accepted forms (including SVG sniffing).

required
logical_size tuple[int, int]

Target logical LCD (width, height) in pixels (the upright orientation the user sees). Typically obtained from :attr:deux.runtime.hid.device.HidDevice.logical_lcd_size.

required
rotation ImageRotation

Pre-upload rotation to apply. Typically obtained from :attr:deux.runtime.hid.device.HidDevice.rotation.

ImageRotation.NONE
fit ('cover', 'contain', 'stretch')

Resize strategy. See module docstring.

"cover"
background tuple[int, int, int]

RGB letterbox colour for fit="contain".

(0, 0, 0)
jpeg_quality int

JPEG quality (1-95).

90

Returns:

Type Description
bytes

JPEG-encoded image bytes in the device transmit orientation, ready to hand to :meth:deux.runtime.hid.device.HidDevice.set_full_screen_image.

Raises:

Type Description
SplashError

If the input cannot be loaded, fit is invalid, or logical_size is non-positive.

Examples:

>>> from deux.runtime.splash import prepare_full_screen_jpeg
>>> from deux.runtime.hid.protocol import ImageRotation
>>> jpeg = prepare_full_screen_jpeg(
...     "splash.png",
...     logical_size=(800, 480),
...     rotation=ImageRotation.NONE,
...     fit="contain",
...     background=(0, 0, 0),
... )
Source code in src/deux/runtime/splash.py
def prepare_full_screen_jpeg(
    image: Image.Image | str | Path | bytes,
    *,
    logical_size: tuple[int, int],
    rotation: ImageRotation = ImageRotation.NONE,
    fit: FitMode = "cover",
    background: tuple[int, int, int] = (0, 0, 0),
    jpeg_quality: int = 90,
) -> bytes:
    """Prepare a JPEG ready for HID command ``0x08`` (full-screen image).

    Loads *image* (from any supported input type), resizes it to
    *logical_size* using *fit*, rotates by *rotation* to match the
    device transmit orientation, and JPEG-encodes the result.

    Parameters
    ----------
    image : Image.Image, str, Path, or bytes
        The source image.  See the module docstring for accepted
        forms (including SVG sniffing).
    logical_size : tuple[int, int]
        Target logical LCD ``(width, height)`` in pixels (the
        upright orientation the user sees).  Typically obtained
        from :attr:`deux.runtime.hid.device.HidDevice.logical_lcd_size`.
    rotation : ImageRotation, default=ImageRotation.NONE
        Pre-upload rotation to apply.  Typically obtained from
        :attr:`deux.runtime.hid.device.HidDevice.rotation`.
    fit : {"cover", "contain", "stretch"}, default="cover"
        Resize strategy.  See module docstring.
    background : tuple[int, int, int], default=(0, 0, 0)
        RGB letterbox colour for ``fit="contain"``.
    jpeg_quality : int, default=90
        JPEG quality (1-95).

    Returns
    -------
    bytes
        JPEG-encoded image bytes in the device transmit orientation,
        ready to hand to
        :meth:`deux.runtime.hid.device.HidDevice.set_full_screen_image`.

    Raises
    ------
    SplashError
        If the input cannot be loaded, *fit* is invalid, or
        *logical_size* is non-positive.

    Examples
    --------
    >>> from deux.runtime.splash import prepare_full_screen_jpeg
    >>> from deux.runtime.hid.protocol import ImageRotation
    >>> jpeg = prepare_full_screen_jpeg(
    ...     "splash.png",
    ...     logical_size=(800, 480),
    ...     rotation=ImageRotation.NONE,
    ...     fit="contain",
    ...     background=(0, 0, 0),
    ... )
    """
    pil_img, _from_svg = _load_image(image, logical_size)
    fitted = _apply_fit(pil_img, logical_size, fit, background)
    rotated = _apply_rotation(fitted, rotation)

    buf = io.BytesIO()
    rotated.save(buf, format="JPEG", quality=jpeg_quality)
    return buf.getvalue()

prepare_solid_color_jpeg

prepare_solid_color_jpeg(color: tuple[int, int, int], *, logical_size: tuple[int, int], rotation: ImageRotation = ImageRotation.NONE, jpeg_quality: int = 90) -> bytes

Prepare a solid-colour full-screen JPEG.

Convenience wrapper around :func:prepare_full_screen_jpeg for clearing the LCD via the full-screen image path (useful when :meth:HidDevice.fill_lcd_color is unavailable on a given family, or when a deterministic clear via the same code path is preferred).

Parameters:

Name Type Description Default
color tuple[int, int, int]

RGB fill colour.

required
logical_size tuple[int, int]

Target logical LCD (width, height).

required
rotation ImageRotation

Pre-upload rotation.

ImageRotation.NONE
jpeg_quality int

JPEG quality.

90

Returns:

Type Description
bytes

JPEG-encoded solid-colour image in transmit orientation.

Source code in src/deux/runtime/splash.py
def prepare_solid_color_jpeg(
    color: tuple[int, int, int],
    *,
    logical_size: tuple[int, int],
    rotation: ImageRotation = ImageRotation.NONE,
    jpeg_quality: int = 90,
) -> bytes:
    """Prepare a solid-colour full-screen JPEG.

    Convenience wrapper around :func:`prepare_full_screen_jpeg` for
    clearing the LCD via the full-screen image path (useful when
    :meth:`HidDevice.fill_lcd_color` is unavailable on a given
    family, or when a deterministic clear via the same code path is
    preferred).

    Parameters
    ----------
    color : tuple[int, int, int]
        RGB fill colour.
    logical_size : tuple[int, int]
        Target logical LCD ``(width, height)``.
    rotation : ImageRotation, default=ImageRotation.NONE
        Pre-upload rotation.
    jpeg_quality : int, default=90
        JPEG quality.

    Returns
    -------
    bytes
        JPEG-encoded solid-colour image in transmit orientation.
    """
    canvas = Image.new("RGB", logical_size, color)
    return prepare_full_screen_jpeg(
        canvas,
        logical_size=logical_size,
        rotation=rotation,
        fit="stretch",
        jpeg_quality=jpeg_quality,
    )