# SDL\_image: Load single GIF images as 8-bit indexed images

**URL:** <https://discourse.libsdl.org/t/sdl-image-load-single-gif-images-as-8-bit-indexed-images/69357>\
**Category:** SDL Commits\
**Created:** [September 21, 2026, 2:16pm UTC](https://discourse.libsdl.org/t/sdl-image-load-single-gif-images-as-8-bit-indexed-images/69357 "2026-09-21T14:16:37Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![SDLPushPostBot](https://discourse.libsdl.org/user_avatar/discourse.libsdl.org/sdlpushpostbot/32/4370_2.png) [@SDLPushPostBot](https://discourse.libsdl.org/u/SDLPushPostBot)\
**Post date:** [September 21, 2026, 2:16pm UTC](https://discourse.libsdl.org/t/sdl-image-load-single-gif-images-as-8-bit-indexed-images/69357/1 "2026-09-21T14:16:37Z")

</div>

> <https://github.com/libsdl-org/SDL_image/commit/329d61949b64d1b8557661bd13f838775c7eb9a1>
>
> Fixes https://github.com/libsdl-org/SDL\_image/issues/765

```diff
From 329d61949b64d1b8557661bd13f838775c7eb9a1 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 20 Sep 2026 11:26:42 -0700
Subject: [PATCH] Load single GIF images as 8-bit indexed images

Fixes https://github.com/libsdl-org/SDL_image/issues/765
---
 src/IMG_gif.c | 102 ++++++++++++++++++++++++++++++--------------------
 1 file changed, 61 insertions(+), 41 deletions(-)

diff --git a/src/IMG_gif.c b/src/IMG_gif.c
index a13bc3de6..46555383e 100644
--- a/src/IMG_gif.c
+++ b/src/IMG_gif.c
@@ -28,6 +28,8 @@
 #include "IMG_anim_encoder.h"
 #include "IMG_anim_decoder.h"
 
+#define IMG_PROP_ANIMATION_DECODER_CREATE_GIF_SINGLE_IMAGE_BOOLEAN "SDL_image.animation_decoder.create.gif.single_image"
+
 // We will have the saving GIF feature by default
 #if !defined(SAVE_GIF)
 #define SAVE_GIF 1
@@ -528,6 +530,8 @@ struct IMG_AnimationDecoderContext
     int frame_count; /* Total number of frames seen */
     int current_frame; /* Current frame index */
 
+ bool single_frame; /* Whether this decoder will return a single frame */
+
     bool got_header; /* Whether we've read the GIF header */
     bool got_eof; /* Whether we've reached the end of the GIF */
 
@@ -550,7 +554,7 @@ struct IMG_AnimationDecoderContext
     bool ignore_props;
 };
 
-static bool IMG_AnimationDecoderGetGIFHeader(IMG_AnimationDecoder *decoder, char**comment, int *loopCount)
+static bool IMG_AnimationDecoderGetGIFHeader(IMG_AnimationDecoder *decoder, char **comment, int *loopCount)
 {
     if (comment) {
         *comment = NULL;
@@ -723,29 +727,31 @@ static bool IMG_AnimationDecoderGetGIFHeader(IMG_AnimationDecoder *decoder, char
             SDL_SeekIO(src, stream_pos, SDL_IO_SEEK_SET);
         }
 
- if (!ctx->canvas) {
- ctx->canvas = SDL_CreateSurface(ctx->width, ctx->height, SDL_PIXELFORMAT_RGBA32);
+ if (!ctx->single_frame) {
             if (!ctx->canvas) {
- return SDL_SetError("Failed to create canvas surface");
- }
+ ctx->canvas = SDL_CreateSurface(ctx->width, ctx->height, SDL_PIXELFORMAT_RGBA32);
+ if (!ctx->canvas) {
+ return SDL_SetError("Failed to create canvas surface");
+ }
 
- if (!SDL_FillSurfaceRect(ctx->canvas, NULL, 0)) {
- SDL_DestroySurface(ctx->canvas);
- ctx->canvas = NULL;
- return SDL_SetError("Failed to fill canvas surface with transparent color");
+ if (!SDL_FillSurfaceRect(ctx->canvas, NULL, 0)) {
+ SDL_DestroySurface(ctx->canvas);
+ ctx->canvas = NULL;
+ return SDL_SetError("Failed to fill canvas surface with transparent color");
+ }
             }
- }
 
- if (!ctx->prev_canvas) {
- ctx->prev_canvas = SDL_CreateSurface(ctx->width, ctx->height, SDL_PIXELFORMAT_RGBA32);
             if (!ctx->prev_canvas) {
- return SDL_SetError("Failed to create previous canvas surface");
- }
+ ctx->prev_canvas = SDL_CreateSurface(ctx->width, ctx->height, SDL_PIXELFORMAT_RGBA32);
+ if (!ctx->prev_canvas) {
+ return SDL_SetError("Failed to create previous canvas surface");
+ }
 
- if (!SDL_FillSurfaceRect(ctx->prev_canvas, NULL, 0)) {
- SDL_DestroySurface(ctx->prev_canvas);
- ctx->prev_canvas = NULL;
- return SDL_SetError("Failed to fill previous canvas surface with transparent color");
+ if (!SDL_FillSurfaceRect(ctx->prev_canvas, NULL, 0)) {
+ SDL_DestroySurface(ctx->prev_canvas);
+ ctx->prev_canvas = NULL;
+ return SDL_SetError("Failed to fill previous canvas surface with transparent color");
+ }
             }
         }
 
@@ -902,31 +908,36 @@ static bool IMG_AnimationDecoderGetNextFrame_Internal(IMG_AnimationDecoder *deco
                               BitSet(ctx->buf[8], INTERLACE), 0, &ctx->state);
         }
 
- if (!image) {
- // Incorrect animation is harder to detect than a direct failure,
- // so it's better to fail than try to animate a GIF without a,
- // full set of frames it has in the file.
+ if (!image) {
+ // Incorrect animation is harder to detect than a direct failure,
+ // so it's better to fail than try to animate a GIF without the
+ // full set of frames it has in the file.
 
- // Only set the error if ReadImage did not do it.
- if (SDL_GetError()[0] == '\0') {
- return SDL_SetError("Failed to decode frame.");
- }
-
- return false;
- }
+ // Only set the error if ReadImage did not do it.
+ if (SDL_GetError()[0] == '\0') {
+ return SDL_SetError("Failed to decode frame.");
+ }
 
- /* Composite the frame onto the canvas */
- SDL_Rect dest = { left, top, width, height };
- if (!SDL_BlitSurface(image, NULL, ctx->canvas, &dest)) {
- SDL_DestroySurface(image);
- return SDL_SetError("Failed to blit frame onto canvas");
+ return false;
         }
 
- /* Store the frame in the output array */
- retval = SDL_DuplicateSurface(ctx->canvas);
- if (!retval) {
+ if (ctx->single_frame &&
+ left == 0 && top == 0 && image->w == ctx->width && image->h == ctx->height) {
+ retval = image;
+ } else {
+ /* Composite the frame onto the canvas */
+ SDL_Rect dest = { left, top, width, height };
+ if (!SDL_BlitSurface(image, NULL, ctx->canvas, &dest)) {
+ SDL_DestroySurface(image);
+ return SDL_SetError("Failed to blit frame onto canvas");
+ }
+
+ /* Store the frame in the output array */
+ retval = SDL_DuplicateSurface(ctx->canvas);
             SDL_DestroySurface(image);
- return SDL_SetError("Failed to duplicate frame surface");
+ if (!retval) {
+ return SDL_SetError("Failed to duplicate frame surface");
+ }
         }
 
         if (ctx->state.Gif89.delayTime < 0 && ctx->last_duration) {
@@ -941,8 +952,6 @@ static bool IMG_AnimationDecoderGetNextFrame_Internal(IMG_AnimationDecoder *deco
 
         ctx->last_disposal = ctx->state.Gif89.disposal;
 
- SDL_DestroySurface(image);
-
         ctx->state.Gif89.transparent = -1;
         ctx->state.Gif89.delayTime = -1;
         ctx->state.Gif89.inputFlag = -1;
@@ -1004,6 +1013,7 @@ bool IMG_CreateGIFAnimationDecoder(IMG_AnimationDecoder *decoder, SDL_Properties
     ctx->last_disposal = GIF_DISPOSE_NONE;
     SDL_Rect r = {0};
     ctx->restore_area = r;
+ ctx->single_frame = SDL_GetBooleanProperty(props, IMG_PROP_ANIMATION_DECODER_CREATE_GIF_SINGLE_IMAGE_BOOLEAN, false);
 
     decoder->ctx = ctx;
     decoder->Reset = IMG_AnimationDecoderReset_Internal;
@@ -1073,7 +1083,17 @@ bool IMG_isGIF(SDL_IOStream *src)
 /* Load a GIF type image from an SDL datasource */
 SDL_Surface *IMG_LoadGIF_IO(SDL_IOStream *src)
 {
- IMG_AnimationDecoder *decoder = IMG_CreateAnimationDecoder_IO(src, false, "gif");
+ SDL_PropertiesID props = SDL_CreateProperties();
+ if (!props) {
+ return NULL;
+ }
+
+ SDL_SetPointerProperty(props, IMG_PROP_ANIMATION_DECODER_CREATE_IOSTREAM_POINTER, src);
+ SDL_SetBooleanProperty(props, IMG_PROP_ANIMATION_DECODER_CREATE_IOSTREAM_AUTOCLOSE_BOOLEAN, false);
+ SDL_SetStringProperty(props, IMG_PROP_ANIMATION_DECODER_CREATE_TYPE_STRING, "gif");
+ SDL_SetBooleanProperty(props, IMG_PROP_ANIMATION_DECODER_CREATE_GIF_SINGLE_IMAGE_BOOLEAN, true);
+ IMG_AnimationDecoder *decoder = IMG_CreateAnimationDecoderWithProperties(props);
+ SDL_DestroyProperties(props);
     if (!decoder) {
         return NULL;
     }

```
