Skip to content

Commit ba1adee

Browse files
authored
Rewrite and re-document the sdl2.ext.renderer API (#207)
* Fully rewrite and document the ext.renderer module * Update examples to use new rendering API * Finish documenting the Texture class
1 parent df2f920 commit ba1adee

File tree

6 files changed

+945
-383
lines changed

6 files changed

+945
-383
lines changed

doc/modules/ext/renderer.rst

Lines changed: 8 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,10 @@
1-
.. currentmodule:: sdl2.ext
1+
`sdl2.ext.renderer` - Accelerated 2D Rendering
2+
==============================================
23

3-
Accelerated 2D Rendering
4-
========================
4+
The :mod:`sdl2.ext.renderer` module implements a Pythonic interface for working
5+
with the SDL Renderer API, which allows for easy hardware-accelerated 2D
6+
rendering with backends for a number of platforms (e.g. OpenGL, Direct3D, Metal)
7+
as well as a software-accelerated fallback.
58

6-
.. class:: Renderer(target : obj[, logical_size=None[, index=-1[, flags=sdl2.SDL_RENDERER_ACCELERATED]])
7-
8-
A rendering context for windows and sprites that can use hardware or
9-
software-accelerated graphics drivers.
10-
11-
If target is a :class:`sdl2.ext.Window` or :class:`sdl2.SDL_Window`,
12-
*index* and *flags* are passed to the relevant
13-
:class:`sdl2.SDL_CreateRenderer()` call. If *target* is a
14-
:class:`SoftwareSprite` or :class:`sdl2.SDL_Surface`, the *index*
15-
and *flags* arguments are ignored.
16-
17-
.. attribute:: sdlrenderer
18-
19-
The underlying :class:`sdl2.SDL_Renderer`.
20-
21-
.. attribute:: rendertarget
22-
23-
The target for which the :class:`Renderer` was created.
24-
25-
.. attribute:: logical_size
26-
27-
The logical size of the renderer.
28-
29-
Setting this allows you to draw as if your renderer had this size, even
30-
though the target may be larger or smaller. When drawing, the renderer will
31-
automatically scale your contents to the target, creating letter-boxing or
32-
sidebars if necessary.
33-
34-
To reset your logical size back to the target's, set it to ``(0, 0)``.
35-
36-
Setting this to a lower value may be useful for low-resolution effects.
37-
38-
Setting this to a larger value may be useful for antialiasing.
39-
40-
.. attribute:: color
41-
42-
The :class:`sdl2.ext.Color` to use for draw and fill operations.
43-
44-
.. attribute:: blendmode
45-
46-
The blend mode used for drawing operations (fill and line). This
47-
can be a value of
48-
49-
* ``SDL_BLENDMODE_NONE`` for no blending
50-
* ``SDL_BLENDMODE_BLEND`` for alpha blending
51-
* ``SDL_BLENDMODE_ADD`` for additive color blending
52-
* ``SDL_BLENDMODE_MOD`` for multiplied color blending
53-
54-
.. attribute:: scale
55-
56-
The horizontal and vertical drawing scale as two-value tuple.
57-
58-
.. method:: clear([color=None])
59-
60-
Clears the rendering context with the currently set or passed
61-
*color*.
62-
63-
.. method:: copy(src : obj[, srcrect=None[, dstrect=None[, angle=0[, center=None[, flip=render.SDL_FLIP_NONE]]]]]) -> None
64-
65-
Copies (blits) the passed *src*, which can be a :class:`TextureSprite` or
66-
:class:`sdl2.SDL_Texture`, to the target of the
67-
:class:`Renderer`. *srcrect* is the source rectangle to be used for
68-
clipping portions of *src*. *dstrect* is the destination rectangle.
69-
*angle* will cause the texture to be rotated around *center* by the given
70-
degrees. *flip* can be one of the SDL_FLIP_* constants and will flip the
71-
texture over its horizontal or vertical middle axis. If *src* is a
72-
:class:`TextureSprite`, *angle*, *center* and *flip* will be set from
73-
*src*'s attributes, if not provided.
74-
75-
.. method:: draw_line(points : iterable[, color=None]) -> None
76-
77-
Draws one or multiple lines on the rendering context. If *line* consists
78-
of four values ``(x1, y1, x2, y2)`` only, a single line is drawn. If
79-
*line* contains more than four values, a series of connected lines is
80-
drawn.
81-
82-
.. method:: draw_point(points : iterable[, color=None]) -> None
83-
84-
Draws one or multiple points on the rendering context. The *points*
85-
argument contains the x and y values of the points as simple sequence in
86-
the form ``(point1_x, point1_y, point2_x, point2_y, ...)``.
87-
88-
.. method:: draw_rect(rects : iterable[, color=None]) -> None
89-
90-
Draws one or multiple rectangles on the rendering context. *rects*
91-
contains sequences of four values denoting the x and y offset and width
92-
and height of each individual rectangle in the form ``((x1, y1, w1, h1),
93-
(x2, y2, w2, h2), ...)``.
94-
95-
.. method:: fill(rects : iterable[, color=None]) -> None
96-
97-
Fills one or multiple rectangular areas on the rendering context with
98-
the current set or passed *color*. *rects* contains sequences of four
99-
values denoting the x and y offset and width and height of each
100-
individual rectangle in the form ``((x1, y1, w1, h1), (x2, y2, w2, h2),
101-
...)``.
102-
103-
.. method:: present() -> None
104-
105-
Refreshes the rendering context, causing changes to the render buffers
106-
to be shown.
9+
.. automodule:: sdl2.ext.renderer
10+
:members:

doc/news.rst

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,21 @@ New Features:
4242
* Added a new function :func:`sdl2.ext.pillow_to_surface` for converting
4343
:obj:`PIL.Image.Image` objects from the Pillow library to SDL
4444
surfaces (PR #205)
45+
* Added a new class :class:`sdl2.ext.Texture` for creating renderer textures
46+
from SDL surfaces, as a basic wrapper for the :obj:`sdl2.SDL_Texture`
47+
structure (PR #207)
48+
* Added a new function :func:`sdl2.ext.set_texture_scale_quality` that globally
49+
sets the scaling method (nearest-neighbour, linear filtering, or anisotropic
50+
filtering) to use for new SDL textures (PR #207)
51+
* Added a new method :meth:`sdl2.ext.Renderer.reset_logical_size` to reset a
52+
Renderer's logical size to its original value (PR #207)
53+
* Added a new method :meth:`sdl2.ext.Renderer.destroy` to safely destroy and
54+
free memory associated with a Renderer after it is no longer needed (PR #207)
55+
* Added support for subpixel precision (i.e. using float coordinates)
56+
with the drawing and copying methods of the :class:`~sdl2.ext.Renderer` class
57+
when using SDL2 2.0.10 or newer (PR #207)
58+
* Added :meth:`sdl2.ext.Renderer.blit` as an alias for the
59+
:meth:`sdl2.ext.Renderer.copy` method (PR #207)
4560

4661
Fixed Bugs:
4762

@@ -70,6 +85,23 @@ API Changes:
7085
directly or as a pointer (PR #204)
7186
* The :func:`sdl2.ext.pixels2d` and :func:`sdl2.ext.pixels3d` functions no
7287
longer raise an ``ExperimentalWarning`` (PR #204)
88+
* Updated the :meth:`~sdl2.ext.Renderer.draw_line` and
89+
:meth:`~sdl2.ext.Renderer.draw_point` methods of the
90+
:class:`~sdl2.ext.Renderer` class to accept coordinates as lists of ``(x, y)``
91+
tuples or :obj:`~sdl2.SDL_Point`s in addition to flat ``[x, y, x, y, x, y]``
92+
lists (PR #207)
93+
* Updated the :meth:`~sdl2.ext.Renderer.draw_rect` and
94+
:meth:`~sdl2.ext.Renderer.fill` methods of the
95+
:class:`~sdl2.ext.Renderer` class to accept coordinates as lists of
96+
:obj:`~sdl2.SDL_Rects`s in addition to lists of ``(x, y, w, h)``
97+
tuples (PR #207)
98+
* Updated the :meth:`~sdl2.ext.Renderer.copy` method of the
99+
:class:`~sdl2.ext.Renderer` class to accept an ``(x, y)`` tuple as a
100+
destination, inferring the destination width and height from the dimensions
101+
of the copied texture (PR #207)
102+
* Changed the ``index`` argument for the :class:`~sdl2.ext.Renderer` class to
103+
take the name of the reqested rendering back end as a string instead of an
104+
index for better clarity and cross-platform consistency (PR #207)
73105

74106
Deprecation Notices:
75107

examples/helloworld.py

Lines changed: 32 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -29,50 +29,43 @@ def run():
2929
# after creation. Thus we need to tell it to be shown now.
3030
window.show()
3131

32-
# Create a sprite factory that allows us to create visible 2D elements
33-
# easily. Depending on what the user chosses, we either create a factory
34-
# that supports hardware-accelerated sprites or software-based ones.
35-
# The hardware-accelerated SpriteFactory requres a rendering context
36-
# (or SDL_Renderer), which will create the underlying textures for us.
32+
# Create a Renderer for the new window, which we can use to copy and
33+
# draw things to the screen. Renderers can use hardware-accelerated
34+
# backends (e.g. OpenGL, Direct3D) as well as software-accelerated ones,
35+
# depending on the flags you create it with.
36+
renderflags = sdl2.SDL_RENDERER_SOFTWARE
3737
if "-hardware" in sys.argv:
38-
print("Using hardware acceleration")
39-
renderer = sdl2.ext.Renderer(window)
40-
factory = sdl2.ext.SpriteFactory(sdl2.ext.TEXTURE, renderer=renderer)
41-
else:
42-
print("Using software rendering")
43-
factory = sdl2.ext.SpriteFactory(sdl2.ext.SOFTWARE)
38+
renderflags = (
39+
sdl2.SDL_RENDERER_ACCELERATED | sdl2.SDL_RENDERER_PRESENTVSYNC
40+
)
41+
renderer = sdl2.ext.Renderer(window, flags=renderflags)
4442

45-
# Creates a simple rendering system for the Window. The
46-
# SpriteRenderSystem can draw Sprite objects on the window.
47-
spriterenderer = factory.create_sprite_render_system(window)
43+
# Import an image file and convert it to a Texture. A Texture is an SDL
44+
# surface that has been prepared for use with a given Renderer.
45+
tst_img = sdl2.ext.load_bmp(RESOURCES.get_path("hello.bmp"))
46+
tx = sdl2.ext.Texture(renderer, tst_img)
4847

49-
# Creates a new 2D pixel-based surface to be displayed, processed or
50-
# manipulated. We will use the one of the shipped example images
51-
# from the resource package to display.
52-
sprite = factory.from_image(RESOURCES.get_path("hello.bmp"))
48+
# Display the image on the window. This code takes the texture we created
49+
# earlier and copies it to the renderer (with the top-left corner of the
50+
# texture placed at the coordinates (0, 0) on the window surface), then
51+
# takes the contents of the renderer surface and presents them on its
52+
# associated window.
53+
renderer.copy(tx, dstrect=(0, 0))
54+
renderer.present()
5355

54-
# Display the surface on the window. This will copy the contents
55-
# (pixels) of the surface to the window. The surface will be
56-
# displayed at surface.position on the window. Play around with the
57-
# surface.x and surface.y values or surface.position (which is just
58-
# surface.x and surface.y grouped as tuple)!
59-
spriterenderer.render(sprite)
56+
# Create a simple event loop. This fetches the SDL2 event queue and checks
57+
# for any quit events. Once a quit event is received, the loop will end
58+
# and we'll send the signal to quit the program.
59+
running = True
60+
while running:
61+
events = sdl2.ext.get_events()
62+
for event in events:
63+
if event.type == sdl2.SDL_QUIT:
64+
running = False
65+
break
6066

61-
# Set up an example event loop processing system. This is a necessity,
62-
# so the application can exit correctly, mouse movements, etc. are
63-
# recognised and so on. The TestEventProcessor class is just for
64-
# testing purposes and does not do anything meaningful. Take a look
65-
# at its code to better understand how the event processing can be
66-
# done and customized!
67-
processor = sdl2.ext.TestEventProcessor()
68-
69-
# Start the event processing. This will run in an endless loop, so
70-
# everything following after processor.run() will not be executed
71-
# before some quitting event is raised.
72-
processor.run(window)
73-
74-
# We called video.init(), so we have to call video.quit() as well to
75-
# release the resources hold by the SDL DLL.
67+
# Now that we're done with the SDL2 library, we shut it down nicely using
68+
# the `sdl2.ext.quit` function.
7669
sdl2.ext.quit()
7770
return 0
7871

examples/transfomations.py

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,18 @@ def run():
1414
window = sdl2.ext.Window("Sprite Transformations", size=(800, 600))
1515
window.show()
1616

17-
# Create a hardware-accelerated sprite factory. The sprite factory requires
18-
# a rendering context, which enables it to create the underlying textures
19-
# that serve as the visual parts for the sprites.
17+
# Create a hardware-accelerated renderer for drawing to the new window.
18+
# We'll also set the default scaling quality to 'best' for smoother
19+
# edges when rendering.
2020
renderer = sdl2.ext.Renderer(window)
21-
factory = sdl2.ext.SpriteFactory(sdl2.ext.TEXTURE, renderer=renderer)
22-
23-
# Create a simple rendering system for the window. We will use it to
24-
# display the sprites.
25-
rendersystem = factory.create_sprite_render_system(window)
26-
27-
# Create the sprite to display.
28-
sprite = factory.from_image(RESOURCES.get_path("hello.bmp"))
29-
30-
# Use the sprite.center tuple to change the center of the sprite for
31-
# rotation. You can reset a changed center simply by assinging None to it.
32-
#
33-
# sprite.center = 10, 30 # Changes the center
34-
# sprite.center = None # Resets the center
21+
sdl2.ext.set_texture_scale_quality(method="best")
3522

23+
# Import an image file and convert it to a Texture for the renderer
24+
tst_img = sdl2.ext.load_bmp(RESOURCES.get_path("hello.bmp"))
25+
tx = sdl2.ext.Texture(renderer, tst_img)
26+
27+
angle = 0
28+
flip = 0
3629
running = True
3730
while running:
3831
events = sdl2.ext.get_events()
@@ -41,26 +34,30 @@ def run():
4134
running = False
4235
break
4336
elif event.type == sdl2.SDL_KEYDOWN:
44-
# Flip the sprite over its vertical axis on pressing down or up
37+
# Flip the sprite vertically using the up/down arrow keys
4538
if event.key.keysym.sym in (sdl2.SDLK_DOWN, sdl2.SDLK_UP):
46-
sprite.flip ^= sdl2.SDL_FLIP_VERTICAL
47-
# Flip the sprite over its horizontal axis on pressing left or
48-
# right
39+
flip ^= sdl2.SDL_FLIP_VERTICAL
40+
# Flip the sprite horizontally using the left/right arrow keys
4941
elif event.key.keysym.sym in (sdl2.SDLK_LEFT, sdl2.SDLK_RIGHT):
50-
sprite.flip ^= sdl2.SDL_FLIP_HORIZONTAL
51-
# Rotate the sprite around its center on pressing plus or
52-
# minus. The center can be changed via sprite.center.
53-
elif event.key.keysym.sym == sdl2.SDLK_PLUS:
54-
sprite.angle += 1.0
55-
if sprite.angle >= 360.0:
56-
sprite.angle = 0.0
42+
flip ^= sdl2.SDL_FLIP_HORIZONTAL
43+
# Rotate the sprite around its center using the (+/-) keys
44+
elif event.key.keysym.sym == sdl2.SDLK_EQUALS:
45+
angle += 1.0
46+
if angle >= 360.0:
47+
angle = 0.0
5748
elif event.key.keysym.sym == sdl2.SDLK_MINUS:
58-
sprite.angle -= 1.0
59-
if sprite.angle <= 0.0:
60-
sprite.angle = 360.0
49+
angle -= 1.0
50+
if angle <= 0.0:
51+
angle = 360.0
52+
53+
# Clear the renderer, copy our texture to it at an offset of (100, 75)
54+
# from the top-left corner, present it to the window, and wait 10 ms
55+
# before moving on
6156
renderer.clear()
62-
rendersystem.render(sprite, 100, 75)
57+
renderer.copy(tx, dstrect=(100, 75), angle=angle, flip=flip)
58+
renderer.present()
6359
sdl2.SDL_Delay(10)
60+
6461
sdl2.ext.quit()
6562
return 0
6663

0 commit comments

Comments
 (0)