Skip to content

Commit d6ed697

Browse files
authored
Add Renderer.logical_to_window and Renderer.window_to_logical from SDL2 (#3519)
* Add logical_to_window and window_to_logical from SDL2 See https://wiki.libsdl.org/SDL2/SDL_RenderLogicalToWindow and https://wiki.libsdl.org/SDL2/SDL_RenderWindowToLogical When setting renderer.logical_size, these two functions offer translation between window space and logical space. * Stubs added, return type of window_to_logical fixed * Fix devcheck by running `dev.py format` * Fix MS build that as warnings -> errors * Try to fix stubs... * tuple instead of *args, SDL3 migration guide follow Both functions now expect a tuple, following the pygame conventions. For SDL3, the ported functions have been renamed and changed. > SDL_RenderWindowToLogical() and SDL_RenderLogicalToWindow() have been > renamed SDL_RenderCoordinatesFromWindow() and > SDL_RenderCoordinatesToWindow() and take floating point coordinates in > both directions. I settled on the following decisions * Regardless of the difference in the windows coordinates being ints in the old, and floats in the new version, I just aliased the new names to the old functions on the python side. * Even on SDL3, the window coordinates will be truncated to int to guarantee future compatibility with old code. * Accept and return floats everywhere, sdl3 aliases removed * double, not float! * Wording of error messages follows review suggestion * modified test according to review * dev.py format * logical_to_window and window_to_logical for _sdl2.video * versionadded added * Moved to SDL3 function names as discussed
1 parent 8c394d2 commit d6ed697

File tree

9 files changed

+152
-0
lines changed

9 files changed

+152
-0
lines changed

buildconfig/stubs/pygame/_render.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ class Renderer:
4040
def fill_rect(self, rect: RectLike) -> None: ...
4141
def fill_triangle(self, p1: Point, p2: Point, p3: Point) -> None: ...
4242
def get_viewport(self) -> Rect: ...
43+
def coordinates_to_window(self, point: Point) -> tuple[float, float]: ...
44+
def coordinates_from_window(self, point: Point) -> tuple[float, float]: ...
4345
def present(self) -> None: ...
4446
def set_viewport(self, area: Optional[RectLike]) -> None: ...
4547
def to_surface(

buildconfig/stubs/pygame/_sdl2/video.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ class Renderer:
146146
def get_viewport(self) -> Rect: ...
147147
def set_viewport(self, area: Optional[RectLike]) -> None: ...
148148
logical_size: Iterable[int]
149+
def coordinates_to_window(self, point: Point) -> tuple[float, float]: ...
150+
def coordinates_from_window(self, point: Point) -> tuple[float, float]: ...
149151
scale: Iterable[float]
150152
target: Optional[Texture]
151153
def blit(

docs/reST/ref/sdl2_video.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,25 @@
428428
drawing area on the target, or ``None`` to use the
429429
entire area of the current rendering target.
430430

431+
.. method:: coordinates_to_window
432+
433+
| :sl:`Translates renderer coordinates to window coordinates`
434+
| :sg:`coordinates_to_window(point) -> (float, float)`
435+
436+
:param point: The coordinates in render space.
437+
438+
.. versionadded:: 2.5.6
439+
440+
441+
.. method:: coordinates_from_window
442+
443+
| :sl:`Translates window coordinates to renderer coordinates`
444+
| :sg:`coordinates_from_window(point) -> (float, float)`
445+
446+
:param point: The coordinates in window space.
447+
448+
.. versionadded:: 2.5.6
449+
431450
.. method:: blit
432451

433452
| :sl:`Draw textures using a Surface-like API`

src_c/cython/pygame/_sdl2/video.pxd

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,8 @@ cdef extern from "SDL.h" nogil:
369369
# https://wiki.libsdl.org/SDL_RenderSetLogicalSize
370370
# https://wiki.libsdl.org/SDL_RenderGetLogicalSize
371371
# https://wiki.libsdl.org/SDL_RenderGetIntegerScale
372+
# https://wiki.libsdl.org/SDL2/SDL_RenderLogicalToWindow
373+
# https://wiki.libsdl.org/SDL2/SDL_RenderWindowToLogical
372374
int SDL_RenderSetScale(SDL_Renderer* renderer,
373375
float scaleX,
374376
float scaleY)
@@ -382,6 +384,20 @@ cdef extern from "SDL.h" nogil:
382384
int* w,
383385
int* h)
384386
int SDL_RenderGetIntegerScale(SDL_Renderer* renderer)
387+
# Note: Must be changed to SDL_RenderCoordinatesToWindow for SDL3
388+
# https://wiki.libsdl.org/SDL3/SDL_RenderCoordinatesToWindow
389+
void SDL_RenderLogicalToWindow(SDL_Renderer* renderer,
390+
float lx,
391+
float ly,
392+
int *wx,
393+
int *wy);
394+
# Note: Must be changed to SDL_RenderCoordinatesFromWindow for SDL3
395+
# https://wiki.libsdl.org/SDL3/SDL_RenderCoordinatesFromWindow
396+
void SDL_RenderWindowToLogical(SDL_Renderer* renderer,
397+
int wx,
398+
int wy,
399+
float *lx,
400+
float *ly);
385401

386402
int SDL_VERSION_ATLEAST(int major, int minor, int patch)
387403

src_c/cython/pygame/_sdl2/video.pyx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,6 +1037,35 @@ cdef class Renderer:
10371037
if res < 0:
10381038
raise error()
10391039

1040+
def coordinates_to_window(self, point):
1041+
"""Translates renderer coordinates to window coordinates
1042+
1043+
:param point: The coordinates in render space.
1044+
"""
1045+
cdef int wx
1046+
cdef int wy
1047+
1048+
# Note: Must be changed to SDL_RenderCoordinatesToWindow for SDL3
1049+
# https://wiki.libsdl.org/SDL3/SDL_RenderCoordinatesToWindow
1050+
SDL_RenderLogicalToWindow(self._renderer, point[0], point[1], &wx, &wy);
1051+
1052+
# Return float for future compatibility with SDL3's RenderCoordinatesToWindow
1053+
return (float(wx), float(wy))
1054+
1055+
def coordinates_from_window(self, point):
1056+
"""Translates window coordinates to renderer coordinates
1057+
1058+
:param point: The coordinates in window space.
1059+
"""
1060+
cdef float lx
1061+
cdef float ly
1062+
1063+
# Note: Must be changed to SDL_RenderCoordinatesFromWindow for SDL3
1064+
# https://wiki.libsdl.org/SDL3/SDL_RenderCoordinatesFromWindow
1065+
SDL_RenderWindowToLogical(self._renderer, point[0], point[1], &lx, &ly);
1066+
1067+
return (lx, ly)
1068+
10401069
def draw_point(self, point):
10411070
"""Draw a point
10421071

src_c/doc/sdl2_video_doc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
#define DOC_SDL2_VIDEO_RENDERER_PRESENT "present() -> None\nUpdate the screen with any rendering performed since the previous call"
4242
#define DOC_SDL2_VIDEO_RENDERER_GETVIEWPORT "get_viewport() -> Rect\nGet the drawing area on the rendering target"
4343
#define DOC_SDL2_VIDEO_RENDERER_SETVIEWPORT "set_viewport(area) -> None\nSet the drawing area on the rendering target"
44+
#define DOC_SDL2_VIDEO_RENDERER_COORDINATESTOWINDOW "coordinates_to_window(point) -> (float, float)\nTranslates renderer coordinates to window coordinates"
45+
#define DOC_SDL2_VIDEO_RENDERER_COORDINATESFROMWINDOW "coordinates_from_window(point) -> (float, float)\nTranslates window coordinates to renderer coordinates"
4446
#define DOC_SDL2_VIDEO_RENDERER_BLIT "blit(source, dest, area=None, special_flags=0)-> Rect\nDraw textures using a Surface-like API"
4547
#define DOC_SDL2_VIDEO_RENDERER_DRAWLINE "draw_line(p1, p2) -> None\nDraw a line"
4648
#define DOC_SDL2_VIDEO_RENDERER_DRAWPOINT "draw_point(point) -> None\nDraw a point"

src_c/render.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,61 @@ renderer_set_logical_size(pgRendererObject *self, PyObject *arg, void *closure)
468468
return 0;
469469
}
470470

471+
static PyObject *
472+
renderer_coordinates_to_window(pgRendererObject *self, PyObject *args,
473+
PyObject *kwargs)
474+
{
475+
float lx, ly;
476+
PyObject *point;
477+
478+
static char *keywords[] = {"point", NULL};
479+
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", keywords, &point)) {
480+
return NULL;
481+
}
482+
if (!pg_TwoFloatsFromObj(point, &lx, &ly)) {
483+
return RAISE(PyExc_TypeError,
484+
"point must be a sequence of two numbers");
485+
}
486+
487+
#if SDL_VERSION_ATLEAST(3, 0, 0)
488+
float wx, wy;
489+
SDL_RenderCoordinatesToWindow(self->renderer, lx, ly, &wx, &wy);
490+
491+
return pg_tuple_couple_from_values_double(wx, wy);
492+
#else
493+
int wx, wy;
494+
SDL_RenderLogicalToWindow(self->renderer, lx, ly, &wx, &wy);
495+
496+
return pg_tuple_couple_from_values_double((float)wx, (float)wy);
497+
#endif
498+
}
499+
500+
static PyObject *
501+
renderer_coordinates_from_window(pgRendererObject *self, PyObject *args,
502+
PyObject *kwargs)
503+
{
504+
float lx, ly;
505+
float wx, wy;
506+
PyObject *point;
507+
508+
static char *keywords[] = {"point", NULL};
509+
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", keywords, &point)) {
510+
return NULL;
511+
}
512+
if (!pg_TwoFloatsFromObj(point, &wx, &wy)) {
513+
return RAISE(PyExc_TypeError,
514+
"point must be a sequence of two numbers");
515+
}
516+
517+
#if SDL_VERSION_ATLEAST(3, 0, 0)
518+
SDL_RenderCoordinatesFromWindow(self->renderer, wx, wy, &lx, &ly);
519+
#else
520+
SDL_RenderWindowToLogical(self->renderer, (int)wx, (int)wy, &lx, &ly);
521+
#endif
522+
523+
return pg_tuple_couple_from_values_double(lx, ly);
524+
}
525+
471526
static PyObject *
472527
renderer_get_scale(pgRendererObject *self, void *closure)
473528
{
@@ -1150,6 +1205,12 @@ static PyMethodDef renderer_methods[] = {
11501205
METH_VARARGS | METH_KEYWORDS, DOC_SDL2_VIDEO_RENDERER_SETVIEWPORT},
11511206
{"get_viewport", (PyCFunction)renderer_get_viewport, METH_NOARGS,
11521207
DOC_SDL2_VIDEO_RENDERER_GETVIEWPORT},
1208+
{"coordinates_to_window", (PyCFunction)renderer_coordinates_to_window,
1209+
METH_VARARGS | METH_KEYWORDS,
1210+
DOC_SDL2_VIDEO_RENDERER_COORDINATESTOWINDOW},
1211+
{"coordinates_from_window", (PyCFunction)renderer_coordinates_from_window,
1212+
METH_VARARGS | METH_KEYWORDS,
1213+
DOC_SDL2_VIDEO_RENDERER_COORDINATESFROMWINDOW},
11531214
{"compose_custom_blend_mode",
11541215
(PyCFunction)renderer_compose_custom_blend_mode,
11551216
METH_VARARGS | METH_KEYWORDS | METH_CLASS,

test/render_test.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,15 @@ def test_logical_size(self):
172172
self.renderer.logical_size = (10, 10)
173173
self.assertEqual(self.renderer.logical_size, (10, 10))
174174

175+
def test_logical_window_mapping(self):
176+
self.renderer.logical_size = (10, 10)
177+
self.assertEqual(self.renderer.coordinates_to_window((10, 10)), (100, 100))
178+
self.assertEqual(self.renderer.coordinates_from_window((100, 100)), (10, 10))
179+
with self.assertRaises(TypeError):
180+
self.renderer.coordinates_to_window(42, 42)
181+
with self.assertRaises(TypeError):
182+
self.renderer.coordinates_from_window(42, 42)
183+
175184
def test_scale(self):
176185
self.assertEqual(self.renderer.scale, (1.0, 1.0))
177186
self.renderer.scale = (0.5, 2)

test/video_test.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,18 @@ def test_renderer_set_viewport(self):
2424
renderer.set_viewport(rect)
2525
self.assertEqual(renderer.get_viewport(), (0, 0, 1920, 1080))
2626

27+
def test_logical_window_mapping(self):
28+
window = video.Window(title=self.default_caption, size=(100, 100))
29+
renderer = video.Renderer(window=window)
30+
renderer.logical_size = (10, 10)
31+
32+
self.assertEqual(renderer.coordinates_to_window((10, 10)), (100, 100))
33+
self.assertEqual(renderer.coordinates_from_window((100, 100)), (10, 10))
34+
with self.assertRaises(TypeError):
35+
renderer.coordinates_to_window(42, 42)
36+
with self.assertRaises(TypeError):
37+
renderer.coordinates_from_window(42, 42)
38+
2739
@unittest.skipIf(IS_PYPY, "PyPy doesn't have sys.getrefcount")
2840
def test_renderer_to_surface_refcount(self):
2941
"""Make sure to_surface doesn't leak memory due to reference counting."""

0 commit comments

Comments
 (0)