Skip to content

Commit

Permalink
Add rcopy method to Renderer
Browse files Browse the repository at this point in the history
  • Loading branch information
a-hurst committed Jun 27, 2023
1 parent a541949 commit 4a1da6d
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 1 deletion.
3 changes: 3 additions & 0 deletions doc/news.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ This describes the latest changes between the PySDL2 releases.
New Features:

* Updated to wrap new functions and constants in SDL2 2.28.0 (PR #266).
* Added a new method :meth:`~sdl2.ext.Renderer.rcopy` for copying textures to
a Renderer by aligning a given point on the texture to a given location on the
rendering context. Useful for centering textures in the renderer.
* Added a new function :func:`~sdl2.ext.key_pressed` for easily checking
if a given key has been pressed (or released).
* Added a new function :func:`~sdl2.ext.mouse_clicked` for easily checking
Expand Down
69 changes: 68 additions & 1 deletion sdl2/ext/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -596,13 +596,16 @@ def copy(self, src, srcrect=None, dstrect=None, angle=0, center=None,
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:
Expand All @@ -617,7 +620,7 @@ def copy(self, src, srcrect=None, dstrect=None, angle=0, center=None,
x, y = dstrect
if srcrect:
w, h = (srcrect.w, srcrect.h)
else:
elif w is None:
w, h = _get_texture_size(texture)
elif _is_rect(dstrect):
x, y, w, h = dstrect
Expand Down Expand Up @@ -645,6 +648,70 @@ def blit(self, src, srcrect=None, dstrect=None, angle=0, center=None,
"""
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.
Expand Down
57 changes: 57 additions & 0 deletions sdl2/test/sdl2ext_renderer_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,63 @@ def test_copy(self, with_sdl):

del view

def test_rcopy(self, with_sdl):
# Initialize target surface and renderer
surface = SDL_CreateRGBSurface(0, 128, 128, 32, 0, 0, 0, 0).contents
renderer = sdl2ext.Renderer(surface)
renderer.clear(0xAABBCC)
view = sdl2ext.PixelView(surface)

# Test copying a Texture with only location argument
sf = SDL_CreateRGBSurface(0, 16, 16, 32, 0, 0, 0, 0)
sdl2ext.fill(sf, (0, 0, 0, 0))
tx = sdl2ext.Texture(renderer, sf)
renderer.rcopy(tx, loc=(10, 10))
renderer.present()
assert view[0][0] == 0xAABBCC
assert view[10][10] == 0
assert view[25][25] == 0
assert view[26][26] == 0xAABBCC

# Test copying a Texture with location and size
renderer.clear(0xAABBCC) # reset surface
renderer.rcopy(tx, loc=(10, 10), size=(30, 40))
renderer.present()
assert view[0][0] == 0xAABBCC
assert view[10][10] == 0
assert view[49][39] == 0
assert view[50][40] == 0xAABBCC

# Test copying a Texture with center alignment
renderer.clear(0xAABBCC) # reset surface
renderer.rcopy(tx, loc=(16, 16), align=(0.5, 0.5))
renderer.present()
assert view[7][7] == 0xAABBCC
assert view[8][8] == 0
assert view[23][23] == 0
assert view[24][24] == 0xAABBCC

# Test copying a Texture with mid-left alignment and scaling
renderer.clear(0xAABBCC) # reset surface
renderer.rcopy(tx, loc=(16, 16), align=(0, 0.5), size=(8, 8))
renderer.present()
assert view[15][11] == 0xAABBCC
assert view[12][16] == 0
assert view[19][23] == 0
assert view[20][24] == 0xAABBCC

# Test copying a Texture subset with mid-left alignment and scaling
renderer.clear(0xAABBCC) # reset surface
tx_subset = SDL_Rect(0, 0, 8, 8)
renderer.rcopy(tx, loc=(16, 16), align=(0, 0.5), srcrect=tx_subset)
renderer.present()
assert view[15][11] == 0xAABBCC
assert view[12][16] == 0
assert view[19][23] == 0
assert view[20][24] == 0xAABBCC

del view

def test_draw_line(self, with_sdl):
surface = SDL_CreateRGBSurface(0, 128, 128, 32, 0, 0, 0, 0).contents
sdl2ext.fill(surface, 0x0)
Expand Down

0 comments on commit 4a1da6d

Please sign in to comment.