Skip to content

Commit 2d32fc5

Browse files
committed
Sprites/Groups can now support Renderers/Textures in a limited capacity
1 parent 787650b commit 2d32fc5

File tree

6 files changed

+70
-13
lines changed

6 files changed

+70
-13
lines changed

buildconfig/stubs/pygame/sprite.pyi

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ from typing import (
99
SupportsFloat,
1010
TypeVar,
1111
Union,
12+
overload,
1213
)
1314

1415
# use typing_extensions for compatibility with older Python versions
@@ -22,6 +23,7 @@ if sys.version_info >= (3, 11):
2223
else:
2324
from typing_extensions import Self
2425

26+
from pygame._sdl2.video import Renderer, Texture
2527
from pygame.mask import Mask
2628
from pygame.rect import FRect, Rect
2729
from pygame.surface import Surface
@@ -37,7 +39,7 @@ class _HasRect(Protocol):
3739
# image in addition to rect
3840
class _HasImageAndRect(_HasRect, Protocol):
3941
@property
40-
def image(self) -> Optional[Surface]: ...
42+
def image(self) -> Optional[Union[Surface, Texture]]: ...
4143

4244
# mask in addition to rect
4345
class _HasMaskAndRect(_HasRect, Protocol):
@@ -54,9 +56,9 @@ _Group = AbstractGroup[Any]
5456
# and allows the use of any class with the required attributes and methods
5557
class _SupportsSprite(_HasImageAndRect, Protocol):
5658
@property
57-
def image(self) -> Optional[Surface]: ...
59+
def image(self) -> Optional[Union[Surface, Texture]]: ...
5860
@image.setter
59-
def image(self, value: Optional[Surface]) -> None: ...
61+
def image(self, value: Optional[Union[Surface, Texture]]) -> None: ...
6062
@property
6163
def rect(self) -> Optional[Union[FRect, Rect]]: ...
6264
@rect.setter
@@ -87,9 +89,9 @@ class _SupportsDirtySprite(_SupportsSprite, Protocol):
8789
# concrete sprite implementation class
8890
class Sprite(_SupportsSprite):
8991
@property
90-
def image(self) -> Optional[Surface]: ...
92+
def image(self) -> Optional[Union[Surface, Texture]]: ...
9193
@image.setter
92-
def image(self, value: Optional[Surface]) -> None: ...
94+
def image(self, value: Optional[Union[Surface, Texture]]) -> None: ...
9395
@property
9496
def rect(self) -> Optional[Union[FRect, Rect]]: ...
9597
@rect.setter
@@ -146,7 +148,10 @@ class AbstractGroup(Generic[_TSprite]):
146148
def has(self, *sprites: _SpriteOrIterable[_TSprite]) -> bool: ...
147149
def update(self, *args: Any, **kwargs: Any) -> None: ...
148150
def draw(
149-
self, surface: Surface, bgd: Optional[Surface] = None, special_flags: int = 0
151+
self,
152+
surface: Union[Surface, Renderer],
153+
bgd: Optional[Surface] = None,
154+
special_flags: int = 0,
150155
) -> list[Union[FRect, Rect]]: ...
151156
def clear(
152157
self,

docs/reST/ref/sprite.rst

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ extend those when you add a Sprite or Group class.
5050

5151
Sprites are not thread safe. So lock them yourself if using threads.
5252

53+
.. note:: As of version 2.5.6, you can use ``Sprite`` and ``Group`` with ``Renderer``
54+
and ``Texture`` from ``_sdl2.video``, at least partially. Instead of ``Sprite.image``
55+
needing to be a ``Surface``, it needs to be a ``Texture`` to do this. This behavior
56+
is still very experimental, so please report any weirdness to the developers as an
57+
issue on the ``pygame-ce`` github repository. Also note that ``LayeredDirty.draw``
58+
does not currently work in this manner. Other group types might work, but have not
59+
been thorougly tested.
60+
61+
.. versionchanged:: 2.5.6 ``Sprite`` and ``Group``
62+
have some compatibility with ``_sdl2.video``
63+
5364
.. class:: Sprite
5465

5566
| :sl:`Simple base class for visible game objects.`
@@ -291,17 +302,29 @@ Sprites are not thread safe. So lock them yourself if using threads.
291302
.. method:: draw
292303

293304
| :sl:`blit the Sprite images`
294-
| :sg:`draw(Surface, bgd=None, special_flags=0) -> list[Rect]`
305+
| :sg:`draw(Surface, bgd=None, special_flags=0) -> list[Union[Rect, FRect]]`
306+
| :sg:`draw(Renderer, bgd=None, special_flags=0) -> list[Union[Rect, FRect]]`
307+
308+
Draws the contained Sprites to the Surface or _sdl2.video.Renderer argument.
309+
310+
Surface
311+
-------
312+
This uses the ``Sprite.image`` attribute for the source surface, and ``Sprite.rect``
313+
for the position.
314+
315+
Renderer
316+
--------
317+
This uses the ``Sprite.image`` attribute for the source Texture, and ``Sprite.rect``
318+
for the position.
295319

296-
Draws the contained Sprites to the Surface argument. This uses the
297-
``Sprite.image`` attribute for the source surface, and ``Sprite.rect``
298-
for the position. ``special_flags`` is passed to ``Surface.blit()``.
320+
``special_flags`` is passed to ``Surface.blit()``.
299321
``bgd`` is unused in this method but ``LayeredDirty.draw()`` uses
300322
it.
301323

302324
The Group keeps sprites in the order they were added, they will be drawn in this order.
303325

304326
.. versionchanged:: 2.5.4 Added the ``bgd`` and ``special_flags`` arguments
327+
.. versionchanged:: 2.5.6 Now accepts a renderer for use with ``_sdl2.video`` and ``Sprite.rect`` supports ``FRect``s
305328

306329
.. ## Group.draw ##
307330

src_c/cython/pygame/_sdl2/video.pxd

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,14 @@ cdef extern from "pygame.h" nogil:
427427
cdef SDL_Rect r
428428
cdef object weakreflist
429429

430+
ctypedef class pygame.rect.FRect [object pgFRectObject]:
431+
cdef SDL_FRect r
432+
cdef object weakreflist
433+
434+
ctypedef fused RectLike:
435+
Rect
436+
FRect
437+
430438
ctypedef class pygame.window.Window [object pgWindowObject]:
431439
cdef SDL_Window *_win
432440
cdef SDL_bool _is_borrowed
@@ -465,7 +473,7 @@ cdef class Renderer:
465473
cdef int _is_borrowed
466474

467475
cpdef object get_viewport(self)
468-
cpdef object blit(self, object source, Rect dest=*, Rect area=*, int special_flags=*)
476+
cpdef object blit(self, object source, RectLike dest=*, RectLike area=*, int special_flags=*)
469477

470478
cdef class Texture:
471479
cdef SDL_Texture* _tex

src_c/cython/pygame/_sdl2/video.pyx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -998,7 +998,7 @@ cdef class Renderer:
998998
else:
999999
raise TypeError('target must be a Texture or None')
10001000

1001-
cpdef object blit(self, object source, Rect dest=None, Rect area=None, int special_flags=0):
1001+
cpdef object blit(self, object source, RectLike dest=None, RectLike area=None, int special_flags=0):
10021002
"""Draw textures using a Surface-like API
10031003
10041004
For compatibility purposes. Draws :class:`Texture` objects onto the

src_c/doc/sprite_doc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#define DOC_SPRITE_GROUP_REMOVE "remove(*sprites) -> None\nremove Sprites from the Group"
1616
#define DOC_SPRITE_GROUP_HAS "has(*sprites) -> bool\ntest if a Group contains Sprites"
1717
#define DOC_SPRITE_GROUP_UPDATE "update(*args, **kwargs) -> None\ncall the update method on contained Sprites"
18-
#define DOC_SPRITE_GROUP_DRAW "draw(Surface, bgd=None, special_flags=0) -> list[Rect]\nblit the Sprite images"
18+
#define DOC_SPRITE_GROUP_DRAW "draw(Surface, bgd=None, special_flags=0) -> list[Union[Rect, FRect]]\ndraw(Renderer, bgd=None, special_flags=0) -> list[Union[Rect, FRect]]\nblit the Sprite images"
1919
#define DOC_SPRITE_GROUP_CLEAR "clear(Surface_dest, background) -> None\ndraw a background over the Sprites"
2020
#define DOC_SPRITE_GROUP_EMPTY "empty() -> None\nremove all Sprites"
2121
#define DOC_SPRITE_RENDERUPDATES "RenderUpdates(*sprites) -> RenderUpdates\nGroup sub-class that tracks dirty updates."

test/sprite_test.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import pygame
99
from pygame import sprite
10+
from pygame._sdl2.video import Renderer, Texture
1011

1112
################################# MODULE LEVEL #################################
1213

@@ -585,6 +586,26 @@ def test_draw(self):
585586
self.assertEqual(self.ag.spritedict[self.s1], pygame.Rect(0, 0, 10, 10))
586587
self.assertEqual(self.ag.spritedict[self.s2], pygame.Rect(10, 0, 10, 10))
587588

589+
def test_draw_sdl2(self):
590+
reinit_display = pygame.display.get_init()
591+
592+
pygame.display.quit()
593+
win = pygame.Window()
594+
group = sprite.Group()
595+
renderer = Renderer(win)
596+
gpusprite = sprite.Sprite(group)
597+
598+
tempsurf = pygame.Surface((50, 50))
599+
gpusprite.image = Texture.from_surface(renderer, tempsurf)
600+
gpusprite.rect = gpusprite.image.get_rect()
601+
602+
group.draw(renderer)
603+
self.assertEqual(group.spritedict[gpusprite], tempsurf.get_rect())
604+
605+
win.destroy()
606+
if reinit_display:
607+
pygame.display.init()
608+
588609
def test_empty(self):
589610
self.ag.empty()
590611
self.assertFalse(self.s1 in self.ag)

0 commit comments

Comments
 (0)