Source code for luma.core.image_composition

# -*- coding: utf-8 -*-
# Copyright (c) 2017-2022 Richard Hull and contributors
# See LICENSE.rst for details.

"""
Composes scrollable, positionable images into another
Image

.. versionadded:: 1.1.0
"""

from PIL import Image, ImageDraw


[docs] class ComposableImage(object): """ This class encapsulates an image and its attributes that can be rendered onto an :py:class:`ImageComposition`. """ def __init__(self, image, position=(0, 0), offset=(0, 0)): """ Instantiates a new ``ComposableImage``. :param image: The composable image. :type image: PIL.Image :param position: The initial position of the image within the composition. :type position: tuple :param offset: The initial offset within the image, from which it should be drawn. :type offset: tuple """ assert image self._image = image self._position = position self._offset = offset @property def position(self): """ Getter for position :returns: A tuple containing the ``x,y`` position. :rtype: tuple """ return self._position @position.setter def position(self, value): """ Indicates where the image is to be rendered in an image composition. :param value: The ``x,y`` position tuple. :type value: tuple """ self._position = value @property def offset(self): """ Getter for offset. :returns: A tuple containing the top,left position. :rtype: tuple """ return self._offset @offset.setter def offset(self, value): """ Indicates the top left position within the image, as of which it is is to be rendered in the image composition. :param value: The top,left position. :type value: tuple """ self._offset = value @property def width(self): """ :returns: The actual width of the image, regardless its position or offset within the image composition. :rtype: int """ return self._image.width @property def height(self): """ :returns: The actual height of the image, regardless its position or offset within the image composition. :rtype: int """ return self._image.height
[docs] def image(self, size): """ :param size: The width, height of the image composition. :type size: tuple :returns: An image, cropped to the boundaries specified by ``size``. :rtype: PIL.Image.Image """ assert size[0] assert size[1] return self._image.crop(box=self._crop_box(size))
def _crop_box(self, size): """ Helper that calculates the crop box for the offset within the image. :param size: The width and height of the image composition. :type size: tuple :returns: The bounding box of the image, given ``size``. :rtype: tuple """ (left, top) = self.offset right = left + min(size[0], self.width) bottom = top + min(size[1], self.height) return (left, top, right, bottom)
[docs] class ImageComposition(object): """ Manages a composition of :py:class:`ComposableImage` instances that can be rendered onto a single :py:class:`PIL.Image.Image`. """ def __init__(self, device): """ Instantiates a new ``ImageComposition`` :param device: The device on which to render. :type device: luma.core.device.device """ self._device = device self._background_image = Image.new(device.mode, device.size) self.composed_images = []
[docs] def add_image(self, image): """ Adds an image to the composition. :param image: The image to add. :type image: PIL.Image.Image """ assert image self.composed_images.append(image)
[docs] def remove_image(self, image): """ Removes an image from the composition. :param image: The image to be removed. :type image: PIL.Image.Image """ assert image self.composed_images.remove(image)
def __call__(self): """ Returns the current composition. :rtype: PIL.Image.Image """ return self._background_image
[docs] def refresh(self): """ Clears the composition and renders all the images taking into account their position and offset. """ self._clear() for img in self.composed_images: self._background_image.paste(img.image(self._device.size), img.position) self._background_image.crop(box=self._device.bounding_box)
def _clear(self): """ Helper that clears the composition. """ draw = ImageDraw.Draw(self._background_image) draw.rectangle(self._device.bounding_box, fill="black") del draw