From e18d63a4e2e2f2ab742ee1ce017b1fd3cb9c73ef Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
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 bfc48f03e4b0b..5f7ac766792b9 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj
+++ b/VisualC-GDK/SDL/SDL.vcxproj
@@ -504,7 +504,6 @@
<ClInclude Include="..\..\src\render\software\SDL_drawline.h" />
<ClInclude Include="..\..\src\render\software\SDL_drawpoint.h" />
<ClInclude Include="..\..\src\render\software\SDL_render_sw_c.h" />
- <ClInclude Include="..\..\src\render\software\SDL_rotate.h" />
<ClInclude Include="..\..\src\render\software\SDL_triangle.h" />
<ClInclude Include="..\..\src\SDL_assert_c.h" />
<ClInclude Include="..\..\src\SDL_error_c.h" />
@@ -604,6 +603,7 @@
<ClInclude Include="..\..\src\video\SDL_pixels_c.h" />
<ClInclude Include="..\..\src\video\SDL_rect_c.h" />
<ClInclude Include="..\..\src\video\SDL_RLEaccel_c.h" />
+ <ClInclude Include="..\..\src\video\SDL_rotate.h" />
<ClInclude Include="..\..\src\video\SDL_stb_c.h" />
<ClInclude Include="..\..\src\video\SDL_surface_c.h" />
<ClInclude Include="..\..\src\video\SDL_sysvideo.h" />
@@ -809,7 +809,6 @@
<ClCompile Include="..\..\src\render\software\SDL_drawline.c" />
<ClCompile Include="..\..\src\render\software\SDL_drawpoint.c" />
<ClCompile Include="..\..\src\render\software\SDL_render_sw.c" />
- <ClCompile Include="..\..\src\render\software\SDL_rotate.c" />
<ClCompile Include="..\..\src\render\software\SDL_triangle.c" />
<ClCompile Include="..\..\src\render\vulkan\SDL_render_vulkan.c" />
<ClCompile Include="..\..\src\render\vulkan\SDL_shaders_vulkan.c" />
@@ -891,6 +890,7 @@
<ClCompile Include="..\..\src\video\SDL_pixels.c" />
<ClCompile Include="..\..\src\video\SDL_rect.c" />
<ClCompile Include="..\..\src\video\SDL_RLEaccel.c" />
+ <ClCompile Include="..\..\src\video\SDL_rotate.c" />
<ClCompile Include="..\..\src\video\SDL_stb.c" />
<ClCompile Include="..\..\src\video\SDL_stretch.c" />
<ClCompile Include="..\..\src\video\SDL_surface.c" />
diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters
index ac16b9156f33d..abdb3175c52bd 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj.filters
+++ b/VisualC-GDK/SDL/SDL.vcxproj.filters
@@ -127,7 +127,6 @@
<ClCompile Include="..\..\src\render\software\SDL_drawline.c" />
<ClCompile Include="..\..\src\render\software\SDL_drawpoint.c" />
<ClCompile Include="..\..\src\render\software\SDL_render_sw.c" />
- <ClCompile Include="..\..\src\render\software\SDL_rotate.c" />
<ClCompile Include="..\..\src\render\software\SDL_triangle.c" />
<ClCompile Include="..\..\src\SDL.c" />
<ClCompile Include="..\..\src\SDL_assert.c" />
@@ -186,6 +185,7 @@
<ClCompile Include="..\..\src\video\SDL_pixels.c" />
<ClCompile Include="..\..\src\video\SDL_rect.c" />
<ClCompile Include="..\..\src\video\SDL_RLEaccel.c" />
+ <ClCompile Include="..\..\src\video\SDL_rotate.c" />
<ClCompile Include="..\..\src\video\SDL_stb.c" />
<ClCompile Include="..\..\src\video\SDL_stretch.c" />
<ClCompile Include="..\..\src\video\SDL_surface.c" />
@@ -406,7 +406,6 @@
<ClInclude Include="..\..\src\render\software\SDL_drawline.h" />
<ClInclude Include="..\..\src\render\software\SDL_drawpoint.h" />
<ClInclude Include="..\..\src\render\software\SDL_render_sw_c.h" />
- <ClInclude Include="..\..\src\render\software\SDL_rotate.h" />
<ClInclude Include="..\..\src\render\software\SDL_triangle.h" />
<ClInclude Include="..\..\src\SDL_assert_c.h" />
<ClInclude Include="..\..\src\SDL_error_c.h" />
@@ -460,6 +459,7 @@
<ClInclude Include="..\..\src\video\SDL_pixels_c.h" />
<ClInclude Include="..\..\src\video\SDL_rect_c.h" />
<ClInclude Include="..\..\src\video\SDL_RLEaccel_c.h" />
+ <ClInclude Include="..\..\src\video\SDL_rotate.h" />
<ClInclude Include="..\..\src\video\SDL_stb_c.h" />
<ClInclude Include="..\..\src\video\SDL_surface_c.h" />
<ClInclude Include="..\..\src\video\SDL_sysvideo.h" />
diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index c93558c6c497e..7af7132b29a66 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/VisualC/SDL/SDL.vcxproj
@@ -417,7 +417,6 @@
<ClInclude Include="..\..\src\render\software\SDL_drawline.h" />
<ClInclude Include="..\..\src\render\software\SDL_drawpoint.h" />
<ClInclude Include="..\..\src\render\software\SDL_render_sw_c.h" />
- <ClInclude Include="..\..\src\render\software\SDL_rotate.h" />
<ClInclude Include="..\..\src\render\software\SDL_triangle.h" />
<ClInclude Include="..\..\src\render\vulkan\SDL_shaders_vulkan.h" />
<ClInclude Include="..\..\src\SDL_assert_c.h" />
@@ -513,6 +512,7 @@
<ClInclude Include="..\..\src\video\SDL_pixels_c.h" />
<ClInclude Include="..\..\src\video\SDL_rect_c.h" />
<ClInclude Include="..\..\src\video\SDL_RLEaccel_c.h" />
+ <ClInclude Include="..\..\src\video\SDL_rotate.h" />
<ClInclude Include="..\..\src\video\SDL_stb_c.h" />
<ClInclude Include="..\..\src\video\SDL_surface_c.h" />
<ClInclude Include="..\..\src\video\SDL_sysvideo.h" />
@@ -676,7 +676,6 @@
<ClCompile Include="..\..\src\render\software\SDL_drawline.c" />
<ClCompile Include="..\..\src\render\software\SDL_drawpoint.c" />
<ClCompile Include="..\..\src\render\software\SDL_render_sw.c" />
- <ClCompile Include="..\..\src\render\software\SDL_rotate.c" />
<ClCompile Include="..\..\src\render\software\SDL_triangle.c" />
<ClCompile Include="..\..\src\SDL.c" />
<ClCompile Include="..\..\src\SDL_assert.c" />
@@ -747,6 +746,7 @@
<ClCompile Include="..\..\src\video\SDL_pixels.c" />
<ClCompile Include="..\..\src\video\SDL_rect.c" />
<ClCompile Include="..\..\src\video\SDL_RLEaccel.c" />
+ <ClCompile Include="..\..\src\video\SDL_rotate.c" />
<ClCompile Include="..\..\src\video\SDL_stb.c" />
<ClCompile Include="..\..\src\video\SDL_stretch.c" />
<ClCompile Include="..\..\src\video\SDL_surface.c" />
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index 3497dcfc054d7..77bd59d5fb9a3 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/VisualC/SDL/SDL.vcxproj.filters
@@ -693,6 +693,9 @@
<ClInclude Include="..\..\src\video\SDL_RLEaccel_c.h">
<Filter>video</Filter>
</ClInclude>
+ <ClInclude Include="..\..\src\video\SDL_rotate.h">
+ <Filter>video</Filter>
+ </ClInclude>
<ClInclude Include="..\..\src\video\SDL_surface_c.h">
<Filter>video</Filter>
</ClInclude>
@@ -885,9 +888,6 @@
<ClInclude Include="..\..\src\render\software\SDL_render_sw_c.h">
<Filter>render\software</Filter>
</ClInclude>
- <ClInclude Include="..\..\src\render\software\SDL_rotate.h">
- <Filter>render\software</Filter>
- </ClInclude>
<ClInclude Include="..\..\src\render\software\SDL_triangle.h">
<Filter>render\software</Filter>
</ClInclude>
@@ -1380,6 +1380,9 @@
<ClCompile Include="..\..\src\video\SDL_rect.c">
<Filter>video</Filter>
</ClCompile>
+ <ClCompile Include="..\..\src\video\SDL_rotate.c">
+ <Filter>video</Filter>
+ </ClCompile>
<ClCompile Include="..\..\src\video\SDL_stb.c">
<Filter>video</Filter>
</ClCompile>
@@ -1589,9 +1592,6 @@
<ClCompile Include="..\..\src\render\software\SDL_render_sw.c">
<Filter>render\software</Filter>
</ClCompile>
- <ClCompile Include="..\..\src\render\software\SDL_rotate.c">
- <Filter>render\software</Filter>
- </ClCompile>
<ClCompile Include="..\..\src\render\software\SDL_triangle.c">
<Filter>render\software</Filter>
</ClCompile>
diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
index f7fc845fe006e..4d3a891c3f54e 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 = "<group>"; };
A7D8A8F223E2514000DCD162 /* SDL_blendline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_blendline.h; sourceTree = "<group>"; };
A7D8A8F323E2514000DCD162 /* SDL_drawpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_drawpoint.h; sourceTree = "<group>"; };
- A7D8A8F423E2514000DCD162 /* SDL_rotate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_rotate.c; sourceTree = "<group>"; };
A7D8A8F523E2514000DCD162 /* SDL_render_sw_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_render_sw_c.h; sourceTree = "<group>"; };
A7D8A8F623E2514000DCD162 /* SDL_blendfillrect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_blendfillrect.h; sourceTree = "<group>"; };
A7D8A8F723E2514000DCD162 /* SDL_drawline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_drawline.h; sourceTree = "<group>"; };
@@ -887,7 +886,6 @@
A7D8A8FB23E2514000DCD162 /* SDL_blendline.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_blendline.c; sourceTree = "<group>"; };
A7D8A8FC23E2514000DCD162 /* SDL_drawpoint.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_drawpoint.c; sourceTree = "<group>"; };
A7D8A8FD23E2514000DCD162 /* SDL_blendfillrect.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_blendfillrect.c; sourceTree = "<group>"; };
- A7D8A8FE23E2514000DCD162 /* SDL_rotate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_rotate.h; sourceTree = "<group>"; };
A7D8A90423E2514000DCD162 /* SDL_render_gles2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_render_gles2.c; sourceTree = "<group>"; };
A7D8A90523E2514000DCD162 /* SDL_shaders_gles2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_shaders_gles2.h; sourceTree = "<group>"; };
A7D8A90623E2514000DCD162 /* SDL_gles2funcs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_gles2funcs.h; sourceTree = "<group>"; };
@@ -1105,6 +1103,8 @@
F3D60A8228C16A1800788A3A /* SDL_hidapi_wii.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_wii.c; sourceTree = "<group>"; };
F3D8BDFA2D6D2C7000B22FA1 /* SDL_eventwatch.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SDL_eventwatch.c; sourceTree = "<group>"; };
F3D8BDFB2D6D2C7000B22FA1 /* SDL_eventwatch_c.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_eventwatch_c.h; sourceTree = "<group>"; };
+ F3DB66322EA9ACC300568044 /* SDL_rotate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_rotate.h; sourceTree = "<group>"; };
+ F3DB66332EA9ACC300568044 /* SDL_rotate.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SDL_rotate.c; sourceTree = "<group>"; };
F3DC38C72E5FC60300CD73DE /* SDL_libusb.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_libusb.h; sourceTree = "<group>"; };
F3DC38C82E5FC60300CD73DE /* SDL_libusb.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SDL_libusb.c; sourceTree = "<group>"; };
F3DDCC4D2AFD42B500B0842B /* SDL_clipboard_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_clipboard_c.h; sourceTree = "<group>"; };
@@ -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 = "<group>";
@@ -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 db2a6cd8d81fa..0eeeabd3845e8 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 b779befedd12c..9feb8a0fc6f7d 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 b19ddb8c241fa..1d17c7fcf0a2f 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 654f617534f46..a2fc06fd7f14d 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 8c85db1efe651..e62b280b5875a 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 516ba8a4d172b..c5071b525ffef 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 8c0e8bac28db3..914b1c63b6e99 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 @@ SDL_Surface *SDL_ConvertSurfaceAndColorspace(SDL_Surface *surface, SDL_PixelForm
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 53dddabb2cb8c..800e0f6357940 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 0000000000000..6eb5e6f700a7d
--- /dev/null
+++ b/test/testrotate.c
@@ -0,0 +1,205 @@
+/*
+ Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
+
+ 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 <SDL3/SDL_test.h>
+#include <SDL3/SDL_test_common.h>
+#include <SDL3/SDL_main.h>
+
+#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
(Patch may be truncated, please check the link at the top of this post.)