SDL_image: Added IMG_CreateAnimatedCursor()

From 25cd57ca8d5de4dc0871852f100732d7525a26ab Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 19 Oct 2025 13:56:01 -0700
Subject: [PATCH] Added IMG_CreateAnimatedCursor()

---
 CMakeLists.txt                 |  2 +-
 include/SDL3_image/SDL_image.h | 20 ++++++++++++++++++++
 src/IMG.c                      | 28 ++++++++++++++++++++++++++++
 src/SDL_image.sym              |  1 +
 4 files changed, 50 insertions(+), 1 deletion(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 69db9866..c888178f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,7 +10,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
 set(MAJOR_VERSION 3)
 set(MINOR_VERSION 3)
 set(MICRO_VERSION 0)
-set(SDL_REQUIRED_VERSION 3.2.6)
+set(SDL_REQUIRED_VERSION 3.3.0)
 
 project(SDL3_image
     LANGUAGES C
diff --git a/include/SDL3_image/SDL_image.h b/include/SDL3_image/SDL_image.h
index 0254b2b9..f3a8d1b2 100644
--- a/include/SDL3_image/SDL_image.h
+++ b/include/SDL3_image/SDL_image.h
@@ -2222,6 +2222,7 @@ typedef struct IMG_Animation
  *
  * \since This function is available since SDL_image 3.0.0.
  *
+ * \sa IMG_CreateAnimatedCursor
  * \sa IMG_LoadAnimation_IO
  * \sa IMG_LoadAnimationTyped_IO
  * \sa IMG_LoadAPNGAnimation_IO
@@ -2249,6 +2250,7 @@ extern SDL_DECLSPEC IMG_Animation * SDLCALL IMG_LoadAnimation(const char *file);
  *
  * \since This function is available since SDL_image 3.0.0.
  *
+ * \sa IMG_CreateAnimatedCursor
  * \sa IMG_LoadAnimation
  * \sa IMG_LoadAnimationTyped_IO
  * \sa IMG_LoadAPNGAnimation_IO
@@ -2283,6 +2285,7 @@ extern SDL_DECLSPEC IMG_Animation * SDLCALL IMG_LoadAnimation_IO(SDL_IOStream *s
  *
  * \since This function is available since SDL_image 3.0.0.
  *
+ * \sa IMG_CreateAnimatedCursor
  * \sa IMG_LoadAnimation
  * \sa IMG_LoadAnimation_IO
  * \sa IMG_LoadAPNGAnimation_IO
@@ -2410,6 +2413,23 @@ extern SDL_DECLSPEC IMG_Animation * SDLCALL IMG_LoadGIFAnimation_IO(SDL_IOStream
  */
 extern SDL_DECLSPEC IMG_Animation * SDLCALL IMG_LoadWEBPAnimation_IO(SDL_IOStream *src);
 
+/**
+ * Create an animated cursor from an animation.
+ *
+ * \param anim an animation to use to create an animated cursor.
+ * \param hot_x the x position of the cursor hot spot.
+ * \param hot_y the y position of the cursor hot spot.
+ * \returns the new cursor on success or NULL on failure; call SDL_GetError()
+ *          for more information.
+ *
+ * \since This function is available since SDL_image 3.4.0.
+ *
+ * \sa IMG_LoadAnimation
+ * \sa IMG_LoadAnimation_IO
+ * \sa IMG_LoadAnimationTyped_IO
+ */
+extern SDL_DECLSPEC SDL_Cursor * SDLCALL IMG_CreateAnimatedCursor(IMG_Animation *anim, int hot_x, int hot_y);
+
 /**
  * An object representing the encoder context.
  */
diff --git a/src/IMG.c b/src/IMG.c
index 8572fcd9..8809de7b 100644
--- a/src/IMG.c
+++ b/src/IMG.c
@@ -321,6 +321,34 @@ IMG_Animation *IMG_LoadAnimationTyped_IO(SDL_IOStream *src, bool closeio, const
     return NULL;
 }
 
+SDL_Cursor *IMG_CreateAnimatedCursor(IMG_Animation *anim, int hot_x, int hot_y)
+{
+    int i;
+    SDL_CursorFrameInfo *frames;
+    SDL_Cursor *cursor;
+
+    if (!anim) {
+        SDL_InvalidParamError("anim");
+        return NULL;
+    }
+
+    frames = (SDL_CursorFrameInfo *)SDL_calloc(anim->count, sizeof(*frames));
+    if (!frames) {
+        return NULL;
+    }
+
+    for (i = 0; i < anim->count; ++i) {
+        frames[i].surface = anim->frames[i];
+        frames[i].duration = (Uint32)anim->delays[i];
+    }
+
+    cursor = SDL_CreateAnimatedCursor(frames, anim->count, hot_x, hot_y);
+
+    SDL_free(frames);
+
+    return cursor;
+}
+
 void IMG_FreeAnimation(IMG_Animation *anim)
 {
     if (anim) {
diff --git a/src/SDL_image.sym b/src/SDL_image.sym
index 96234f4e..b23a914e 100644
--- a/src/SDL_image.sym
+++ b/src/SDL_image.sym
@@ -85,5 +85,6 @@ SDL3_image_0.0.0 {
     IMG_GetAnimationDecoderProperties;
     IMG_GetAnimationDecoderStatus;
     IMG_GetClipboardImage;
+    IMG_CreateAnimatedCursor;
   local: *;
 };