Source code for rubato.classes.components.image

"""
The image component that renders an image from the filesystem.
"""
from __future__ import annotations
from typing import TYPE_CHECKING, Dict
import sdl2, sdl2.ext, sdl2.sdlgfx, sdl2.surface

from . import Component
from ... import Vector, Defaults, Display, Radio, get_path

if TYPE_CHECKING:
    from .. import Camera


[docs]class Image(Component): """ A component that handles Images. Args: options: A Image config. Defaults to the :ref:`Image defaults <imagedef>`. Attributes: visible (bool): Whether or not the image is visible. """ def __init__(self, options: dict = {}): param = Defaults.image_defaults | options super().__init__(param) if param["rel_path"] == "": self._image: sdl2.SDL_Surface = sdl2.SDL_CreateRGBSurfaceWithFormat( 0, param["size"].x, param["size"].y, 32, sdl2.SDL_PIXELFORMAT_RGBA8888, ).contents else: try: self._image: sdl2.SDL_Surface = sdl2.ext.load_img(param["rel_path"], False) except OSError: self._image = sdl2.ext.load_img(get_path(param["rel_path"]), False) except sdl2.ext.SDLError as e: fname = param["rel_path"].replace("\\", "/").split("/")[-1] raise TypeError(f"{fname} is not a valid image file") from e self.singular = False self.visible: bool = param["visible"] self._aa: bool = param["anti_aliasing"] self._flipx: bool = param["flipx"] self._flipy: bool = param["flipy"] self._scale: Vector = param["scale"] self._rot = self.rotation_offset self._original = Display.clone_surface(self._image) self._tx = sdl2.ext.Texture(Display.renderer, self.image) self._changed = False self._update_rotozoom() Radio.listen("ZOOM", self.cam_update) @property def image(self) -> sdl2.SDL_Surface: """The SDL Surface of the image.""" return self._image @image.setter def image(self, new: sdl2.SDL_Surface): self._image = sdl2.SDL_ConvertSurfaceFormat(new, sdl2.SDL_PIXELFORMAT_RGBA8888, 0).contents self._original = Display.clone_surface(self._image) self._update_rotozoom() @property def scale(self) -> Vector: """The scale of the image.""" return self._scale @scale.setter def scale(self, new: Vector): self._scale = new self._changed = True @property def rotation_offset(self) -> float: """The rotation offset of the image.""" return self._rot @rotation_offset.setter def rotation_offset(self, new: float): self._rot = new self._changed = True @property def flipx(self) -> bool: """Whether or not the image is flipped horizontally.""" return self._flipx @flipx.setter def flipx(self, new: bool): self._flipx = new self._changed = True @property def flipy(self) -> bool: """Whether or not the image is flipped vertically.""" return self._flipy @flipy.setter def flipy(self, new: bool): self._flipy = new self._changed = True @property def aa(self) -> bool: """Whether or not the image is anti-aliased.""" return self._aa @aa.setter def aa(self, new: bool): self._aa = new self._changed = True
[docs] def get_size(self) -> Vector: """ Gets the current size of the image. Returns: Vector: The size of the image """ if self.image.w == self._original.w and self.image.h == self._original.h: return Vector(self.image.w, self.image.h) * self.scale return Vector(self.image.w, self.image.h)
[docs] def get_size_original(self) -> Vector: """ Gets the original size of the image. Returns: Vector: The original size of the image. """ return Vector(self._original.w, self._original.h)
def _update_rotozoom(self): """Updates the image surface. Called automatically when image scale or rotation are updated""" if self.gameobj: self._image = sdl2.sdlgfx.rotozoomSurfaceXY( self._original, self.gameobj.rotation + self.rotation_offset, -self.scale.x if self.flipx else self.scale.x, -self.scale.y if self.flipy else self.scale.y, int(self.aa), ).contents self._tx = sdl2.ext.Texture(Display.renderer, self.image)
[docs] def resize(self, new_size: Vector): """ Resize the image to a given size in pixels. Args: new_size: The new size of the image in pixels. """ if -1 < new_size.x < 1: new_size.x = 1 if -1 < new_size.y < 1: new_size.y = 1 image_scaled = sdl2.surface.SDL_CreateRGBSurfaceWithFormat( 0, new_size.x, new_size.y, 32, sdl2.SDL_PIXELFORMAT_RGBA8888, ).contents sdl2.surface.SDL_BlitScaled( self._original, None, image_scaled, sdl2.SDL_Rect(0, 0, new_size.x, new_size.y), ) self._image = image_scaled self._tx = sdl2.ext.Texture(Display.renderer, self.image)
[docs] def cam_update(self, info: Dict[str, Camera]): """Updates the image sizing when the camera zoom changes.""" width, height = self.image.w, self.image.h new_size = Vector( round(info["camera"].scale(width)), round(info["camera"].scale(height)), ) self.resize(new_size)
[docs] def draw(self, camera: Camera): if self._changed: self._changed = False self._update_rotozoom() if self.visible: Display.update(self._tx, camera.transform(self.gameobj.pos - Vector(*self._tx.size) / 2))
[docs] def delete(self): """Deletes the image component""" self._tx.destroy() sdl2.SDL_FreeSurface(self._image) sdl2.SDL_FreeSurface(self._original) self._image = None self._tx = None self._original = None
[docs] def clone(self) -> "Image": """ Clones the current image. Returns: Image: The cloned image. """ new = Image( { "scale": self.scale, "anti_aliasing": self.aa, "flipx": self.flipx, "flipy": self.flipy, "offset": self.offset, "visible": self.visible, } ) new.image = Display.clone_surface(self.image) return new