SDL_image: Minor cleanup (838cd)

From 838cd9fe90476f0dbdce360b570edb97d7cf1ee8 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 14 Aug 2025 14:34:02 -0700
Subject: [PATCH] Minor cleanup

This also makes it so decoders are responsible for setting IMG_DECODER_STATUS_COMPLETE, so IMG_GetAnimationDecoderFrame() knows to clear any lingering SDL error state.
---
 VisualC/SDL_image.vcxproj         |  6 +++---
 VisualC/SDL_image.vcxproj.filters |  6 ++++++
 include/SDL3_image/SDL_image.h    | 30 +++++++++++++++---------------
 src/IMG_anim_decoder.c            | 18 +++++++-----------
 src/IMG_anim_encoder.c            |  5 ++---
 src/IMG_anim_encoder.h            |  2 +-
 src/IMG_avif.c                    |  2 ++
 src/IMG_gif.c                     |  1 +
 src/IMG_libpng.c                  |  1 +
 src/IMG_webp.c                    |  7 ++++---
 10 files changed, 42 insertions(+), 36 deletions(-)

diff --git a/VisualC/SDL_image.vcxproj b/VisualC/SDL_image.vcxproj
index 1f1655ff..49a6b45f 100644
--- a/VisualC/SDL_image.vcxproj
+++ b/VisualC/SDL_image.vcxproj
@@ -219,6 +219,7 @@
     <ClCompile Include="..\src\IMG_xcf.c" />
     <ClCompile Include="..\src\IMG_xpm.c" />
     <ClCompile Include="..\src\IMG_xv.c" />
+    <ClCompile Include="..\src\xmlman.c" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\src\version.rc" />
@@ -230,6 +231,7 @@
     <ClInclude Include="..\src\IMG_libpng.h" />
     <ClInclude Include="..\src\IMG_gif.h" />
     <ClInclude Include="..\src\IMG_avif.h" />
+    <ClInclude Include="..\src\xmlman.h" />
   </ItemGroup>
   <ItemGroup>
     <CustomBuild Include="external\optional\x64\libavif-16.dll">
@@ -436,6 +438,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-
-</Project>
-
+</Project>
\ No newline at end of file
diff --git a/VisualC/SDL_image.vcxproj.filters b/VisualC/SDL_image.vcxproj.filters
index 04309215..5ac3ea57 100644
--- a/VisualC/SDL_image.vcxproj.filters
+++ b/VisualC/SDL_image.vcxproj.filters
@@ -70,6 +70,9 @@
     <ClCompile Include="..\src\IMG_anim_decoder.c">
       <Filter>Sources</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\xmlman.c">
+      <Filter>Sources</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <Filter Include="Sources">
@@ -104,6 +107,9 @@
     <ClInclude Include="..\src\IMG_anim_decoder.h">
       <Filter>Sources</Filter>
     </ClInclude>
+    <ClInclude Include="..\src\xmlman.h">
+      <Filter>Sources</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\src\version.rc">
diff --git a/include/SDL3_image/SDL_image.h b/include/SDL3_image/SDL_image.h
index 00c83552..2a923062 100644
--- a/include/SDL3_image/SDL_image.h
+++ b/include/SDL3_image/SDL_image.h
@@ -2403,20 +2403,6 @@ extern SDL_DECLSPEC IMG_Animation * SDLCALL IMG_LoadGIFAnimation_IO(SDL_IOStream
  */
 extern SDL_DECLSPEC IMG_Animation * SDLCALL IMG_LoadWEBPAnimation_IO(SDL_IOStream *src);
 
-/**
- * An enum representing the status of the encoder and decoder.
- *
- * \since This enum is available since SDL_image 3.4.0.
- */
-typedef enum IMG_AnimationDecoderStatus
-{
-    IMG_DECODER_STATUS_OK,          /**< Decoded the frame successfully. */
-    IMG_DECODER_STATUS_FAILED,      /**< Decoding the frame failed. Call SDL_GetError for more information. */
-    IMG_DECODER_STATUS_COMPLETE,    /**< No more frames available. */
-
-    IMG_DECODER_STATUS_INVALID      /**< Invalid decoder status that does not represent any valid status. */
-} IMG_AnimationDecoderStatus;
-
 /**
  * An object representing the encoder context.
  */
@@ -2553,6 +2539,19 @@ extern SDL_DECLSPEC bool SDLCALL IMG_AddAnimationEncoderFrame(IMG_AnimationEncod
  */
 extern SDL_DECLSPEC bool SDLCALL IMG_CloseAnimationEncoder(IMG_AnimationEncoder *encoder);
 
+/**
+ * An enum representing the status of an animation decoder.
+ *
+ * \since This enum is available since SDL_image 3.4.0.
+ */
+typedef enum IMG_AnimationDecoderStatus
+{
+    IMG_DECODER_STATUS_INVALID = -1,    /**< The decoder is invalid */
+    IMG_DECODER_STATUS_OK,              /**< The decoder is ready to decode the next frame */
+    IMG_DECODER_STATUS_FAILED,          /**< The decoder failed to decode a frame, call SDL_GetError() for more information. */
+    IMG_DECODER_STATUS_COMPLETE         /**< No more frames available */
+} IMG_AnimationDecoderStatus;
+
 /**
  * An object representing animation decoder.
  */
@@ -2690,13 +2689,14 @@ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL IMG_GetAnimationDecoderProperties(I
  *                 `IMG_PROP_ANIMATION_DECODER_CREATE_TIMEBASE_DENOMINATOR_NUMBER`
  *                 property is set when creating the decoder.
  * \returns true on success or false on failure and when no more frames are
- *          available; call SDL_GetError() for more information.
+ *          available; call IMG_GetAnimationDecoderStatus() or SDL_GetError() for more information.
  *
  * \since This function is available since SDL_image 3.4.0.
  *
  * \sa IMG_CreateAnimationDecoder
  * \sa IMG_CreateAnimationDecoder_IO
  * \sa IMG_CreateAnimationDecoderWithProperties
+ * \sa IMG_GetAnimationDecoderStatus
  * \sa IMG_ResetAnimationDecoder
  * \sa IMG_CloseAnimationDecoder
  */
diff --git a/src/IMG_anim_decoder.c b/src/IMG_anim_decoder.c
index e4f1fb1c..6268714c 100644
--- a/src/IMG_anim_decoder.c
+++ b/src/IMG_anim_decoder.c
@@ -193,8 +193,8 @@ bool IMG_GetAnimationDecoderFrame(IMG_AnimationDecoder *decoder, SDL_Surface **f
     if (!duration) {
         duration = &temp_duration;
     }
-    SDL_ClearError();
-    // Reset the status before trying to get the next frame.
+
+    // Reset the status before trying to get the next frame
     decoder->status = IMG_DECODER_STATUS_OK;
 
     bool result = decoder->GetNextFrame(decoder, frame, duration);
@@ -202,17 +202,14 @@ bool IMG_GetAnimationDecoderFrame(IMG_AnimationDecoder *decoder, SDL_Surface **f
         SDL_DestroySurface(temp_frame);
     }
 
-    if (!result) {
-        if (SDL_GetError()[0] == '\0') {
-            decoder->status = IMG_DECODER_STATUS_COMPLETE;
-        } else {
-            decoder->status = IMG_DECODER_STATUS_FAILED;
-        }
-    }
-
     if (result) {
         decoder->accumulated_pts += *duration;
     } else {
+        if (decoder->status == IMG_DECODER_STATUS_COMPLETE) {
+            SDL_ClearError();
+        } else {
+            decoder->status = IMG_DECODER_STATUS_FAILED;
+        }
         *frame = NULL;
         *duration = 0;
     }
@@ -263,7 +260,6 @@ IMG_AnimationDecoderStatus IMG_GetAnimationDecoderStatus(IMG_AnimationDecoder* d
     if (!decoder) {
         return IMG_DECODER_STATUS_INVALID;
     }
-
     return decoder->status;
 }
 
diff --git a/src/IMG_anim_encoder.c b/src/IMG_anim_encoder.c
index eecacd26..7c9ec23f 100644
--- a/src/IMG_anim_encoder.c
+++ b/src/IMG_anim_encoder.c
@@ -171,13 +171,12 @@ bool IMG_AddAnimationEncoderFrame(IMG_AnimationEncoder *encoder, SDL_Surface *su
     if (!surface || surface->w <= 0 || surface->h <= 0) {
         return SDL_InvalidParamError("surface");
     }
-    bool result = encoder->AddFrame(encoder, surface, duration);
 
+    bool result = encoder->AddFrame(encoder, surface, duration);
     if (result) {
         encoder->accumulated_pts += duration;
-        encoder->last_delay = duration;
+        encoder->last_duration = duration;
     }
-
     return result;
 }
 
diff --git a/src/IMG_anim_encoder.h b/src/IMG_anim_encoder.h
index fd413bf7..cded6f2b 100644
--- a/src/IMG_anim_encoder.h
+++ b/src/IMG_anim_encoder.h
@@ -30,7 +30,7 @@ struct IMG_AnimationEncoder
     int timebase_numerator;
     int timebase_denominator;
     Uint64 accumulated_pts;
-    Uint64 last_delay;
+    Uint64 last_duration;
 
     bool (*AddFrame)(IMG_AnimationEncoder *encoder, SDL_Surface *surface, Uint64 duration);
     bool (*Close)(IMG_AnimationEncoder *encoder);
diff --git a/src/IMG_avif.c b/src/IMG_avif.c
index 62e7de29..2c0d02d2 100644
--- a/src/IMG_avif.c
+++ b/src/IMG_avif.c
@@ -890,6 +890,7 @@ static bool IMG_AnimationDecoderGetNextFrame_Internal(IMG_AnimationDecoder *deco
     avifResult result;
 
     if (ctx->total_frames - ctx->current_frame < 1) {
+        decoder->status = IMG_DECODER_STATUS_COMPLETE;
         return false;
     }
 
@@ -897,6 +898,7 @@ static bool IMG_AnimationDecoderGetNextFrame_Internal(IMG_AnimationDecoder *deco
     if (result != AVIF_RESULT_OK) {
         if (result == AVIF_RESULT_NO_IMAGES_REMAINING) {
             // This shouldn't happen here, but handle it gracefully
+            decoder->status = IMG_DECODER_STATUS_COMPLETE;
             return false;
         }
 
diff --git a/src/IMG_gif.c b/src/IMG_gif.c
index 48179214..364af15f 100644
--- a/src/IMG_gif.c
+++ b/src/IMG_gif.c
@@ -757,6 +757,7 @@ static bool IMG_AnimationDecoderGetNextFrame_Internal(IMG_AnimationDecoder *deco
     int framesLoaded = 0;
 
     if (ctx->got_eof) {
+        decoder->status = IMG_DECODER_STATUS_COMPLETE;
         return false;
     }
 
diff --git a/src/IMG_libpng.c b/src/IMG_libpng.c
index 9398cd67..18773ee9 100644
--- a/src/IMG_libpng.c
+++ b/src/IMG_libpng.c
@@ -1147,6 +1147,7 @@ static bool IMG_AnimationDecoderGetNextFrame_Internal(IMG_AnimationDecoder *deco
     }
 
     if (ctx->actl.num_frames - ctx->current_frame_index < 1) {
+        decoder->status = IMG_DECODER_STATUS_COMPLETE;
         return false;
     }
 
diff --git a/src/IMG_webp.c b/src/IMG_webp.c
index 0bd172f5..be7ca0be 100644
--- a/src/IMG_webp.c
+++ b/src/IMG_webp.c
@@ -436,11 +436,11 @@ static bool IMG_AnimationDecoderGetNextFrame_Internal(IMG_AnimationDecoder *deco
     // Get the next frame from the demuxer.
     if (decoder->ctx->iter.frame_num < 1) {
         if (!lib.WebPDemuxGetFrame(decoder->ctx->demuxer, 1, &decoder->ctx->iter)) {
-            SDL_SetError("Failed to get first frame from WEBP demuxer");
-            return false;
+            return SDL_SetError("Failed to get first frame from WEBP demuxer");
         }
     } else {
         if (!lib.WebPDemuxNextFrame(&decoder->ctx->iter)) {
+            decoder->status = IMG_DECODER_STATUS_COMPLETE;
             return false;
         }
     }
@@ -449,6 +449,7 @@ static bool IMG_AnimationDecoderGetNextFrame_Internal(IMG_AnimationDecoder *deco
     int availableFrames = totalFrames - (decoder->ctx->iter.frame_num - 1);
 
     if (availableFrames < 1) {
+        decoder->status = IMG_DECODER_STATUS_COMPLETE;
         return false;
     }
 
@@ -915,7 +916,7 @@ static bool IMG_CloseWEBPAnimation(IMG_AnimationEncoder *encoder)
         goto done;
     }
 
-    int timestamp = (int)IMG_GetCurrentTimestamp(encoder, encoder->last_delay, 1000);
+    int timestamp = (int)IMG_GetCurrentTimestamp(encoder, encoder->last_duration, 1000);
     if (!lib.WebPAnimEncoderAdd(ctx->encoder, NULL, timestamp, &ctx->config)) {
         error = "WebPAnimEncoderAdd() failed";
         goto done;