Source code for rubato.struct.gameobject.raster

"""A Raster is a grid of pixels that you can draw shapes onto or edit individual pixels."""
from typing import Dict, Tuple
import sdl2, sdl2.ext, sdl2.sdlgfx

from . import Component
from ... import Display, Vector, Color, Radio, Draw, Camera


[docs]class Raster(Component): """ A raster area for drawings. Rasters are used to draw shapes and manipulate individual pixels. Once a rasters size is set, it cannot be changed. You can however change the scale and rotation of the raster. Args: width: The width of the raster. Defaults to 32. height: The height of the raster. Defaults to 32. offset: The offset of the raster from the gameobject. Defaults to Vector(0, 0). rot_offset: The rotation offset of the raster from the gameobject. Defaults to 0. scale: The scale of the raster. Defaults to Vector(1, 1). """ def __init__( self, width: int = 32, height: int = 32, scale: Vector = Vector(1, 1), offset: Vector = Vector(), rot_offset: float = 0, z_index: int = 0 ): super().__init__(offset=offset, rot_offset=rot_offset, z_index=z_index) self.singular = False self._raster: sdl2.SDL_Surface = sdl2.SDL_CreateRGBSurfaceWithFormat( 0, width, height, 32, sdl2.SDL_PIXELFORMAT_RGBA8888, ).contents self._drawn = Display.clone_surface(self._raster) # The raster with rotation and scale self._texture = sdl2.ext.Texture(Display.renderer, self._raster) self._scale = scale self._rot = self.rot_offset self._cam_zoom = 1 Radio.listen("ZOOM", self.cam_update) self._changed = False self._update_rotozoom() self._go_rotation = 0 @property def raster(self) -> sdl2.SDL_Surface: """The raster surface.""" return self._raster @raster.setter def raster(self, new: sdl2.SDL_Surface): self._raster = sdl2.SDL_ConvertSurfaceFormat(new, sdl2.SDL_PIXELFORMAT_RGBA8888, 0).contents self._update_rotozoom() @property def width(self) -> int: """The height of the raster in pixels.""" return self._raster.w @property def height(self) -> int: """The width of the raster in pixels.""" return self._raster.h @property def rendered_size(self) -> Vector: """The size of the raster after scaling (ie. the size it will be rendered at).""" return Vector(self._raster.w, self._raster.h) * self.scale @property def scale(self) -> Vector: """The scale of the raster.""" return self._scale @scale.setter def scale(self, new: Vector): self._scale = new self._changed = True @property def rot_offset(self) -> float: """The rotation offset of the raster.""" return self._rot @rot_offset.setter def rot_offset(self, new: float): self._rot = new self._changed = True
[docs] def draw_point(self, pos: Vector, color: Color = Color.black): """ Draws a point on the image. Args: pos: The position to draw the point. color: The color of the point. Defaults to black. """ sdl2.ext.fill( self._raster, sdl2.ext.rgba_to_color(color.rgba32), (pos.x, pos.y, 1, 1), )
[docs] def draw_line(self, start: Vector, end: Vector, color: Color = Color.black, width: int = 1): """ Draws a line on the image. Args: start: The start of the line. end: The end of the line. color: The color of the line. Defaults to black. width: The width of the line. Defaults to 1. """ sdl2.ext.line( self._raster, sdl2.ext.rgba_to_color(color.rgba32), (start.x, start.y, end.x, end.y), width, )
[docs] def draw_rect(self, top_left: Vector, bottom_right: Vector, color: Color = Color.black, width: int = 1): """ Draws a rectangle border on the image. Args: top_left: The top left corner of the rectangle. bottom_right: The bottom right corner of the rectangle. color: The color of the rectangle. Defaults to black. width: Width of the rectangle border. Defaults to 1. """ # TODO: maybe add a fill option? SDL_FillRect? self.draw_line(top_left, Vector(bottom_right.x, top_left.y), color, width) self.draw_line(Vector(bottom_right.x, top_left.y), bottom_right, color, width) self.draw_line(bottom_right, Vector(top_left.x, bottom_right.y), color, width) self.draw_line(Vector(top_left.x, bottom_right.y), top_left, color, width)
[docs] def get_pixel(self, pos: Vector) -> Color: """ Gets the color of a pixel on the image. Args: pos: The position of the pixel. Returns: Color: The color of the pixel. """ # The 4 is required because the pixel is a 32 bit value but the pixels are stored as 8 bit values # Same as # print(self._raster.format.BytesPerPixel) return Color( self._raster.contents.pixels[pos.y * self._raster.pitch + pos.x * 4 + 1], self._raster.contents.pixels[pos.y * self._raster.pitch + pos.x * 4 + 2], self._raster.contents.pixels[pos.y * self._raster.pitch + pos.x * 4 + 3], self._raster.contents.pixels[pos.y * self._raster.pitch + pos.x * 4] )
[docs] def get_pixel_tuple(self, pos: Tuple[int | float, int | float]) \ -> Tuple[int | float, int | float, int | float, int | float]: """ Gets the color of a pixel on the image. Args: pos: The position of the pixel. Returns: The color of the pixel. """ # The 4 is required because the pixel is a 32 bit value but the pixels are stored as 8 bit values # Same as self.raster.format.contents.BytesPerPixel # print(self._raster.pixels[pos[1] * self._raster.pitch + pos[0] * 4]) # THIS IS NOT WORKING, but if we are able to access the pixels like in normal sdl2, it should be fine return ( self._raster.pixels[pos[1] * self._raster.pitch + pos[0] * 4], self._raster.pixels[pos[1] * self._raster.pitch + pos[0] * 4 + 1], self._raster.pixels[pos[1] * self._raster.pitch + pos[0] * 4 + 2], self._raster.pixels[pos[1] * self._raster.pitch + pos[0] * 4 + 3] )
[docs] def set_pixel(self, pos: Vector, color: Color): """ Sets the color of a pixel on the image. Args: pos: The position of the pixel. color: The color of the pixel. """
[docs] def switch_color(self, color: Color, new_color: Color): """ Switches a color in the image. Args: color: The color to switch. new_color: The new color to switch to. """ for x in range(self.get_size().x): for y in range(self.get_size().y): if self.get_pixel(Vector(x, y)) == color: new_color.a = self.get_pixel_tuple((x, y))[0] # Preserve the alpha value. self.set_pixel(Vector(x, y), new_color) self.set_pixel(Vector(x, y), color) # Set the color of the pixel.
[docs] def set_colorkey(self, color: Color): """ Sets the colorkey of the image. Args: color: Color to set as the colorkey. """ sdl2.surface.SDL_SetColorKey( self._raster, sdl2.SDL_TRUE, sdl2.SDL_MapRGB(self._raster.format, color.r, color.g, color.b) )
[docs] def cam_update(self, info: Dict[str, Camera]): """Updates the raster size when the camera zoom changes""" self._cam_zoom = info["camera"].zoom self._changed = True
def _update_rotozoom(self): """Updates the image surface. Called automatically when image scale or rotation are updated""" if self.gameobj: self._drawn = sdl2.sdlgfx.rotozoomSurfaceXY( self._raster, -self.gameobj.rotation - self.rot_offset, self.scale.x * self._cam_zoom, self.scale.y * self._cam_zoom, 0, ).contents self._texture = sdl2.ext.Texture(Display.renderer, self._drawn)
[docs] def draw(self, camera: Camera): if self.hidden: return if self._changed or self._go_rotation != self.gameobj.rotation: self._go_rotation = self.gameobj.rotation self._changed = False self._update_rotozoom() Draw.push( self.true_z, lambda: Display.update( self._texture, camera.transform(self.gameobj.pos + self.offset - Vector(*self._texture.size) / 2) ), )
# TODO when we make raster actually work make sure to add a clone function