Source code for tcod.sdl.render

"""SDL Rendering functionality.

.. versionadded:: 13.4
"""

from __future__ import annotations

import enum
from typing import TYPE_CHECKING, Any, Final, Literal

import numpy as np
from typing_extensions import deprecated

import tcod.sdl.constants
import tcod.sdl.video
from tcod.cffi import ffi, lib
from tcod.sdl._internal import Properties, _check, _check_p

if TYPE_CHECKING:
    from collections.abc import Sequence

    from numpy.typing import NDArray


[docs] class TextureAccess(enum.IntEnum): """Determines how a texture is expected to be used.""" STATIC = 0 """Texture rarely changes.""" STREAMING = 1 """Texture frequently changes.""" TARGET = 2 """Texture will be used as a render target."""
[docs] class RendererFlip(enum.IntFlag): """Flip parameter for :any:`Renderer.copy`.""" NONE = 0 """Default value, no flip.""" HORIZONTAL = 1 """Flip the image horizontally.""" VERTICAL = 2 """Flip the image vertically."""
[docs] class LogicalPresentation(enum.IntEnum): """SDL logical presentation modes. See https://wiki.libsdl.org/SDL3/SDL_RendererLogicalPresentation .. versionadded:: 19.0 """ DISABLED = 0 """""" STRETCH = 1 """""" LETTERBOX = 2 """""" OVERSCAN = 3 """""" INTEGER_SCALE = 4 """"""
[docs] class BlendFactor(enum.IntEnum): """SDL blend factors. .. seealso:: :any:`compose_blend_mode` https://wiki.libsdl.org/SDL_BlendFactor .. versionadded:: 13.5 """ ZERO = 0x1 """""" ONE = 0x2 """""" SRC_COLOR = 0x3 """""" ONE_MINUS_SRC_COLOR = 0x4 """""" SRC_ALPHA = 0x5 """""" ONE_MINUS_SRC_ALPHA = 0x6 """""" DST_COLOR = 0x7 """""" ONE_MINUS_DST_COLOR = 0x8 """""" DST_ALPHA = 0x9 """""" ONE_MINUS_DST_ALPHA = 0xA """"""
[docs] class BlendOperation(enum.IntEnum): """SDL blend operations. .. seealso:: :any:`compose_blend_mode` https://wiki.libsdl.org/SDL_BlendOperation .. versionadded:: 13.5 """ ADD = 0x1 """dest + source""" SUBTRACT = 0x2 """dest - source""" REV_SUBTRACT = 0x3 """source - dest""" MINIMUM = 0x4 """min(dest, source)""" MAXIMUM = 0x5 """max(dest, source)"""
[docs] class BlendMode(enum.IntEnum): """SDL blend modes. .. seealso:: :any:`Texture.blend_mode` :any:`Renderer.draw_blend_mode` :any:`compose_blend_mode` .. versionadded:: 13.5 """ NONE = 0x00000000 """""" BLEND = 0x00000001 """""" ADD = 0x00000002 """""" MOD = 0x00000004 """""" INVALID = 0x7FFFFFFF """"""
[docs] class ScaleMode(enum.IntEnum): """Texture scaling modes. .. versionadded:: 19.3 """ NEAREST = lib.SDL_SCALEMODE_NEAREST """Nearing neighbor.""" LINEAR = lib.SDL_SCALEMODE_LINEAR """Linier filtering."""
# PIXELART = lib.SDL_SCALEMODE_PIXELART # Needs SDL 3.4 # noqa: ERA001
[docs] def compose_blend_mode( # noqa: PLR0913 source_color_factor: BlendFactor, dest_color_factor: BlendFactor, color_operation: BlendOperation, source_alpha_factor: BlendFactor, dest_alpha_factor: BlendFactor, alpha_operation: BlendOperation, ) -> BlendMode: """Return a custom blend mode composed of the given factors and operations. .. seealso:: https://wiki.libsdl.org/SDL_ComposeCustomBlendMode .. versionadded:: 13.5 """ return BlendMode( lib.SDL_ComposeCustomBlendMode( source_color_factor, dest_color_factor, color_operation, source_alpha_factor, dest_alpha_factor, alpha_operation, ) )
[docs] class Texture: """SDL hardware textures. Create a new texture using :any:`Renderer.new_texture` or :any:`Renderer.upload_texture`. """ def __init__(self, sdl_texture_p: Any, sdl_renderer_p: Any = None) -> None: # noqa: ANN401 """Encapsulate an SDL_Texture pointer. This function is private.""" self.p = sdl_texture_p self._sdl_renderer_p = sdl_renderer_p # Keep alive. props = Properties(lib.SDL_GetTextureProperties(self.p)) self.format: Final[int] = props[(tcod.sdl.constants.SDL_PROP_TEXTURE_FORMAT_NUMBER, int)] """Texture format, read only.""" self.access: Final[TextureAccess] = TextureAccess( props[(tcod.sdl.constants.SDL_PROP_TEXTURE_ACCESS_NUMBER, int)] ) """Texture access mode, read only. .. versionchanged:: 13.5 Attribute is now a :any:`TextureAccess` value. """ self.width: Final[int] = props[(tcod.sdl.constants.SDL_PROP_TEXTURE_WIDTH_NUMBER, int)] """Texture pixel width, read only.""" self.height: Final[int] = props[(tcod.sdl.constants.SDL_PROP_TEXTURE_HEIGHT_NUMBER, int)] """Texture pixel height, read only."""
[docs] def __eq__(self, other: object) -> bool: """Return True if compared to the same texture.""" if isinstance(other, Texture): return bool(self.p == other.p) return NotImplemented
[docs] def __hash__(self) -> int: """Return hash for the owned pointer.""" return hash(self.p)
[docs] def update(self, pixels: NDArray[Any], rect: tuple[int, int, int, int] | None = None) -> None: """Update the pixel data of this texture. .. versionadded:: 13.5 """ if rect is None: rect = (0, 0, self.width, self.height) assert pixels.shape[:2] == (self.height, self.width) if not pixels[0].flags.c_contiguous: pixels = np.ascontiguousarray(pixels) _check(lib.SDL_UpdateTexture(self.p, (rect,), ffi.cast("void*", pixels.ctypes.data), pixels.strides[0]))
@property def alpha_mod(self) -> int: """Texture alpha modulate value, can be set to 0 - 255.""" out = ffi.new("uint8_t*") _check(lib.SDL_GetTextureAlphaMod(self.p, out)) return int(out[0]) @alpha_mod.setter def alpha_mod(self, value: int) -> None: _check(lib.SDL_SetTextureAlphaMod(self.p, value)) @property def blend_mode(self) -> BlendMode: """Texture blend mode, can be set. .. versionchanged:: 13.5 Property now returns a BlendMode instance. """ out = ffi.new("SDL_BlendMode*") _check(lib.SDL_GetTextureBlendMode(self.p, out)) return BlendMode(out[0]) @blend_mode.setter def blend_mode(self, value: int) -> None: _check(lib.SDL_SetTextureBlendMode(self.p, value)) @property def color_mod(self) -> tuple[int, int, int]: """Texture RGB color modulate values, can be set.""" rgb = ffi.new("uint8_t[3]") _check(lib.SDL_GetTextureColorMod(self.p, rgb, rgb + 1, rgb + 2)) return int(rgb[0]), int(rgb[1]), int(rgb[2]) @color_mod.setter def color_mod(self, rgb: tuple[int, int, int]) -> None: _check(lib.SDL_SetTextureColorMod(self.p, rgb[0], rgb[1], rgb[2])) @property def scale_mode(self) -> ScaleMode: """Get or set this textures :any:`ScaleMode`. .. versionadded:: 19.3 """ mode = ffi.new("SDL_ScaleMode*") _check(lib.SDL_GetTextureScaleMode(self.p, mode)) return ScaleMode(mode[0]) @scale_mode.setter def scale_mode(self, value: ScaleMode, /) -> None: _check(lib.SDL_SetTextureScaleMode(self.p, value))
class _RestoreTargetContext: """A context manager which tracks the current render target and restores it on exiting.""" def __init__(self, renderer: Renderer) -> None: self.renderer = renderer self.old_texture_p = lib.SDL_GetRenderTarget(renderer.p) def __enter__(self) -> None: pass def __exit__(self, *_: object) -> None: _check(lib.SDL_SetRenderTarget(self.renderer.p, self.old_texture_p))
[docs] class Renderer: """SDL Renderer.""" def __init__(self, sdl_renderer_p: Any) -> None: # noqa: ANN401 """Encapsulate an SDL_Renderer pointer. This function is private.""" if ffi.typeof(sdl_renderer_p) is not ffi.typeof("struct SDL_Renderer*"): msg = f"Expected a {ffi.typeof('struct SDL_Window*')} type (was {ffi.typeof(sdl_renderer_p)})." raise TypeError(msg) if not sdl_renderer_p: msg = "C pointer must not be null." raise TypeError(msg) self.p = sdl_renderer_p
[docs] def __eq__(self, other: object) -> bool: """Return True if compared to the same renderer.""" if isinstance(other, Renderer): return bool(self.p == other.p) return NotImplemented
[docs] def __hash__(self) -> int: """Return hash for the owned pointer.""" return hash(self.p)
[docs] def copy( # noqa: PLR0913 self, texture: Texture, source: tuple[float, float, float, float] | None = None, dest: tuple[float, float, float, float] | None = None, angle: float = 0, center: tuple[float, float] | None = None, flip: RendererFlip = RendererFlip.NONE, ) -> None: """Copy a texture to the rendering target. Args: texture: The texture to copy onto the current texture target. source: The (x, y, width, height) region of `texture` to copy. If None then the entire texture is copied. dest: The (x, y, width, height) region of the target. If None then the entire target is drawn over. angle: The angle in degrees to rotate the image clockwise. center: The (x, y) point where rotation is applied. If None then the center of `dest` is used. flip: Flips the `texture` when drawing it. .. versionchanged:: 13.5 `source` and `dest` can now be float tuples. Added the `angle`, `center`, and `flip` parameters. """ _check( lib.SDL_RenderTextureRotated( self.p, texture.p, (source,) if source is not None else ffi.NULL, (dest,) if dest is not None else ffi.NULL, angle, (center,) if center is not None else ffi.NULL, flip, ) )
[docs] def present(self) -> None: """Present the currently rendered image to the screen.""" lib.SDL_RenderPresent(self.p)
[docs] def set_render_target(self, texture: Texture) -> _RestoreTargetContext: """Change the render target to `texture`, returns a context that will restore the original target when exited.""" restore = _RestoreTargetContext(self) _check(lib.SDL_SetRenderTarget(self.p, texture.p)) return restore
[docs] def new_texture(self, width: int, height: int, *, format: int | None = None, access: int | None = None) -> Texture: # noqa: A002 """Allocate and return a new Texture for this renderer. Args: width: The pixel width of the new texture. height: The pixel height of the new texture. format: The format the new texture. access: The access mode of the texture. Defaults to :any:`TextureAccess.STATIC`. See :any:`TextureAccess` for more options. """ if format is None: format = 0 # noqa: A001 if access is None: access = int(lib.SDL_TEXTUREACCESS_STATIC) texture_p = ffi.gc(lib.SDL_CreateTexture(self.p, format, access, width, height), lib.SDL_DestroyTexture) return Texture(texture_p, self.p)
[docs] def upload_texture(self, pixels: NDArray[Any], *, format: int | None = None, access: int | None = None) -> Texture: # noqa: A002 """Return a new Texture from an array of pixels. Args: pixels: An RGB or RGBA array of pixels in row-major order. format: The format of `pixels` when it isn't a simple RGB or RGBA array. access: The access mode of the texture. Defaults to :any:`TextureAccess.STATIC`. See :any:`TextureAccess` for more options. """ if format is None: assert len(pixels.shape) == 3 # noqa: PLR2004 assert pixels.dtype == np.uint8 if pixels.shape[2] == 4: # noqa: PLR2004 format = int(lib.SDL_PIXELFORMAT_RGBA32) # noqa: A001 elif pixels.shape[2] == 3: # noqa: PLR2004 format = int(lib.SDL_PIXELFORMAT_RGB24) # noqa: A001 else: msg = f"Can't determine the format required for an array of shape {pixels.shape}." raise TypeError(msg) texture = self.new_texture(pixels.shape[1], pixels.shape[0], format=format, access=access) if not pixels[0].flags["C_CONTIGUOUS"]: pixels = np.ascontiguousarray(pixels) _check( lib.SDL_UpdateTexture(texture.p, ffi.NULL, ffi.cast("const void*", pixels.ctypes.data), pixels.strides[0]) ) return texture
@property def draw_color(self) -> tuple[int, int, int, int]: """Get or set the active RGBA draw color for this renderer. .. versionadded:: 13.5 """ rgba = ffi.new("uint8_t[4]") _check(lib.SDL_GetRenderDrawColor(self.p, rgba, rgba + 1, rgba + 2, rgba + 3)) return tuple(rgba) @draw_color.setter def draw_color(self, rgba: tuple[int, int, int, int]) -> None: _check(lib.SDL_SetRenderDrawColor(self.p, *rgba)) @property def draw_blend_mode(self) -> BlendMode: """Get or set the active blend mode of this renderer. .. versionadded:: 13.5 """ out = ffi.new("SDL_BlendMode*") _check(lib.SDL_GetRenderDrawBlendMode(self.p, out)) return BlendMode(out[0]) @draw_blend_mode.setter def draw_blend_mode(self, value: int) -> None: _check(lib.SDL_SetRenderDrawBlendMode(self.p, value)) @property def output_size(self) -> tuple[int, int]: """Get the (width, height) pixel resolution of the current rendering context. .. seealso:: https://wiki.libsdl.org/SDL3/SDL_GetCurrentRenderOutputSize .. versionadded:: 13.5 """ out = ffi.new("int[2]") _check(lib.SDL_GetCurrentRenderOutputSize(self.p, out, out + 1)) return tuple(out) @property def clip_rect(self) -> tuple[int, int, int, int] | None: """Get or set the clipping rectangle of this renderer. Set to None to disable clipping. .. versionadded:: 13.5 """ if not lib.SDL_RenderClipEnabled(self.p): return None rect = ffi.new("SDL_Rect*") lib.SDL_GetRenderClipRect(self.p, rect) return rect.x, rect.y, rect.w, rect.h @clip_rect.setter def clip_rect(self, rect: tuple[int, int, int, int] | None) -> None: rect_p = ffi.NULL if rect is None else ffi.new("SDL_Rect*", rect) _check(lib.SDL_SetRenderClipRect(self.p, rect_p))
[docs] def set_logical_presentation(self, resolution: tuple[int, int], mode: LogicalPresentation) -> None: """Set this renderers device independent resolution. .. seealso:: https://wiki.libsdl.org/SDL3/SDL_SetRenderLogicalPresentation .. versionadded:: 19.0 """ width, height = resolution _check(lib.SDL_SetRenderLogicalPresentation(self.p, width, height, mode))
@property def logical_size(self) -> tuple[int, int] | None: """Get current independent (width, height) resolution, or None if logical size is unset. .. seealso:: https://wiki.libsdl.org/SDL3/SDL_GetRenderLogicalPresentation .. versionadded:: 13.5 .. versionchanged:: 19.0 Setter is deprecated, use :any:`set_logical_presentation` instead. .. versionchanged:: 20.0 Return ``None`` instead of ``(0, 0)`` when logical size is disabled. """ out = ffi.new("int[2]") lib.SDL_GetRenderLogicalPresentation(self.p, out, out + 1, ffi.NULL) out_tuple = tuple(out) return None if out_tuple == (0, 0) else out_tuple @logical_size.setter @deprecated("Use set_logical_presentation method to correctly setup logical size.") def logical_size(self, size: tuple[int, int]) -> None: width, height = size _check(lib.SDL_SetRenderLogicalPresentation(self.p, width, height, lib.SDL_LOGICAL_PRESENTATION_STRETCH)) @property def scale(self) -> tuple[float, float]: """Get or set an (x_scale, y_scale) multiplier for drawing. .. seealso:: https://wiki.libsdl.org/SDL3/SDL_SetRenderScale .. versionadded:: 13.5 """ out = ffi.new("float[2]") lib.SDL_GetRenderScale(self.p, out, out + 1) return out[0], out[1] @scale.setter def scale(self, scale: tuple[float, float]) -> None: _check(lib.SDL_SetRenderScale(self.p, *scale)) @property def viewport(self) -> tuple[int, int, int, int] | None: """Get or set the drawing area for the current rendering target. .. seealso:: https://wiki.libsdl.org/SDL3/SDL_SetRenderViewport .. versionadded:: 13.5 """ rect = ffi.new("SDL_Rect*") lib.SDL_GetRenderViewport(self.p, rect) return rect.x, rect.y, rect.w, rect.h @viewport.setter def viewport(self, rect: tuple[int, int, int, int] | None) -> None: _check(lib.SDL_SetRenderViewport(self.p, (rect,)))
[docs] def set_vsync(self, enable: bool) -> None: # noqa: FBT001 """Enable or disable VSync for this renderer. .. versionadded:: 13.5 """ _check(lib.SDL_SetRenderVSync(self.p, enable))
[docs] def read_pixels( self, *, rect: tuple[int, int, int, int] | None = None, format: Literal["RGB", "RGBA"] = "RGBA", # noqa: A002 out: NDArray[np.uint8] | None = None, ) -> NDArray[np.uint8]: """Fetch the pixel contents of the current rendering target to an array. By default returns an RGBA pixel array of the full target in the shape: ``(height, width, rgba)``. The target can be changed with :any:`set_render_target` Args: rect: The ``(left, top, width, height)`` region of the target to fetch, or None for the entire target. format: The pixel format. Defaults to ``"RGBA"``. out: The output array. Can be None or must be an ``np.uint8`` array of shape: ``(height, width, channels)``. Must be C contiguous along the ``(width, channels)`` axes. This operation is slow due to coping from VRAM to RAM. When reading the main rendering target this should be called after rendering and before :any:`present`. See https://wiki.libsdl.org/SDL3/SDL_RenderReadPixels Returns: The output uint8 array of shape ``(height, width, channels)`` with the fetched pixels. .. versionadded:: 15.0 .. versionchanged:: 19.0 `format` no longer accepts `int` values. """ surface = _check_p( ffi.gc(lib.SDL_RenderReadPixels(self.p, (rect,) if rect is not None else ffi.NULL), lib.SDL_DestroySurface) ) width, height = rect[2:4] if rect is not None else (int(surface.w), int(surface.h)) depth = {"RGB": 3, "RGBA": 4}.get(format) if depth is None: msg = f"Pixel format {format!r} not supported by tcod." raise TypeError(msg) expected_shape = height, width, depth if out is None: out = np.empty(expected_shape, dtype=np.uint8) if out.dtype != np.uint8: msg = "`out` must be a uint8 array." raise TypeError(msg) if out.shape != expected_shape: msg = f"Expected `out` to be an array of shape {expected_shape}, got {out.shape} instead." raise TypeError(msg) if not out[0].flags.c_contiguous: msg = "`out` array must be C contiguous." out_surface = tcod.sdl.video._TempSurface(out) _check(lib.SDL_BlitSurface(surface, ffi.NULL, out_surface.p, ffi.NULL)) return out
[docs] def clear(self) -> None: """Clear the current render target with :any:`draw_color`. .. versionadded:: 13.5 """ _check(lib.SDL_RenderClear(self.p))
[docs] def fill_rect(self, rect: tuple[float, float, float, float]) -> None: """Fill a rectangle with :any:`draw_color`. .. versionadded:: 13.5 """ _check(lib.SDL_RenderFillRect(self.p, (rect,)))
[docs] def draw_rect(self, rect: tuple[float, float, float, float]) -> None: """Draw a rectangle outline. .. versionadded:: 13.5 """ _check(lib.SDL_RenderFillRects(self.p, (rect,), 1))
[docs] def draw_point(self, xy: tuple[float, float]) -> None: """Draw a point. .. versionadded:: 13.5 """ x, y = xy _check(lib.SDL_RenderPoint(self.p, x, y))
[docs] def draw_line(self, start: tuple[float, float], end: tuple[float, float]) -> None: """Draw a single line. .. versionadded:: 13.5 """ x1, y1 = start x2, y2 = end _check(lib.SDL_RenderLine(self.p, x1, y1, x2, y2))
@staticmethod def _convert_array(array: NDArray[np.number] | Sequence[Sequence[float]], item_length: int) -> NDArray[np.float32]: """Convert ndarray for a SDL function expecting a C contiguous array of either intc or float32. Array shape is enforced to be (n, item_length) """ out = np.ascontiguousarray(array, np.float32) if len(out.shape) != 2: # noqa: PLR2004 msg = f"Array must have 2 axes, but shape is {out.shape!r}" raise TypeError(msg) if out.shape[1] != item_length: msg = f"Array shape[1] must be {item_length}, but shape is {out.shape!r}" raise TypeError(msg) return out
[docs] def fill_rects(self, rects: NDArray[np.number] | Sequence[tuple[float, float, float, float]]) -> None: """Fill multiple rectangles from an array. Args: rects: A sequence or array of (x, y, width, height) rectangles. .. versionadded:: 13.5 """ rects = self._convert_array(rects, item_length=4) _check(lib.SDL_RenderFillRects(self.p, ffi.from_buffer("SDL_FRect*", rects), rects.shape[0]))
[docs] def draw_rects(self, rects: NDArray[np.number] | Sequence[tuple[float, float, float, float]]) -> None: """Draw multiple outlined rectangles from an array. Args: rects: A sequence or array of (x, y, width, height) rectangles. .. versionadded:: 13.5 """ rects = self._convert_array(rects, item_length=4) assert len(rects.shape) == 2 # noqa: PLR2004 assert rects.shape[1] == 4 # noqa: PLR2004 _check(lib.SDL_RenderRects(self.p, ffi.from_buffer("SDL_FRect*", rects), rects.shape[0]))
[docs] def draw_points(self, points: NDArray[np.number] | Sequence[tuple[float, float]]) -> None: """Draw an array of points. Args: points: A sequence or array of (x, y) points. .. versionadded:: 13.5 """ points = self._convert_array(points, item_length=2) _check(lib.SDL_RenderPoints(self.p, ffi.from_buffer("SDL_FPoint*", points), points.shape[0]))
[docs] def draw_lines(self, points: NDArray[np.number] | Sequence[tuple[float, float]]) -> None: """Draw a connected series of lines from an array. Args: points: A sequence or array of (x, y) points. .. versionadded:: 13.5 """ points = self._convert_array(points, item_length=2) _check(lib.SDL_RenderLines(self.p, ffi.from_buffer("SDL_FPoint*", points), points.shape[0]))
[docs] def geometry( self, texture: Texture | None, xy: NDArray[np.float32] | Sequence[tuple[float, float]], color: NDArray[np.float32] | Sequence[tuple[float, float, float, float]], uv: NDArray[np.float32] | Sequence[tuple[float, float]], indices: NDArray[np.uint8 | np.uint16 | np.uint32] | None = None, ) -> None: """Render triangles from texture and vertex data. Args: texture: The SDL texture to render from. xy: A sequence of (x, y) points to buffer. color: A sequence of (r, g, b, a) colors to buffer. uv: A sequence of (x, y) coordinates to buffer. indices: A sequence of indexes referring to the buffered data, every 3 indexes is a triangle to render. .. versionadded:: 13.5 .. versionchanged:: 19.0 `color` now takes float values instead of 8-bit integers. """ xy = np.ascontiguousarray(xy, np.float32) assert len(xy.shape) == 2 # noqa: PLR2004 assert xy.shape[1] == 2 # noqa: PLR2004 color = np.ascontiguousarray(color, np.float32) assert len(color.shape) == 2 # noqa: PLR2004 assert color.shape[1] == 4 # noqa: PLR2004 uv = np.ascontiguousarray(uv, np.float32) assert len(uv.shape) == 2 # noqa: PLR2004 assert uv.shape[1] == 2 # noqa: PLR2004 if indices is not None: assert indices.dtype.type in (np.uint8, np.uint16, np.uint32, np.int8, np.int16, np.int32) indices = np.ascontiguousarray(indices) assert len(indices.shape) == 1 assert xy.shape[0] == color.shape[0] == uv.shape[0] _check( lib.SDL_RenderGeometryRaw( self.p, texture.p if texture else ffi.NULL, ffi.cast("float*", xy.ctypes.data), xy.strides[0], ffi.cast("SDL_FColor*", color.ctypes.data), color.strides[0], ffi.cast("float*", uv.ctypes.data), uv.strides[0], xy.shape[0], # Number of vertices. ffi.cast("void*", indices.ctypes.data) if indices is not None else ffi.NULL, indices.size if indices is not None else 0, indices.itemsize if indices is not None else 0, ) )
[docs] def coordinates_from_window(self, xy: tuple[float, float], /) -> tuple[float, float]: """Return the renderer coordinates from the given windows coordinates. .. seealso:: https://wiki.libsdl.org/SDL3/SDL_RenderCoordinatesFromWindow .. versionadded:: 20.0 """ x, y = xy out_xy = ffi.new("float[2]") _check(lib.SDL_RenderCoordinatesFromWindow(self.p, x, y, out_xy, out_xy + 1)) return tuple(out_xy)
[docs] def coordinates_to_window(self, xy: tuple[float, float], /) -> tuple[float, float]: """Return the window coordinates from the given render coordinates. .. seealso:: https://wiki.libsdl.org/SDL3/SDL_RenderCoordinatesToWindow .. versionadded:: 20.0 """ x, y = xy out_xy = ffi.new("float[2]") _check(lib.SDL_RenderCoordinatesToWindow(self.p, x, y, out_xy, out_xy + 1)) return tuple(out_xy)
[docs] def new_renderer( window: tcod.sdl.video.Window, *, driver: str | None = None, vsync: int = True, ) -> Renderer: """Initialize and return a new SDL Renderer. Args: window: The window that this renderer will be attached to. driver: Force SDL to use a specific video driver. vsync: If True then Vsync will be enabled. Example:: # Start by creating a window. sdl_window = tcod.sdl.video.new_window(640, 480) # Create a renderer with target texture support. sdl_renderer = tcod.sdl.render.new_renderer(sdl_window) .. seealso:: :func:`tcod.sdl.video.new_window` .. versionchanged:: 19.0 Removed `software` and `target_textures` parameters. `vsync` now takes an integer. `driver` now take a string. """ props = Properties() props[(tcod.sdl.constants.SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, int)] = vsync props[(tcod.sdl.constants.SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, tcod.sdl.video.Window)] = window if driver is not None: props[(tcod.sdl.constants.SDL_PROP_RENDERER_CREATE_NAME_STRING, str)] = driver renderer_p = _check_p(ffi.gc(lib.SDL_CreateRendererWithProperties(props.p), lib.SDL_DestroyRenderer)) return Renderer(renderer_p)