"""Handles the rendering of libtcod's tilesets.
Using this module you can render a console to an SDL :any:`Texture` directly, letting you have full control over how
consoles are displayed.
This includes rendering multiple tilesets in a single frame and rendering consoles on top of each other.
Example::
tileset = tcod.tileset.load_tilesheet("dejavu16x16_gs_tc.png", 32, 8, tcod.tileset.CHARMAP_TCOD)
console = tcod.console.Console(20, 8)
console.print(0, 0, "Hello World")
sdl_window = tcod.sdl.video.new_window(
console.width * tileset.tile_width,
console.height * tileset.tile_height,
flags=tcod.lib.SDL_WINDOW_RESIZABLE,
)
sdl_renderer = tcod.sdl.render.new_renderer(sdl_window, target_textures=True)
atlas = tcod.render.SDLTilesetAtlas(sdl_renderer, tileset)
console_render = tcod.render.SDLConsoleRender(atlas)
while True:
sdl_renderer.copy(console_render.render(console))
sdl_renderer.present()
for event in tcod.event.wait():
if isinstance(event, tcod.event.Quit):
raise SystemExit()
.. versionadded:: 13.4
"""
from __future__ import annotations
from typing import Any, Final
import tcod.console
import tcod.sdl.render
import tcod.tileset
from tcod._internal import _check, _check_p
from tcod.cffi import ffi, lib
[docs]
class SDLTilesetAtlas:
"""Prepares a tileset for rendering using SDL."""
def __init__(self, renderer: tcod.sdl.render.Renderer, tileset: tcod.tileset.Tileset) -> None:
self._renderer = renderer
self.tileset: Final[tcod.tileset.Tileset] = tileset
"""The tileset used to create this SDLTilesetAtlas."""
self.p: Final = ffi.gc(
_check_p(lib.TCOD_sdl2_atlas_new(renderer.p, tileset._tileset_p)), lib.TCOD_sdl2_atlas_delete
)
@classmethod
def _from_ref(cls, renderer_p: Any, atlas_p: Any) -> SDLTilesetAtlas: # noqa: ANN401
self = object.__new__(cls)
# Ignore Final reassignment type errors since this is an alternative constructor.
# This could be a sign that the current constructor was badly implemented.
self._renderer = tcod.sdl.render.Renderer(renderer_p)
self.tileset = tcod.tileset.Tileset._from_ref(atlas_p.tileset) # type: ignore[misc]
self.p = atlas_p # type: ignore[misc]
return self
[docs]
class SDLConsoleRender:
"""Holds an internal cache console and texture which are used to optimized console rendering."""
def __init__(self, atlas: SDLTilesetAtlas) -> None:
self.atlas: Final[SDLTilesetAtlas] = atlas
"""The SDLTilesetAtlas used to create this SDLConsoleRender.
.. versionadded:: 13.7
"""
self._renderer = atlas._renderer
self._cache_console: tcod.console.Console | None = None
self._texture: tcod.sdl.render.Texture | None = None
[docs]
def render(self, console: tcod.console.Console) -> tcod.sdl.render.Texture:
"""Render a console to a cached Texture and then return the Texture.
You should not draw onto the returned Texture as only changed parts of it will be updated on the next call.
This function requires the SDL renderer to have target texture support.
It will also change the SDL target texture for the duration of the call.
"""
if self._cache_console and (
self._cache_console.width != console.width or self._cache_console.height != console.height
):
self._cache_console = None
self._texture = None
if self._cache_console is None or self._texture is None:
self._cache_console = tcod.console.Console(console.width, console.height)
self._texture = self._renderer.new_texture(
self.atlas.tileset.tile_width * console.width,
self.atlas.tileset.tile_height * console.height,
format=int(lib.SDL_PIXELFORMAT_RGBA32),
access=int(lib.SDL_TEXTUREACCESS_TARGET),
)
with self._renderer.set_render_target(self._texture):
_check(
lib.TCOD_sdl2_render_texture(
self.atlas.p, console.console_c, self._cache_console.console_c, self._texture.p
)
)
return self._texture