From e18d63a4e2e2f2ab742ee1ce017b1fd3cb9c73ef Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 22 Oct 2025 17:28:34 -0700 Subject: [PATCH] Added SDL_RotateSurface() Fixes https://github.com/libsdl-org/SDL/issues/14269 --- VisualC-GDK/SDL/SDL.vcxproj | 4 +- VisualC-GDK/SDL/SDL.vcxproj.filters | 4 +- VisualC/SDL/SDL.vcxproj | 4 +- VisualC/SDL/SDL.vcxproj.filters | 12 +- Xcode/SDL/SDL.xcodeproj/project.pbxproj | 16 +- include/SDL3/SDL_surface.h | 18 ++ src/dynapi/SDL_dynapi.sym | 1 + src/dynapi/SDL_dynapi_overrides.h | 1 + src/dynapi/SDL_dynapi_procs.h | 1 + src/render/software/SDL_render_sw.c | 4 +- src/{render/software => video}/SDL_rotate.c | 15 +- src/{render/software => video}/SDL_rotate.h | 0 src/video/SDL_surface.c | 33 ++++ test/CMakeLists.txt | 1 + test/testrotate.c | 205 ++++++++++++++++++++ 15 files changed, 286 insertions(+), 33 deletions(-) rename src/{render/software => video}/SDL_rotate.c (98%) rename src/{render/software => video}/SDL_rotate.h (100%) create mode 100644 test/testrotate.c diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj index bfc48f03e4..5f7ac76679 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj +++ b/VisualC-GDK/SDL/SDL.vcxproj @@ -504,7 +504,6 @@ - @@ -604,6 +603,7 @@ + @@ -809,7 +809,6 @@ - @@ -891,6 +890,7 @@ + diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters index ac16b9156f..abdb3175c5 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj.filters +++ b/VisualC-GDK/SDL/SDL.vcxproj.filters @@ -127,7 +127,6 @@ - @@ -186,6 +185,7 @@ + @@ -406,7 +406,6 @@ - @@ -460,6 +459,7 @@ + diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj index c93558c6c4..7af7132b29 100644 --- a/VisualC/SDL/SDL.vcxproj +++ b/VisualC/SDL/SDL.vcxproj @@ -417,7 +417,6 @@ - @@ -513,6 +512,7 @@ + @@ -676,7 +676,6 @@ - @@ -747,6 +746,7 @@ + diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters index 3497dcfc05..77bd59d5fb 100644 --- a/VisualC/SDL/SDL.vcxproj.filters +++ b/VisualC/SDL/SDL.vcxproj.filters @@ -693,6 +693,9 @@ video + + video + video @@ -885,9 +888,6 @@ render\software - - render\software - render\software @@ -1380,6 +1380,9 @@ video + + video + video @@ -1589,9 +1592,6 @@ render\software - - render\software - render\software diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj index f7fc845fe0..4d3a891c3f 100644 --- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj +++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj @@ -288,7 +288,6 @@ A7D8B9E323E2514400DCD162 /* SDL_drawline.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A8F123E2514000DCD162 /* SDL_drawline.c */; }; A7D8B9E923E2514400DCD162 /* SDL_blendline.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A8F223E2514000DCD162 /* SDL_blendline.h */; }; A7D8B9EF23E2514400DCD162 /* SDL_drawpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A8F323E2514000DCD162 /* SDL_drawpoint.h */; }; - A7D8B9F523E2514400DCD162 /* SDL_rotate.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A8F423E2514000DCD162 /* SDL_rotate.c */; }; A7D8B9FB23E2514400DCD162 /* SDL_render_sw_c.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A8F523E2514000DCD162 /* SDL_render_sw_c.h */; }; A7D8BA0123E2514400DCD162 /* SDL_blendfillrect.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A8F623E2514000DCD162 /* SDL_blendfillrect.h */; }; A7D8BA0723E2514400DCD162 /* SDL_drawline.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A8F723E2514000DCD162 /* SDL_drawline.h */; }; @@ -298,7 +297,6 @@ A7D8BA1F23E2514400DCD162 /* SDL_blendline.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A8FB23E2514000DCD162 /* SDL_blendline.c */; }; A7D8BA2523E2514400DCD162 /* SDL_drawpoint.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A8FC23E2514000DCD162 /* SDL_drawpoint.c */; }; A7D8BA2B23E2514400DCD162 /* SDL_blendfillrect.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A8FD23E2514000DCD162 /* SDL_blendfillrect.c */; }; - A7D8BA3123E2514400DCD162 /* SDL_rotate.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A8FE23E2514000DCD162 /* SDL_rotate.h */; }; A7D8BA4923E2514400DCD162 /* SDL_render_gles2.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A90423E2514000DCD162 /* SDL_render_gles2.c */; }; A7D8BA4F23E2514400DCD162 /* SDL_shaders_gles2.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A90523E2514000DCD162 /* SDL_shaders_gles2.h */; }; A7D8BA5523E2514400DCD162 /* SDL_gles2funcs.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A90623E2514000DCD162 /* SDL_gles2funcs.h */; }; @@ -523,6 +521,8 @@ F3D60A8328C16A1900788A3A /* SDL_hidapi_wii.c in Sources */ = {isa = PBXBuildFile; fileRef = F3D60A8228C16A1800788A3A /* SDL_hidapi_wii.c */; }; F3D8BDFC2D6D2C7000B22FA1 /* SDL_eventwatch_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3D8BDFB2D6D2C7000B22FA1 /* SDL_eventwatch_c.h */; }; F3D8BDFD2D6D2C7000B22FA1 /* SDL_eventwatch.c in Sources */ = {isa = PBXBuildFile; fileRef = F3D8BDFA2D6D2C7000B22FA1 /* SDL_eventwatch.c */; }; + F3DB66342EA9ACC300568044 /* SDL_rotate.c in Sources */ = {isa = PBXBuildFile; fileRef = F3DB66332EA9ACC300568044 /* SDL_rotate.c */; }; + F3DB66352EA9ACC300568044 /* SDL_rotate.h in Headers */ = {isa = PBXBuildFile; fileRef = F3DB66322EA9ACC300568044 /* SDL_rotate.h */; }; F3DC38C92E5FC60300CD73DE /* SDL_libusb.h in Headers */ = {isa = PBXBuildFile; fileRef = F3DC38C72E5FC60300CD73DE /* SDL_libusb.h */; }; F3DC38CA2E5FC60300CD73DE /* SDL_libusb.c in Sources */ = {isa = PBXBuildFile; fileRef = F3DC38C82E5FC60300CD73DE /* SDL_libusb.c */; }; F3DDCC562AFD42B600B0842B /* SDL_clipboard_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3DDCC4D2AFD42B500B0842B /* SDL_clipboard_c.h */; }; @@ -877,7 +877,6 @@ A7D8A8F123E2514000DCD162 /* SDL_drawline.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_drawline.c; sourceTree = ""; }; A7D8A8F223E2514000DCD162 /* SDL_blendline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_blendline.h; sourceTree = ""; }; A7D8A8F323E2514000DCD162 /* SDL_drawpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_drawpoint.h; sourceTree = ""; }; - A7D8A8F423E2514000DCD162 /* SDL_rotate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_rotate.c; sourceTree = ""; }; A7D8A8F523E2514000DCD162 /* SDL_render_sw_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_render_sw_c.h; sourceTree = ""; }; A7D8A8F623E2514000DCD162 /* SDL_blendfillrect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_blendfillrect.h; sourceTree = ""; }; A7D8A8F723E2514000DCD162 /* SDL_drawline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_drawline.h; sourceTree = ""; }; @@ -887,7 +886,6 @@ A7D8A8FB23E2514000DCD162 /* SDL_blendline.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_blendline.c; sourceTree = ""; }; A7D8A8FC23E2514000DCD162 /* SDL_drawpoint.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_drawpoint.c; sourceTree = ""; }; A7D8A8FD23E2514000DCD162 /* SDL_blendfillrect.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_blendfillrect.c; sourceTree = ""; }; - A7D8A8FE23E2514000DCD162 /* SDL_rotate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_rotate.h; sourceTree = ""; }; A7D8A90423E2514000DCD162 /* SDL_render_gles2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_render_gles2.c; sourceTree = ""; }; A7D8A90523E2514000DCD162 /* SDL_shaders_gles2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_shaders_gles2.h; sourceTree = ""; }; A7D8A90623E2514000DCD162 /* SDL_gles2funcs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_gles2funcs.h; sourceTree = ""; }; @@ -1105,6 +1103,8 @@ F3D60A8228C16A1800788A3A /* SDL_hidapi_wii.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_wii.c; sourceTree = ""; }; F3D8BDFA2D6D2C7000B22FA1 /* SDL_eventwatch.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SDL_eventwatch.c; sourceTree = ""; }; F3D8BDFB2D6D2C7000B22FA1 /* SDL_eventwatch_c.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_eventwatch_c.h; sourceTree = ""; }; + F3DB66322EA9ACC300568044 /* SDL_rotate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_rotate.h; sourceTree = ""; }; + F3DB66332EA9ACC300568044 /* SDL_rotate.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SDL_rotate.c; sourceTree = ""; }; F3DC38C72E5FC60300CD73DE /* SDL_libusb.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_libusb.h; sourceTree = ""; }; F3DC38C82E5FC60300CD73DE /* SDL_libusb.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SDL_libusb.c; sourceTree = ""; }; F3DDCC4D2AFD42B500B0842B /* SDL_clipboard_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_clipboard_c.h; sourceTree = ""; }; @@ -1661,6 +1661,8 @@ F3DDCC542AFD42B600B0842B /* SDL_rect_impl.h */, A7D8A61523E2513D00DCD162 /* SDL_RLEaccel.c */, A7D8A76723E2513E00DCD162 /* SDL_RLEaccel_c.h */, + F3DB66322EA9ACC300568044 /* SDL_rotate.h */, + F3DB66332EA9ACC300568044 /* SDL_rotate.c */, F3EFA5E92D5AB97300BCF22F /* SDL_stb.c */, F3EFA5EA2D5AB97300BCF22F /* SDL_stb_c.h */, A7D8A60323E2513D00DCD162 /* SDL_stretch.c */, @@ -2220,8 +2222,6 @@ A7D8A8F323E2514000DCD162 /* SDL_drawpoint.h */, A7D8A8F523E2514000DCD162 /* SDL_render_sw_c.h */, A7D8A8F923E2514000DCD162 /* SDL_render_sw.c */, - A7D8A8F423E2514000DCD162 /* SDL_rotate.c */, - A7D8A8FE23E2514000DCD162 /* SDL_rotate.h */, ); path = software; sourceTree = ""; @@ -2532,6 +2532,7 @@ A7D8AEEE23E2514100DCD162 /* SDL_cocoaopengles.h in Headers */, F3D46ACA2D20625800D9CBDF /* SDL_storage.h in Headers */, F3D46ACB2D20625800D9CBDF /* SDL_sensor.h in Headers */, + F3DB66352EA9ACC300568044 /* SDL_rotate.h in Headers */, F3D46ACC2D20625800D9CBDF /* SDL_properties.h in Headers */, F3D46ACD2D20625800D9CBDF /* SDL_bits.h in Headers */, F3D46ACE2D20625800D9CBDF /* SDL_keyboard.h in Headers */, @@ -2669,7 +2670,6 @@ F3DDCC5D2AFD42B600B0842B /* SDL_rect_impl.h in Headers */, A7D8B9FB23E2514400DCD162 /* SDL_render_sw_c.h in Headers */, E4F257972C81903800FCEAFC /* SDL_sysgpu.h in Headers */, - A7D8BA3123E2514400DCD162 /* SDL_rotate.h in Headers */, A7D8A98D23E2514000DCD162 /* SDL_sensor_c.h in Headers */, A7D8BA7323E2514400DCD162 /* SDL_shaders_gl.h in Headers */, A7D8BA4F23E2514400DCD162 /* SDL_shaders_gles2.h in Headers */, @@ -2912,7 +2912,6 @@ A7D8AEC423E2514100DCD162 /* SDL_cocoaevents.m in Sources */, E479118F2BA9555500CE3B7F /* SDL_genericstorage.c in Sources */, A7D8B86623E2514400DCD162 /* SDL_audiocvt.c in Sources */, - A7D8B9F523E2514400DCD162 /* SDL_rotate.c in Sources */, A7D8BBE323E2574800DCD162 /* SDL_uikitvideo.m in Sources */, F338A1182D1B37D8007CDFDF /* SDL_tray.m in Sources */, 5616CA4E252BB2A6005D5928 /* SDL_sysurl.m in Sources */, @@ -2940,6 +2939,7 @@ E4F257962C81903800FCEAFC /* SDL_gpu.c in Sources */, F3D60A8328C16A1900788A3A /* SDL_hidapi_wii.c in Sources */, A7D8B9DD23E2514400DCD162 /* SDL_blendpoint.c in Sources */, + F3DB66342EA9ACC300568044 /* SDL_rotate.c in Sources */, A7D8B4EE23E2514300DCD162 /* SDL_gamepad.c in Sources */, E4A568B62AF763940062EEC4 /* SDL_sysmain_callbacks.c in Sources */, F316ABD82B5C3185002EF551 /* SDL_memset.c in Sources */, diff --git a/include/SDL3/SDL_surface.h b/include/SDL3/SDL_surface.h index db2a6cd8d8..0eeeabd384 100644 --- a/include/SDL3/SDL_surface.h +++ b/include/SDL3/SDL_surface.h @@ -939,6 +939,24 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetSurfaceClipRect(SDL_Surface *surface, SD */ extern SDL_DECLSPEC bool SDLCALL SDL_FlipSurface(SDL_Surface *surface, SDL_FlipMode flip); +/** + * Return a copy of a surface rotated clockwise a number of degrees. + * + * The angle of rotation can be negative for counter-clockwise rotation. + * + * When the rotation isn't a multiple of 90 degrees, the resulting surface is larger than the original, with the background filled in with the colorkey, if available, or RGBA 255/255/255/0 if not. + * + * \param surface the surface to rotate. + * \param angle the rotation angle, in degrees. + * \returns a rotated copy of the surface or NULL on failure; call SDL_GetError() for + * more information. + * + * \threadsafety This function is not thread safe. + * + * \since This function is available since SDL 3.2.0. + */ +extern SDL_DECLSPEC SDL_Surface * SDLCALL SDL_RotateSurface(SDL_Surface *surface, float angle); + /** * Creates a new surface identical to the existing surface. * diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index b779befedd..9feb8a0fc6 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -1267,6 +1267,7 @@ SDL3_0.0.0 { SDL_GetSystemPageSize; SDL_GetPenDeviceType; SDL_CreateAnimatedCursor; + SDL_RotateSurface; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index b19ddb8c24..1d17c7fcf0 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -1293,3 +1293,4 @@ #define SDL_GetSystemPageSize SDL_GetSystemPageSize_REAL #define SDL_GetPenDeviceType SDL_GetPenDeviceType_REAL #define SDL_CreateAnimatedCursor SDL_CreateAnimatedCursor_REAL +#define SDL_RotateSurface SDL_RotateSurface_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 654f617534..a2fc06fd7f 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1301,3 +1301,4 @@ SDL_DYNAPI_PROC(bool,SDL_SavePNG,(SDL_Surface *a,const char *b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_GetSystemPageSize,(void),(),return) SDL_DYNAPI_PROC(SDL_PenDeviceType,SDL_GetPenDeviceType,(SDL_PenID a),(a),return) SDL_DYNAPI_PROC(SDL_Cursor*,SDL_CreateAnimatedCursor,(SDL_CursorFrameInfo *a,int b,int c,int d),(a,b,c,d),return) +SDL_DYNAPI_PROC(SDL_Surface*,SDL_RotateSurface,(SDL_Surface *a,float b),(a,b),return) diff --git a/src/render/software/SDL_render_sw.c b/src/render/software/SDL_render_sw.c index 8c85db1efe..e62b280b58 100644 --- a/src/render/software/SDL_render_sw.c +++ b/src/render/software/SDL_render_sw.c @@ -31,9 +31,9 @@ #include "SDL_blendpoint.h" #include "SDL_drawline.h" #include "SDL_drawpoint.h" -#include "SDL_rotate.h" #include "SDL_triangle.h" #include "../../video/SDL_pixels_c.h" +#include "../../video/SDL_rotate.h" // SDL surface based renderer implementation @@ -406,7 +406,7 @@ static bool SW_RenderCopyEx(SDL_Renderer *renderer, SDL_Surface *surface, SDL_Te SDL_GetSurfaceColorMod(src, &rMod, &gMod, &bMod); // SDLgfx_rotateSurface only accepts 32-bit surfaces with a 8888 layout. Everything else has to be converted. - if (src->fmt->bits_per_pixel != 32 || SDL_PIXELLAYOUT(src->format) != SDL_PACKEDLAYOUT_8888 || !SDL_ISPIXELFORMAT_ALPHA(src->format)) { + if (!(SDL_BITSPERPIXEL(src->format) == 32 && SDL_PIXELLAYOUT(src->format) == SDL_PACKEDLAYOUT_8888)) { blitRequired = true; } diff --git a/src/render/software/SDL_rotate.c b/src/video/SDL_rotate.c similarity index 98% rename from src/render/software/SDL_rotate.c rename to src/video/SDL_rotate.c index 516ba8a4d1..c5071b525f 100644 --- a/src/render/software/SDL_rotate.c +++ b/src/video/SDL_rotate.c @@ -30,16 +30,9 @@ Andreas Schiffler -- aschiffler at ferzkopp dot net */ #include "SDL_internal.h" -#ifdef SDL_VIDEO_RENDER_SW - -#if defined(SDL_PLATFORM_WINDOWS) -#include "../../core/windows/SDL_windows.h" -#endif - +#include "SDL_surface_c.h" #include "SDL_rotate.h" -#include "../../video/SDL_surface_c.h" - // ---- Internally used structures /** @@ -506,8 +499,9 @@ SDL_Surface *SDLgfx_rotateSurface(SDL_Surface *src, double angle, int smooth, in } } // This function requires a 32-bit surface or 8-bit surface with a colorkey - is8bit = src->fmt->bits_per_pixel == 8 && colorKeyAvailable; - if (!(is8bit || (src->fmt->bits_per_pixel == 32 && SDL_ISPIXELFORMAT_ALPHA(src->format)))) { + is8bit = (src->format == SDL_PIXELFORMAT_INDEX8) && colorKeyAvailable; + if (!is8bit && + !(SDL_BITSPERPIXEL(src->format) == 32 && SDL_PIXELLAYOUT(src->format) == SDL_PACKEDLAYOUT_8888)) { return NULL; } @@ -609,4 +603,3 @@ SDL_Surface *SDLgfx_rotateSurface(SDL_Surface *src, double angle, int smooth, in return rz_dst; } -#endif // SDL_VIDEO_RENDER_SW diff --git a/src/render/software/SDL_rotate.h b/src/video/SDL_rotate.h similarity index 100% rename from src/render/software/SDL_rotate.h rename to src/video/SDL_rotate.h diff --git a/src/video/SDL_surface.c b/src/video/SDL_surface.c index 8c0e8bac28..914b1c63b6 100644 --- a/src/video/SDL_surface.c +++ b/src/video/SDL_surface.c @@ -24,6 +24,7 @@ #include "SDL_video_c.h" #include "SDL_RLEaccel_c.h" #include "SDL_pixels_c.h" +#include "SDL_rotate.h" #include "SDL_stb_c.h" #include "SDL_yuv_c.h" #include "../render/SDL_sysrender.h" @@ -2167,6 +2168,38 @@ error: return NULL; } +SDL_Surface *SDL_RotateSurface(SDL_Surface *surface, float angle) +{ + SDL_Surface *rotated = NULL; + + CHECK_PARAM(!SDL_SurfaceValid(surface)) { + SDL_InvalidParamError("surface"); + return NULL; + } + + SDL_Rect rect_dest; + double cangle, sangle; + SDL_FPoint center = { surface->w * 0.5f, surface->h * 0.5f }; + SDLgfx_rotozoomSurfaceSizeTrig(surface->w, surface->h, angle, ¢er, &rect_dest, &cangle, &sangle); + + // This function requires a 32-bit surface or 8-bit surface with a colorkey + if ((SDL_BITSPERPIXEL(surface->format) == 32 && SDL_PIXELLAYOUT(surface->format) == SDL_PACKEDLAYOUT_8888) || + (surface->format == SDL_PIXELFORMAT_INDEX8 && SDL_SurfaceHasColorKey(surface))) { + rotated = SDLgfx_rotateSurface(surface, angle, 1, 0, 0, &rect_dest, cangle, sangle, ¢er); + } else { + SDL_Surface *convert = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA32); + if (convert) { + SDL_Surface *tmp = SDLgfx_rotateSurface(convert, angle, 1, 0, 0, &rect_dest, cangle, sangle, ¢er); + if (tmp) { + rotated = SDL_ConvertSurfaceAndColorspace(tmp, surface->format, surface->palette, surface->colorspace, surface->props); + SDL_DestroySurface(tmp); + } + SDL_DestroySurface(convert); + } + } + return rotated; +} + SDL_Surface *SDL_DuplicateSurface(SDL_Surface *surface) { CHECK_PARAM(!SDL_SurfaceValid(surface)) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 53dddabb2c..800e0f6357 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -393,6 +393,7 @@ if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 4) add_sdl_test_executable(pretest SOURCES pretest.c NONINTERACTIVE NONINTERACTIVE_TIMEOUT 60) endif() add_sdl_test_executable(testrendertarget NEEDS_RESOURCES TESTUTILS SOURCES testrendertarget.c) +add_sdl_test_executable(testrotate SOURCES testrotate.c) add_sdl_test_executable(testscale NEEDS_RESOURCES TESTUTILS SOURCES testscale.c) add_sdl_test_executable(testsem NONINTERACTIVE DISABLE_THREADS_ARGS "--no-threads" NONINTERACTIVE_ARGS 10 NONINTERACTIVE_TIMEOUT 30 SOURCES testsem.c) add_sdl_test_executable(testsensor SOURCES testsensor.c) diff --git a/test/testrotate.c b/test/testrotate.c new file mode 100644 index 0000000000..6eb5e6f700 --- /dev/null +++ b/test/testrotate.c @@ -0,0 +1,205 @@ +/* + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely. +*/ + +#define SDL_MAIN_USE_CALLBACKS 1 +#include +#include +#include + +#define IMAGE_SIZE 256 + +static SDLTest_CommonState *state; +static SDL_Surface *image; +static SDL_Texture *texture; +static int format_index = -1; +static SDL_PixelFormat formats[] = { + SDL_PIXELFORMAT_RGBA32, + SDL_PIXELFORMAT_ARGB32, + SDL_PIXELFORMAT_RGBX32, + SDL_PIXELFORMAT_XRGB32, + SDL_PIXELFORMAT_ARGB1555, + SDL_PIXELFORMAT_INDEX8 +}; +static int angle; +static int direction = 1; + +static bool UpdateImageFormat(void) +{ + static const SDL_Color colors[] = { + { 255, 255, 255, SDL_ALPHA_TRANSPARENT }, /* Colorkey - white with transparent alpha */ + { 255, 0, 0, SDL_ALPHA_OPAQUE }, /* Red */ + { 255, 255, 0, SDL_ALPHA_OPAQUE }, /* Yellow */ + { 0, 255, 0, SDL_ALPHA_OPAQUE }, /* Green */ + { 0, 0, 255, SDL_ALPHA_OPAQUE }, /* Blue */ + }; + SDL_Rect rect; + Uint32 color; + + ++format_index; + if (format_index == SDL_arraysize(formats)) { + format_index = 0; + } + + SDL_DestroySurface(image); + + image = SDL_CreateSurface(IMAGE_SIZE, IMAGE_SIZE, formats[format_index]); + if (!image) { + SDL_Log("Couldn't create surface: %s\n", SDL_GetError()); + return false; + } + + if (image->format == SDL_PIXELFORMAT_INDEX8) { + /* Set the palette and colorkey */ + SDL_Palette *palette = SDL_CreateSurfacePalette(image); + + SDL_SetPaletteColors(palette, colors, 0, SDL_arraysize(colors)); + SDL_SetSurfaceColorKey(image, true, 0); + } + + /* Upper left */ + rect.x = 0; + rect.y = 0; + rect.w = IMAGE_SIZE / 2; + rect.h = IMAGE_SIZE / 2; + color = SDL_MapSurfaceRGB(image, colors[1].r, colors[1].g, colors[1].b); + SDL_FillSurfaceRect(image, &rect, color); + + /* Upper right */ + rect.x += rect.w; + color = SDL_MapSurfaceRGB(image, colors[2].r, colors[2].g, colors[2].b); + SDL_FillSurfaceRect(image, &rect, color); + + /* Lower left */ + rect.x = 0; + rect.y += rect.h; + color = SDL_MapSurfaceRGB(image, colors[3].r, colors[3].g, colors[3].b); + SDL_FillSurfaceRect(image, &rect, color); + + /* Lower right */ + rect.x += rect.w; + color = SDL_MapSurfaceRGB(image, colors[4].r, colors[4].g, colors[4].b); + SDL_FillSurfaceRect(image, &rect, color); + + return true; +} + +static bool UpdateRotation(SDL_Renderer *renderer) +{ + SDL_Surface *rotated; + + angle += direction; + + rotated = SDL_RotateSurface(image, (float)angle); + if (!rotated) { + SDL_Log("Couldn't rotate surface: %s", SDL_GetError()); + return false; + } + + SDL_DestroyTexture(texture); + texture = SDL_CreateTextureFromSurface(renderer, rotated); + SDL_DestroySurface(rotated); + if (!texture) { + SDL_Log("Couldn't create texture: %s", SDL_GetError()); + return false; + } + + return true; +} + +static void Draw(SDL_Renderer *renderer) +{ + int w, h; + SDL_FRect dst; + + /* Clear the screen */ + SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); + SDL_RenderClear(renderer); + + UpdateRotation(renderer); + + /* Draw the rotated image */ + SDL_GetCurrentRenderOutputSize(renderer, &w, &h); + dst.x = (w - texture->w) / 2.0f; + dst.y = (h - texture->h) / 2.0f; + dst.w = (float)texture->w; + dst.h = (float)texture->h; + SDL_RenderTexture(renderer, texture, NULL, &dst); + + /* Show the current format */ + SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE); + SDL_RenderDebugTextFormat(renderer, 4.0f, 4.0f, "Format: %s, press SPACE to cycle", SDL_GetPixelFormatName(formats[format_index])); + + /* All done! */ + SDL_RenderPresent(renderer); +} + +void SDL_AppQuit(void *appstate, SDL_AppResult result) +{ + SDLTest_CommonQuit(state); +} + +SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) +{ + /* Initialize test framework */ + state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO); + if (!state) { + return SDL_APP_FAILURE; + } + + if (!SDLTest_CommonInit(state)) { + return SDL_APP_FAILURE; + } + + /* Create the spinning image */ + UpdateImageFormat(); + + return SDL_APP_CONTINUE; +} + + +SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) +{ + switch (event->type) { + case SDL_EVENT_KEY_UP: + switch (event->key.key) { + case SDLK_SPACE: + UpdateImageFormat(); + break; + case SDLK_LEFT: + direction = -1; + break; + case SDLK_RIGHT: + direction = 1; + break; + default: + break; + } + break; + default: + break; + } + return SDLTest_CommonEventMainCallbacks(state, event); +} + +SDL_AppResult SDL_AppIterate(void *appstate) +{ + int i; + + for (i = 0; i < state->num_windows; ++i) { + Draw(state->renderers[i]); + } + + /* Wait a bit so we don't spin too quickly to see */ + SDL_Delay(10); + + return SDL_APP_CONTINUE; +}