1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902
|
from ctypes import byref, c_int, c_float
from .. import blendmode, surface, rect, video, render, error, dll, hints
from ..stdinc import Uint8, Uint32
from .color import convert_to_color
from .err import raise_sdl_err
from .compat import deprecated, stringify, byteify, isiterable
from .sprite import SoftwareSprite, TextureSprite
from .surface import _get_target_surface
from .window import Window
__all__ = ["set_texture_scale_quality", "Renderer", "Texture"]
# NOTE: The following 3 utility functions probably belong in a separate module
def is_numeric(x):
try:
return float(x) == x
except (TypeError, ValueError):
return False
def _is_point(x):
if isiterable(x):
return len(x) == 2
if isinstance(x, rect.SDL_Point) or isinstance(x, rect.SDL_FPoint):
return True
return False
def _is_rect(x):
if isiterable(x):
return len(x) == 4
if isinstance(x, rect.SDL_Rect) or isinstance(x, rect.SDL_FRect):
return True
return False
def _sanitize_points(points):
# If first item is numeric, assume flat list of points
if isinstance(points, rect.SDL_Point) or isinstance(points, rect.SDL_FPoint):
points = [points]
elif is_numeric(points[0]):
if len(points) % 2 != 0:
raise ValueError("Flat x/y coordinate lists must have even length")
chunked = []
for i in range(0, len(points), 2):
p = (points[i], points[i+1])
chunked.append(p)
points = chunked
type_err = (
"Points must be specified as either (x, y) tuples or "
"SDL_Point objects (Got unsupported format '{0}')"
)
pts = []
if not isiterable(points):
points = [points]
for p in points:
if isinstance(p, rect.SDL_Point) or isinstance(p, rect.SDL_FPoint):
p = (p.x, p.y)
elif len(p) != 2:
raise ValueError(type_err.format(str(p)))
if dll.version < 2010 and any([int(v) != v for v in p]):
e = "Floating point rendering requires SDL 2.0.10 or newer"
raise RuntimeError(e + " (got '{0}')".format(str(p)))
pts.append((p[0], p[1]))
return pts
def _sanitize_rects(rects):
type_err = (
"Rectangles must be specified as either (x, y, w, h) tuples or "
"SDL_Rect objects (Got unsupported format '{0}')"
)
out = []
if not (isiterable(rects) and _is_rect(rects[0])):
rects = [rects]
for r in rects:
if isinstance(r, rect.SDL_Rect) or isinstance(r, rect.SDL_FRect):
r = (r.x, r.y, r.w, r.h)
elif len(r) != 4:
raise ValueError(type_err.format(str(r)))
if dll.version < 2010 and any([int(v) != v for v in r]):
e = "Floating point rendering requires SDL 2.0.10 or newer"
raise RuntimeError(e + " (got '{0}')".format(str(r)))
out.append((r[0], r[1], r[2], r[3]))
return out
def _get_texture_size(texture):
flags = Uint32()
access, w, h = (c_int(), c_int(), c_int())
ret = render.SDL_QueryTexture(
texture, byref(flags), byref(access), byref(w), byref(h)
)
if ret < 0:
raise_sdl_err("getting texture attributes")
return (w.value, h.value)
def set_texture_scale_quality(method):
"""Sets the default scaling quailty for :obj:`~sdl2.ext.Texture` objects.
By default, SDL2 uses low-quality nearest-neighbour scaling for all new
textures. This method lets you change the default scaling method to one of
the following options:
========= =============================================================
Method Description
========= =============================================================
Nearest Nearest-neighbour pixel scaling (no filtering)
Linear Linear filtering
Best Anisotropic filtering (falls back to linear if not available)
========= =============================================================
This function does not apply retroactively, and will only affect textures
created after it is called.
Args:
method (str): The default scaling method to use for SDL textures. Must
be one of 'nearest', 'linear', or 'best'.
"""
method = byteify(method.lower())
if method not in [b"nearest", b"linear", b"best"]:
raise ValueError(
"Texture scaling method must be 'nearest', 'linear', or 'best'."
)
hints.SDL_SetHint(hints.SDL_HINT_RENDER_SCALE_QUALITY, method)
class Texture(object):
"""A 2D texture to be used with a :obj:`~sdl2.ext.Renderer`.
In SDL2, textures are 2D images that have been prepared for fast rendering
with a given renderer. For example, if an SDL surface is converted into a
texture with a :obj:`~sdl2.ext.Renderer` using the OpenGL backend, the pixel
data for the surface will internally be converted into an OpenGL texture.
Once a surface has been converted into a :obj:`Texture`, the surface can be
safely deleted if no longer needed.
Args:
renderer (:obj:`~sdl2.ext.Renderer`): The renderer associated with the
texture.
surface (:obj:`~sdl2.SDL_Surface`): An SDL surface from which the
texture will be created.
"""
def __init__(self, renderer, surface):
# Validate and get reference to the parent renderer
self._renderer_ref = None
if isinstance(renderer, Renderer):
self._renderer_ref = renderer._renderer_ref
elif hasattr(renderer, "contents"):
if isinstance(renderer.contents, render.SDL_Renderer):
self._renderer_ref = [renderer]
if self._renderer_ref is None:
raise TypeError(
"'renderer' must be a valid Renderer object or a pointer to "
"an SDL_Renderer."
)
# Convert the passed surface into a texture
surface = _get_target_surface(surface, "surface")
self._tx = render.SDL_CreateTextureFromSurface(self._renderer, surface)
if not self._tx:
raise_sdl_err("creating the texture")
# Cache the size of the texture for future use
self._size = _get_texture_size(self.tx)
def __del__(self):
if hasattr(self, "_tx"):
self.destroy()
@property
def _renderer(self):
# NOTE: This is sort of a hack to get around a ctypes problem. Basically,
# if the parent renderer of a texture is destroyed before the texture
# itself, destroying or doing anything with that texture will result in a
# segfault. By wrapping the renderer in a List, we can overwrite it with
# None when destroyed (within the Renderer class) and detect that in any
# child textures to avoid any memory-unsafe behaviour.
return self._renderer_ref[0]
@property
def tx(self):
""":obj:`~sdl2.SDL_Texture`: The underlying base SDL texture object.
Can be used to perform operations with the texture using the base
PySDL2 bindings.
"""
if self._tx is None:
raise RuntimeError(
"Cannot use a texture after it has been destroyed."
)
elif self._renderer_ref[0] is None:
raise RuntimeError(
"Cannot use a texture after its parent renderer has been "
"destroyed."
)
return self._tx
@property
def size(self):
"""tuple: The width and height (in pixels) of the texture."""
tx = self.tx # Makes sure texture is still usable
return self._size
def destroy(self):
"""Deletes the texture and frees its associated memory.
When a texture is no longer needed, it should be destroyed using this
method to free up memory for new textures. After being destroyed, a
texture can no longer be used.
"""
if self._tx and self._renderer_ref[0]:
render.SDL_DestroyTexture(self._tx)
self._tx = None
@property
def scale_mode(self):
"""str: The current scaling mode to use for rendering the texture. Can
be 'nearest', 'linear', 'best', or 'unknown'.
See :func:`set_texture_scale_quality` for more information.
.. note::
For SDL versions older than 2.0.12, the scaling mode will always be
'unknown'.
"""
if dll.version < 2012:
return "unknown"
modes = ["nearest", "linear", "best"]
mode_num = c_int()
ret = render.SDL_GetTextureScaleMode(self.tx, byref(mode_num))
if ret < 0:
raise_sdl_err("retrieving the texture scaling mode")
try:
return modes[mode_num.value]
except IndexError:
return "unknown"
def set_scale_mode(self, mode):
"""Sets a custom scaling method to use for rendering the texture.
This method overrides the default texture scaling method specified by
:func:`set_texture_scale_quality`, the documentation for which describes
the different possible scaling modes.
.. note::
Support for custom per-texture scaling modes is only available in
SDL2 2.0.12 and up. As such, this method has no effect when used with
earlier releases of SDL2.
Args:
mode (str): The scaling method to use for the current texture. Must
be one of 'nearest', 'linear', or 'best'.
"""
if dll.version < 2012:
return None
modes = {
"nearest": render.SDL_ScaleModeNearest,
"linear": render.SDL_ScaleModeLinear,
"best": render.SDL_ScaleModeBest,
}
mode = mode.lower()
if mode not in modes.keys():
raise ValueError(
"Texture scaling mode must be either 'nearest', 'linear', "
"or 'best'."
)
ret = render.SDL_SetTextureScaleMode(self.tx, modes[mode])
if ret < 0:
raise_sdl_err("setting the texture scaling mode")
class Renderer(object):
"""A rendering context for SDL2 windows and sprites.
A ``Renderer`` can be created from an SDL window, an SDL Surface, or a
:obj:`~sdl2.ext.SoftwareSprite`. Depending on the settings and operating
system, this rendering context can use either hardware or software
acceleration.
A useful feature of SDL renderers is that that the logical size of the
rendering context can be different than the actual size (in pixels) of its
target window or surface. For example, the renderer for a 1024x768 window
can be set to have a logical size of 640x480, improving performance at the
cost of image quality. The rendered content will be automatically scaled to
fit the target, and will be centered with black bars on either side in the
case of an aspect ratio mismatch.
If creating a rendering context from a window, you can customize the
renderer using flags to request different settings:
=============================== ===========================================
Flag Description
=============================== ===========================================
``SDL_RENDERER_SOFTWARE`` Requests a software-accelerated renderer.
``SDL_RENDERER_ACCELERATED`` Requests a hardware-accelerated renderer.
``SDL_RENDERER_PRESENTVSYNC`` Enables vsync support for :meth:`present`.
``SDL_RENDERER_TARGETTEXTURE`` Requests support for rendering to texture.
=============================== ===========================================
To combine multiple flags, you can use a bitwise OR to combine two or more
together before passing them to the `flags` argument::
render_flags = (
sdl2.SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
)
sdl2.ext.Renderer(window, flags=render_flags)
By default, SDL2 will choose the first renderer backend that supports all
the requested flags. However, you can also request a specific rendering
backend by name (e.g. 'opengl', 'opengles2', 'metal', 'direct3d', etc.),
giving you more control but likely making your code less cross-platform.
Args:
target (:obj:`~sdl2.ext.Window`, :obj:`~sdl2.SDL_Surface`): The target
window or surface from which to create the rendering context.
backend (str or int, optional): The name of the specific backend to use
for the new rendering context (e.g. 'opengl'). Defaults to letting
SDL2 decide. If ``target`` is not an SDL window, this argument has
no effect.
logical_size (tuple, optional): The initial logical size (in pixels) of
the rendering context as a ``(w, h)`` tuple. Defaults to the size
of the target window or surface.
flags (int, optional): The requested features and settings for the
new rendering context. Defaults to requesting a hardware-accelerated
context. If ``target`` is not an SDL window, this argument has no
effect.
Raises:
RuntimeError: If a requested rendering backend is not available.
"""
def __init__(self, target, backend=-1, logical_size=None,
flags=render.SDL_RENDERER_ACCELERATED):
self._renderer_ref = None
self.rendertarget = None
available = self._get_render_drivers()
if isinstance(backend, str):
if backend.lower() in available:
index = available.index(backend.lower())
else:
e1 = "'{0}' is not a supported renderer on this system. "
e2 = "The renderer backend must be one of the following: "
raise RuntimeError(e1.format(backend) + e2 + str(available))
else:
index = backend
_size = None
if isinstance(target, Window):
_renderer = render.SDL_CreateRenderer(target.window, index, flags)
_size = target.size
elif isinstance(target, video.SDL_Window):
_renderer = render.SDL_CreateRenderer(target, index, flags)
w, h = c_int(0), c_int(0)
video.SDL_GetWindowSize(target, byref(w), byref(h))
_size = (w.value, h.value)
elif isinstance(target, SoftwareSprite):
_renderer = render.SDL_CreateSoftwareRenderer(target.surface)
_size = target.size
elif isinstance(target, surface.SDL_Surface):
_renderer = render.SDL_CreateSoftwareRenderer(target)
_size = (target.w, target.h)
elif "SDL_Surface" in str(type(target)):
_renderer = render.SDL_CreateSoftwareRenderer(target.contents)
_size = (target.contents.w, target.contents.h)
else:
raise TypeError("unsupported target type")
if not _renderer:
raise_sdl_err("creating the SDL renderer")
error.SDL_ClearError() # Clear any errors from renderer selection
self._renderer_ref = [_renderer]
self.rendertarget = target
self.color = (0, 0, 0, 0) # Set black as the default draw color
self.logical_size = _size
self._original_logical_size = self.logical_size
if logical_size is not None:
self.logical_size = logical_size
def __del__(self):
if self._renderer_ref is not None:
self.destroy()
def _get_render_drivers(self):
renderers = []
drivers = render.SDL_GetNumRenderDrivers()
for x in range(drivers):
info = render.SDL_RendererInfo()
ret = render.SDL_GetRenderDriverInfo(x, info)
if ret == 0:
renderers.append(stringify(info.name))
error.SDL_ClearError()
return renderers
@property
def sdlrenderer(self):
""":obj:`~sdl2.SDL_Renderer`: The underlying base SDL renderer object.
Can be used to perform operations with the renderer using the base
PySDL2 bindings.
"""
if self._renderer_ref[0] is None:
raise RuntimeError(
"Cannot use a renderer after it has been destroyed."
)
return self._renderer_ref[0]
@property
@deprecated
def renderer(self):
return self.sdlrenderer
@property
def logical_size(self):
"""tuple: The logical size of the rendering context (in pixels), as a
``(width, height)`` tuple.
"""
w, h = c_int(0), c_int(0)
render.SDL_RenderGetLogicalSize(self.sdlrenderer, byref(w), byref(h))
return w.value, h.value
@logical_size.setter
def logical_size(self, size):
width, height = size
ret = render.SDL_RenderSetLogicalSize(self.sdlrenderer, width, height)
if ret != 0:
raise_sdl_err("setting the logical size of the renderer")
@property
def color(self):
""":obj:`~sdl2.ext.Color`: The current drawing color of the renderer."""
r, g, b, a = Uint8(0), Uint8(0), Uint8(0), Uint8(0)
ret = render.SDL_GetRenderDrawColor(self.sdlrenderer, byref(r), byref(g),
byref(b), byref(a))
if ret < 0:
raise_sdl_err("retrieving the drawing color of the renderer")
return convert_to_color((r.value, g.value, b.value, a.value))
@color.setter
def color(self, value):
c = convert_to_color(value)
ret = render.SDL_SetRenderDrawColor(self.sdlrenderer, c.r, c.g, c.b, c.a)
if ret < 0:
raise_sdl_err("setting the drawing color of the renderer")
@property
def blendmode(self):
"""int: The blend mode used for :meth:`fill` and :meth:`line` drawing
operations. This value can be any of the following constants:
========================= ====================================
Flag Description
========================= ====================================
``SDL_BLENDMODE_NONE`` No blending
``SDL_BLENDMODE_BLEND`` Alpha channel blending
``SDL_BLENDMODE_ADD`` Additive blending
``SDL_BLENDMODE_MOD`` Color modulation
``SDL_BLENDMODE_MUL`` Color multiplication (SDL >= 2.0.12)
========================= ====================================
"""
mode = blendmode.SDL_BlendMode()
ret = render.SDL_GetRenderDrawBlendMode(self.sdlrenderer, byref(mode))
if ret < 0:
raise_sdl_err("retrieving the blend mode for the renderer")
return mode
@blendmode.setter
def blendmode(self, value):
ret = render.SDL_SetRenderDrawBlendMode(self.sdlrenderer, value)
if ret < 0:
raise_sdl_err("setting the blend mode for the renderer")
@property
def scale(self):
"""tuple: The x/y scaling factors applied to all drawing coordinates
before rendering, in the format ``(scale_x, scale_y)``. These can be
used to facilitate resolution-independent drawing.
"""
sx = c_float(0.0)
sy = c_float(0.0)
render.SDL_RenderGetScale(self.sdlrenderer, byref(sx), byref(sy))
return sx.value, sy.value
@scale.setter
def scale(self, value):
if any([s <= 0 for s in value]):
raise ValueError("Scaling factors must be greater than zero.")
sx, sy = value
ret = render.SDL_RenderSetScale(self.sdlrenderer, sx, sy)
if ret != 0:
raise_sdl_err("setting the scaling factors for the renderer")
def destroy(self):
"""Destroys the renderer and any associated textures.
When a renderer is no longer needed, it should be destroyed using this
method to free up its associated memory. After being destroyed, a
renderer can no longer be used.
"""
if self._renderer_ref[0]:
render.SDL_DestroyRenderer(self._renderer_ref[0])
self._renderer_ref[0] = None
self.rendertarget = None
def reset_logical_size(self):
"""Resets the logical size of the renderer to its original value."""
self.logical_size = self._original_logical_size
def clear(self, color=None):
"""Clears the rendering surface with a given color.
Args:
color (:obj:`~sdl2.ext.Color`, optional): The color with which to
clear the entire rendering context. If not specified, the
renderer's current :attr:`color` will be used.
"""
if color is None:
ret = render.SDL_RenderClear(self.sdlrenderer)
else:
tmp = self.color
self.color = color
ret = render.SDL_RenderClear(self.sdlrenderer)
self.color = tmp
if ret < 0:
raise_sdl_err("clearing the rendering context")
def copy(self, src, srcrect=None, dstrect=None, angle=0, center=None,
flip=render.SDL_FLIP_NONE):
"""Copies (blits) a texture to the rendering context.
If the source texture is an :obj:`~sdl2.SDL_Surface`, you will need
to convert it into a :obj:`~sdl2.ext.Texture` first before it can be
copied to the rendering surface.
The source texture can be flipped horizontally or vertically when
being copied to the rendering context using one of the following
flags:
========================= ===================================
Flag Description
========================= ===================================
``SDL_FLIP_NONE`` Does not flip the source (default)
``SDL_FLIP_HORIZONTAL`` Flips the source horizontally
``SDL_FLIP_VERTICAL`` Flips the source vertically
========================= ===================================
.. note::
Subpixel rendering (i.e. using floats as pixel coordinates) requires
SDL 2.0.10 or newer.
Args:
src (:obj:`~sdl2.ext.Texture`, :obj:`~sdl2.SDL_Texture`): The source
texture to copy to the rendering surface.
srcrect (tuple, optional): An ``(x, y, w, h)`` rectangle defining
the subset of the source texture to copy to the rendering
surface. Defaults to copying the entire source texture.
dstrect (tuple, optional): An ``(x, y, w, h)`` rectangle
defining the region of the rendering surface to which the
source texture will be copied. Alternatively, if only
``(x, y)`` coordinates are provided, the width and height of
the source rectangle will be used. Defaults to stretching
the source across the entire rendering context.
angle (float, optional): The clockwise rotation (in degrees) to
apply to the destination rectangle. Defaults to no rotation.
center (tuple, optional): The point around with the destination
rectangle will be rotated. Defaults to the center of the
destination rectangle.
flip (int, optional): A flag indicating whether the source should
be flipped (horizontally or vertically) when rendering to the
render context. Defaults to no flipping.
"""
if dll.version < 2010:
render_copy = render.SDL_RenderCopyEx
Point = rect.SDL_Point
Rect = rect.SDL_Rect
else:
render_copy = render.SDL_RenderCopyExF
Point = rect.SDL_FPoint
Rect = rect.SDL_FRect
w, h = (None, None)
if isinstance(src, TextureSprite):
texture = src.texture
angle = angle if angle != 0 else src.angle
center = center if center else src.center
flip = flip if flip != 0 else src.flip
w, h = src.size
elif isinstance(src, Texture):
texture = src.tx
w, h = src.size
elif isinstance(src, render.SDL_Texture):
texture = src
else:
raise TypeError("src must be a Texture object or an SDL_Texture")
if srcrect:
x, y, w, h = _sanitize_rects([srcrect])[0]
srcrect = rect.SDL_Rect(int(x), int(y), int(w), int(h))
if dstrect:
if _is_point(dstrect):
x, y = dstrect
if srcrect:
w, h = (srcrect.w, srcrect.h)
elif w is None:
w, h = _get_texture_size(texture)
elif _is_rect(dstrect):
x, y, w, h = dstrect
else:
err = "'dstrect' must be a valid point or rectangle."
raise ValueError(err)
dstrect = Rect(x, y, w, h)
if center:
x, y = _sanitize_points(center)[0]
center = Point(x, y)
ret = render_copy(
self.sdlrenderer, texture, srcrect, dstrect, angle, center, flip
)
if ret < 0:
raise_sdl_err("copying the texture to the rendering context")
def blit(self, src, srcrect=None, dstrect=None, angle=0, center=None,
flip=render.SDL_FLIP_NONE):
"""Copies a texture to the rendering context.
An alias for the :meth:`copy` method.
"""
self.copy(src, srcrect, dstrect, angle, center, flip)
def rcopy(self, src, loc, size=None, align=(0.0, 0.0), srcrect=None):
"""Copies a texture to the rendering context with relative alignment.
This method draws a texture to the rendering context using two things:
a location on the renderer surface, and a point on the texture to align
to that location. For example, you may want to draw a texture such that
its center is aligned with the middle of the screen::
screen_c = [int(n / 2) for n in renderer.logical_size]
renderer.rcopy(img, loc=screen_c, align=(0.5, 0.5))
Alignments are specified with an (x, y) tuple of values from 0.0 to 1.0,
inclusive, indicating the point on the texture to place at the given
location. ``(0, 0)`` represents the top-left corner of the texture, and
``(1, 1)`` represents the bottom right. An alignment of ``(0.5, 0.5)``
represents the midpoint of the surface.
Args:
src (:obj:`~sdl2.ext.Texture`, :obj:`~sdl2.SDL_Texture`): The source
texture to copy to the rendering surface.
loc (tuple): The (x, y) pixel coordinates at which to place the
texture on the surface.
size (tuple, optional): The scaled ``(width, height)`` output size
in pixels of the texture on the surface. If not specified, the
texture will be drawn with its original size.
align (tuple, optional): The point on the source texture to align to
the given location. Values can range from ``(0.0, 0.0)``
(top-left corner) to ``(1.0, 1.0)`` (bottom-right corner).
srcrect (tuple, optional): An ``(x, y, w, h)`` rectangle defining
the subset of the source texture to copy to the rendering
surface. Defaults to copying the entire source texture.
"""
# Do initial type checking
if not _is_point(loc):
raise ValueError("'loc' must be a valid set of (x, y) coordinates")
if size and not (isiterable(size) and len(size) == 2):
raise ValueError("'size' must be a valid set of (width, height) values")
# If using subset of surface and size not given, get size from srcrect
if srcrect and not size:
x, y, w, h = _sanitize_rects([srcrect])[0]
size = (w, h)
if isinstance(src, TextureSprite):
texture = src.texture
w, h = size if size else src.size
elif isinstance(src, Texture):
texture = src.tx
w, h = size if size else src.size
elif isinstance(src, render.SDL_Texture):
texture = src
w, h = size if size else _get_texture_size(texture)
else:
raise TypeError("src must be a Texture object or an SDL_Texture")
# Calcuate the destination rect for the given loc/size/alignment
loc_x, loc_y = loc
align_x, align_y = align
left = loc_x - int(w * align_x)
top = loc_y - int(h * align_y)
self.copy(src, srcrect, (left, top, w, h), 0, None, 0)
def present(self):
"""Presents the current rendering surface to the screen.
Because SDL renderers use batch rendering (i.e. they have a separate
backbuffer that is drawn to and buffer shown on screen which are
switched when this function is called), any drawing operations
performed with the renderer will not take effect until this method
is called.
It is recommended that you clear and redraw the contents of the
rendering context every time before this method is called, as the
contents of the buffers are not guaranteed to remain the same between
repeat presentations.
"""
render.SDL_RenderPresent(self.sdlrenderer)
def draw_line(self, points, color=None):
"""Draws one or more connected lines on the rendering context.
.. note::
Subpixel rendering (i.e. using floats as pixel coordinates) requires
SDL 2.0.10 or newer.
Args:
points (list): A list of 2 or more ``(x, y)`` coordinates or
:obj:`~sdl2.SDL_Point` objects defining the set of connected
lines to draw.
color (:obj:`~sdl2.ext.Color`, optional): The color with which to
draw the lines. If not specified, the renderer's current
:attr:`color` will be used.
"""
if dll.version < 2010:
draw_lines = render.SDL_RenderDrawLines
Point = rect.SDL_Point
else:
draw_lines = render.SDL_RenderDrawLinesF
Point = rect.SDL_FPoint
points = _sanitize_points(points)
if len(points) < 2:
raise ValueError("At least two (x, y) points are required.")
sdlpts = []
for p in points:
x, y = p
sdlpts.append(Point(x, y))
points_ptr = (Point * len(points))(*sdlpts)
if color is None:
ret = draw_lines(self.sdlrenderer, points_ptr, len(points))
else:
tmp = self.color
self.color = color
ret = draw_lines(self.sdlrenderer, points_ptr, len(points))
self.color = tmp
if ret < 0:
raise_sdl_err("drawing lines to the renderer")
def draw_point(self, points, color=None):
"""Draws one or more points on the rendering context.
.. note::
Subpixel rendering (i.e. using floats as pixel coordinates) requires
SDL 2.0.10 or newer.
Args:
points (list): A list of ``(x, y)`` coordinates or
:obj:`~sdl2.SDL_Point` objects defining the set of points to
draw.
color (:obj:`~sdl2.ext.Color`, optional): The color with which to
draw the points. If not specified, the renderer's current
:attr:`color` will be used.
"""
if dll.version < 2010:
draw_points = render.SDL_RenderDrawPoints
Point = rect.SDL_Point
else:
draw_points = render.SDL_RenderDrawPointsF
Point = rect.SDL_FPoint
points = _sanitize_points(points)
sdlpts = []
for p in points:
x, y = p
sdlpts.append(Point(x, y))
points_ptr = (Point * len(points))(*sdlpts)
if color is None:
ret = draw_points(self.sdlrenderer, points_ptr, len(points))
else:
tmp = self.color
self.color = color
ret = draw_points(self.sdlrenderer, points_ptr, len(points))
self.color = tmp
if ret < 0:
raise_sdl_err("drawing points to the renderer")
def draw_rect(self, rects, color=None):
"""Draws one or more rectangles on the rendering context.
Rectangles can be specified as 4-item ``(x, y, w, h)`` tuples,
:obj:`~sdl2.rect.SDL_Rect` objects, or a list containing multiple
rectangles in either format.
.. note::
Subpixel rendering (i.e. using floats as pixel coordinates) requires
SDL 2.0.10 or newer.
Args:
rects (tuple, :obj:`~sdl2.SDL_Rect`, list): The rectangle(s) to draw
to the rendering context.
color (:obj:`~sdl2.ext.Color`, optional): The color with which to
draw the rectangle(s). If not specified, the renderer's current
:attr:`color` will be used.
"""
if dll.version < 2010:
draw_rects = render.SDL_RenderDrawRects
Rect = rect.SDL_Rect
else:
draw_rects = render.SDL_RenderDrawRectsF
Rect = rect.SDL_FRect
rects = _sanitize_rects(rects)
sdlrects = []
for r in rects:
x, y, w, h = r
sdlrects.append(Rect(x, y, w, h))
rects_ptr = (Rect * len(rects))(*sdlrects)
if color is None:
ret = draw_rects(self.sdlrenderer, rects_ptr, len(rects))
else:
tmp = self.color
self.color = color
ret = draw_rects(self.sdlrenderer, rects_ptr, len(rects))
self.color = tmp
if ret < 0:
raise_sdl_err("drawing rectangles to the renderer")
def fill(self, rects, color=None):
"""Fills one or more rectangular regions the rendering context.
Fill regions can be specified as 4-item ``(x, y, w, h)`` tuples,
:obj:`~sdl2.rect.SDL_Rect` objects, or a list containing multiple
rectangles in either format.
.. note::
Subpixel rendering (i.e. using floats as pixel coordinates) requires
SDL 2.0.10 or newer.
Args:
rects (tuple, :obj:`~sdl2.SDL_Rect`, list): The rectangle(s) to fill
within the rendering context.
color (:obj:`~sdl2.ext.Color`, optional): The color with which to
fill the rectangle(s). If not specified, the renderer's current
:attr:`color` will be used.
"""
if dll.version < 2010:
fill_rects = render.SDL_RenderFillRects
Rect = rect.SDL_Rect
else:
fill_rects = render.SDL_RenderFillRectsF
Rect = rect.SDL_FRect
rects = _sanitize_rects(rects)
sdlrects = []
for r in rects:
x, y, w, h = r
sdlrects.append(Rect(x, y, w, h))
rects_ptr = (Rect * len(rects))(*sdlrects)
if color is None:
ret = fill_rects(self.sdlrenderer, rects_ptr, len(rects))
else:
tmp = self.color
self.color = color
ret = fill_rects(self.sdlrenderer, rects_ptr, len(rects))
self.color = tmp
if ret < 0:
raise_sdl_err("filling rectangles in the renderer")
|