Source code for pygame_zer.image

import math
from typing import TypeAlias

import pygame

from .camera import Camera
from .driver import Driver
from .shape import Shape
from .types import Vec2f, Vec2i

ScaledSource: TypeAlias = tuple[
    Vec2i,  # position on screen
    pygame.Surface,  # surface
]


[docs] class Image(Shape): """ A pygamezer image. You should use this class if you want to create an image in the driver world. The constructor will add the shape to the driver for you. You can set a desired output size in world space in the constructor. If you do, the image will be scaled to fit the requested size, which may change the aspect ratio. Attributes ---------- dest : pygame_zer.types.Vec2f The topleft of the image in world space driver : pygame_zer.Driver The driver this image is attached to image_size : pygame_zer.types.Vec2f The size of the internal source image. This is not necessarily the same as the original source size, since a custom output size can change the aspect ratio source : pygame.Surface The image/surface to draw. This may be scaled internally to a different size if a custom size argument is specified size : pygame_zer.types.Vec2f, optional The size of the image in world space. Default the original source size """ def __init__( self, driver: Driver, source: pygame.Surface, dest: Vec2f, size: Vec2f | None = None, ): # a/b * x = c/d # x = (c/d)/(a/b) # If size is not none, rescale to appropriate size if size is not None: source_scale_val = (size[0] / size[1]) / ( source.get_width() / source.get_height() ) self.source = pygame.transform.scale_by(source, (source_scale_val, 1)) else: self.source = source self.dest = dest self.image_size: Vec2f = self.source.get_size() self.size = size if size is not None else self.image_size driver._insert_shape(self)
[docs] def scaled_source(self, camera: Camera) -> ScaledSource | None: zoom = camera.camerazoom topleft = camera.point_to_camera(self.dest) bottomright = camera.point_to_camera( (self.dest[0] + self.size[0], self.dest[1] + self.size[1]) ) if topleft[0] >= camera.rendersize[0] or topleft[1] >= camera.rendersize[1]: # Too far right or down return None if bottomright[0] <= 0 or bottomright[1] <= 0: # Too far left or up return None content_topleft = [max(tl, 0) for tl in topleft] content_bottomright = [ min(br, rs) for br, rs in zip(bottomright, camera.rendersize) ] content_offset_tl = [ctl - tl for ctl, tl in zip(content_topleft, topleft)] content_offset_br = [ br - cbr for cbr, br in zip(content_bottomright, bottomright) ] size = [camera.distance_to_camera(n) for n in self.size] content_size = [ (s - cobr) - cotl for s, cobr, cotl in zip(size, content_offset_br, content_offset_tl) ] offsetratio_tl = [cotl / cs for cotl, cs in zip(content_offset_tl, size)] offsetratio_br = [cobr / cs for cobr, cs in zip(content_offset_br, size)] image_tl = [iS * otl for iS, otl in zip(self.image_size, offsetratio_tl)] image_br = [iS - (iS * obr) for iS, obr in zip(self.image_size, offsetratio_br)] if int(image_br[0] - image_tl[0]) <= 0 or int(image_br[1] - image_tl[1]) <= 0: return None segment = self.source.subsurface( ( image_tl[0], image_tl[1], image_br[0] - image_tl[0], image_br[1] - image_tl[1], ) ) scaled = pygame.transform.smoothscale_by( segment, ( content_size[0] / segment.get_width(), content_size[1] / segment.get_height(), ), ) return ((content_topleft[0], content_topleft[1]), scaled)
[docs] def draw(self, camera: Camera): scaled_src = self.scaled_source(camera) if scaled_src is not None: camera.surface.blit(scaled_src[1], scaled_src[0])
[docs] def translate(self, x: float, y: float): self.dest = (self.dest[0] + x, self.dest[1] + y)