Skip to content

Instantly share code, notes, and snippets.

@urraka
Last active September 16, 2024 17:12
Show Gist options
  • Save urraka/685d9a6340b26b830d49 to your computer and use it in GitHub Desktop.
Save urraka/685d9a6340b26b830d49 to your computer and use it in GitHub Desktop.
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define STBI_ONLY_PNG
#define STBI_ONLY_JPEG
#define STBI_ONLY_BMP
#define STBI_ONLY_GIF
#include "stb_image.h"
#include "stb_image_write.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct gif_result_t {
int delay;
unsigned char *data;
struct gif_result_t *next;
} gif_result;
STBIDEF unsigned char *stbi_xload(char const *filename, int *x, int *y, int *frames)
{
FILE *f;
stbi__context s;
unsigned char *result = 0;
if (!(f = stbi__fopen(filename, "rb")))
return stbi__errpuc("can't fopen", "Unable to open file");
stbi__start_file(&s, f);
if (stbi__gif_test(&s))
{
int c;
stbi__gif g;
gif_result head;
gif_result *prev = 0, *gr = &head;
memset(&g, 0, sizeof(g));
memset(&head, 0, sizeof(head));
*frames = 0;
while (gr->data = stbi__gif_load_next(&s, &g, &c, 4))
{
if (gr->data == (unsigned char*)&s)
{
gr->data = 0;
break;
}
if (prev) prev->next = gr;
gr->delay = g.delay;
prev = gr;
gr = (gif_result*) stbi__malloc(sizeof(gif_result));
memset(gr, 0, sizeof(gif_result));
++(*frames);
}
STBI_FREE(g.out);
if (gr != &head)
STBI_FREE(gr);
if (*frames > 0)
{
*x = g.w;
*y = g.h;
}
result = head.data;
if (*frames > 1)
{
unsigned int size = 4 * g.w * g.h;
unsigned char *p = 0;
result = (unsigned char*)stbi__malloc(*frames * (size + 2));
gr = &head;
p = result;
while (gr)
{
prev = gr;
memcpy(p, gr->data, size);
p += size;
*p++ = gr->delay & 0xFF;
*p++ = (gr->delay & 0xFF00) >> 8;
gr = gr->next;
STBI_FREE(prev->data);
if (prev != &head) STBI_FREE(prev);
}
}
}
else
{
result = stbi__load_main(&s, x, y, frames, 4);
*frames = !!result;
}
fclose(f);
return result;
}
#ifdef __cplusplus
}
#endif
@Flix01
Copy link

Flix01 commented Feb 16, 2016

I'm sorry: on a closer look the memory leaks seem to be still present even with my (horrible and incorrect) 'fix'.
So I don't have any valid solution yet.

@urraka
Copy link
Author

urraka commented Feb 16, 2016

Sorry, STBI_FREE(s.out) is wrong. Should be STBI_FREE(g.out) instead. I'll take another look to see if I find why it's still leaking. Maybe I'll do some actual testing too instead of just trying to guess what's wrong.

@urraka
Copy link
Author

urraka commented Feb 16, 2016

I tested with some sample gifs. Adding STBI_FREE(g.out) after the while loop seems to fix the leaks here. Make sure you call stbi_image_free on the result of stbi_xload at some point too.

@Flix01
Copy link

Flix01 commented Feb 17, 2016

I tested with some sample gifs. Adding STBI_FREE(g.out) after the while loop seems to fix the leaks here.

Yes! This worked for me too (tested with 2 different gifs: no more leaks!)

Thanks.
P.S. You should modify your code to reflect the fix.
In any case, for clarity, from Revision 1 of this gist, users should just add STBI_FREE(g.out); after the loop (before: if (gr != &head)).

@urraka
Copy link
Author

urraka commented Feb 17, 2016

Cool. It's updated now.

@Gtoyos
Copy link

Gtoyos commented Jan 30, 2020

This code is outdated. Multiple functions of stbi_image have been changed so this code doesn't work anymore:

In line 44: stbi__gif_load_next(&s, &g, &c, 4)
New definition: static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back)

In line 98: result = stbi__load_main(&s, x, y, frames, 4);
New definition: static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc)

Is there any way to make it work with the current functions? What has changed since 2016? There is still a link to this piece of code as a GIF loader solution.

@urraka
Copy link
Author

urraka commented Jan 30, 2020

Hey @Gtoyos, I suggest that you look into stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) (https://github.com/nothings/stb/blob/f67165c2bb2af3060ecae7d20d6f731173485ad0/stb_image.h#L1300). It looks like it's been added somewhere along the way and it returns a buffer with all frames and delays in a separate argument.

@urraka
Copy link
Author

urraka commented Jan 20, 2022

@Platforming-Mayhem
Copy link

Platforming-Mayhem commented Nov 19, 2023

Hi @urraka, Do u have any tutorials or advice on how to use the updated version? I'm currently stuck on how to access/load the other frames of the gif. Also do you know how to allow stbi_set_flip_vertically_on_load to work on the gifs?

@urraka
Copy link
Author

urraka commented Nov 19, 2023

Hello @Platforming-Mayhem.

It goes something like this:

// INPUT:
// unsigned char *buffer;
// int bufferSize;

int width = 0;
int height = 0;
int nFrames = 0;
int *delays = NULL;
unsigned char *data = stbi_xload_mem(buffer, bufferSize, &width, &height, &nFrames, &delays);

unsigned char *get_frame_image(int frame) {
  return data + (width * height * 4 * frame);
}

int get_frame_delay(int frame) {
  return delays[frame] / 10; // don't remember what the /10 was all about, probably just time unit conversion
}

Here's some reference code using it (PASCAL)

Regarding the stbi_set_flip_vertically_on_load I wouldn't know. It's been a while since I used this stuff. You could try making that call before the loading happens and see if it makes a difference.

@Platforming-Mayhem
Copy link

YESSS!!! I WORKED.
@urraka Thank you very much :)

@Platforming-Mayhem
Copy link

Platforming-Mayhem commented Nov 20, 2023

@urraka Sorry to bother you, but do u have any idea as to why this is happening when it loads the next frame?
image

image

I also get this exception thrown when loading the next frame on majority of my gifs
image
Some of them don't get this issue, but those are the ones where the gif is created in a specific program, I think it was Adobe Animate CC.
Animated Gifs created in Krita don't work, Animated Gifs created in Photoshop also don't work.

@Platforming-Mayhem
Copy link

Nevermind this was my mistake.

@Platforming-Mayhem
Copy link

Platforming-Mayhem commented Nov 21, 2023

image
This is how you allow vertical flipping for the gifs :)

@urraka
Copy link
Author

urraka commented Nov 22, 2023

Hey @Platforming-Mayhem, looks like you got it working, good to know!

By the way, that last line you're highlighting looks suspicious to me. Isn't stbi_set_flip_vertically_on_load a function? My C/C++ is rusty but I think that might be interpreting it as a pointer and just testing that it's not zero, so stbi__vertical_flip_slices is always called.

@Platforming-Mayhem
Copy link

@urraka I'm not sure, since I just copied it from one of the stb_load functions

@urraka
Copy link
Author

urraka commented Nov 23, 2023

@Platforming-Mayhem nevermind what I said, my bad. The function is stbi_set_flip_vertically_on_load but you have stbi__vertically_flip_on_load so it's all good ;D

@Platforming-Mayhem
Copy link

Hi @urraka,
I've been using this code for a while now but recently I found out that it takes up a lot of memory. Is this normal and is there anyway to decrease the amount of memory that is used?
Thanks :)

@urraka
Copy link
Author

urraka commented Jan 4, 2024

Hi @Platforming-Mayhem. The way this works is the easy way out: load every frame into memory in RGBA format. If you have big images with many frames it will take a lot of memory (width * height * 4 * number of frames bytes). I suppose, in theory, one could load the compressed file into memory and buffer a few frames at a time (sort of like video preloading)... but that would need a lot more work.

@Platforming-Mayhem
Copy link

Thanks :)
This was a big help :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment