"""A Surface is a grid of pixels that you can draw shapes onto or edit individual pixels."""
from __future__ import annotations
import sdl2, sdl2.ext
from ..c_src import PixelEditor
from .. import Vector, Color, Display, Surf
[docs]class Surface(Surf):
"""
A surface.
Args:
width: The width of the surface in pixels. Once set this cannot be changed. Defaults to 32.
height: The height of the surface in pixels. Once set this cannot be changed. Defaults to 32.
scale: The scale of the surface. Defaults to Vector(1, 1).
af: Whether to use anisotropic filtering. Defaults to False.
"""
def __init__(
self,
width: int = 32,
height: int = 32,
scale: Vector = Vector(1, 1),
rotation: float = 0,
af: bool = False,
):
super().__init__(rotation, scale, af)
self.surf = sdl2.SDL_CreateRGBSurfaceWithFormat(0, width, height, 32, Display.pixel_format).contents
self.width: int = width
"""(READ ONLY) The width of the surface in pixels."""
self.height: int = height
"""(READ ONLY) The height of the surface in pixels."""
[docs] def clear(self):
"""
Clears the surface.
"""
PixelEditor.clear_pixels(self.surf.pixels, self.surf.w, self.surf.h)
self.uptodate = False
[docs] def draw_point(self, pos: Vector, color: Color = Color.black, blending: bool = True):
"""
Draws a point on the surface.
Args:
pos: The position to draw the point.
color: The color of the point. Defaults to black.
blending: Whether to use blending. Defaults to False.
"""
x, y = pos.tuple_int()
PixelEditor.set_pixel(self.surf.pixels, self.surf.w, self.surf.h, x, y, color.rgba32(), blending)
self.uptodate = False
[docs] def draw_line(
self,
start: Vector,
end: Vector,
color: Color = Color.black,
aa: bool = False,
thickness: int = 1,
blending: bool = True
):
"""
Draws a line on the surface.
Args:
start: The start of the line.
end: The end of the line.
color: The color of the line. Defaults to black.
aa: Whether to use anti-aliasing. Defaults to False.
thickness: The thickness of the line. Defaults to 1.
blending: Whether to use blending. Defaults to False.
"""
sx, sy = start.tuple_int()
ex, ey = end.tuple_int()
PixelEditor.draw_line(
self.surf.pixels, self.surf.w, self.surf.h, sx, sy, ex, ey, color.rgba32(), aa, blending, thickness
)
self.uptodate = False
[docs] def draw_rect(
self,
top_left: Vector,
dims: Vector,
border: Color | None = None,
border_thickness: int = 1,
fill: Color | None = None,
blending: bool = True
):
"""
Draws a rectangle on the surface.
Args:
top_left: The top left corner of the rectangle.
dims: The dimensions of the rectangle.
border: The border color of the rectangle. Defaults to None.
border_thickness: The thickness of the border. Defaults to 1.
fill: The fill color of the rectangle. Set to None for no fill. Defaults to None.
blending: Whether to use blending. Defaults to False.
"""
x, y = top_left.tuple_int()
w, h = dims.tuple_int()
PixelEditor.draw_rect(
self.surf.pixels,
self.surf.w,
self.surf.h,
x,
y,
w,
h,
border.rgba32() if border else 0,
fill.rgba32() if fill else 0,
blending,
border_thickness,
)
self.uptodate = False
[docs] def draw_circle(
self,
center: Vector,
radius: int,
border: Color | None = None,
border_thickness: int = 1,
fill: Color | None = None,
aa: bool = False,
blending: bool = True,
):
"""
Draws a circle on the surface.
Args:
center: The center of the circle.
radius: The radius of the circle.
border: The border color of the circle. Defaults to None.
border_thickness: The thickness of the border. Defaults to 1.
fill: The fill color of the circle. Set to None for no fill. Defaults to None.
aa: Whether to use anti-aliasing. Defaults to False.
blending: Whether to use blending. Defaults to False.
"""
x, y = center.tuple_int()
PixelEditor.draw_circle(
self.surf.pixels,
self.surf.w,
self.surf.h,
x,
y,
radius,
border.rgba32() if border else 0,
fill.rgba32() if fill else 0,
aa,
blending,
border_thickness,
)
self.uptodate = False
[docs] def draw_poly(
self,
points: list[Vector],
border: Color | None = None,
border_thickness: int = 1,
fill: Color | None = None,
aa: bool = False,
blending: bool = True,
):
"""
Draws a polygon on the surface.
Args:
points: The points of the polygon.
border: The border color of the polygon. Defaults to None.
border_thickness: The thickness of the border. Defaults to 1.
fill: The fill color of the polygon. Set to None for no fill. Defaults to None.
aa: Whether to use anti-aliasing. Defaults to False.
blending: Whether to use blending. Defaults to False.
"""
PixelEditor.draw_poly(
self.surf.pixels,
self.surf.w,
self.surf.h,
points,
border.rgba32() if border else 0,
fill.rgba32() if fill else 0,
aa,
blending,
border_thickness,
)
self.uptodate = False
[docs] def get_pixel(self, pos: Vector) -> Color:
"""
Gets the color of a pixel on the surface.
Args:
pos: The position of the pixel.
Returns:
The color of the pixel.
"""
x, y = pos.tuple_int()
if 0 <= x < self.surf.w and 0 <= y < self.surf.h:
return Color.from_rgba32(PixelEditor.get_pixel(self.surf.pixels, self.surf.w, self.surf.h, x, y))
else:
raise ValueError(f"Position is outside of the ${self.__class__.__name__}.")
[docs] def get_pixel_tuple(self, pos: Vector) -> tuple[int, int, int, int]:
"""
Gets the color of a pixel on the surface.
Args:
pos: The position of the pixel.
Returns:
The color of the pixel.
"""
return self.get_pixel(pos).to_tuple()
[docs] def switch_color(self, color: Color, new_color: Color):
"""
Switches a color in the surface.
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.draw_point(Vector(x, y), new_color)
self.draw_point(Vector(x, y), color) # Set the color of the pixel.
self.uptodate = False
[docs] def set_colorkey(self, color: Color):
"""
Sets the colorkey of the surface.
Args:
color: Color to set as the colorkey.
"""
sdl2.SDL_SetColorKey(self.surf, sdl2.SDL_TRUE, sdl2.SDL_MapRGB(self.surf.format, color.r, color.g, color.b))
self.uptodate = False
[docs] def clone(self) -> Surface:
"""
Clones the current surface.
Returns:
The cloned surface.
"""
new = Surface(
self.width,
self.height,
scale=self.scale,
rotation=self.rotation,
af=self.af,
)
if self.surf:
new.surf = Display.clone_surface(self.surf)
return new