From aa19bc897ddc9a9da73d97146f705aa7cda4d828 Mon Sep 17 00:00:00 2001
From: zeromake <[EMAIL REDACTED]>
Date: Tue, 1 Nov 2022 10:29:43 +0800
Subject: [PATCH] Add webp Animation support
---
IMG.c | 1 +
IMG_webp.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++
SDL_image.h | 20 ++++++++
3 files changed, 166 insertions(+)
diff --git a/IMG.c b/IMG.c
index e48ed243..4f7bb3ca 100644
--- a/IMG.c
+++ b/IMG.c
@@ -86,6 +86,7 @@ static struct {
} supported_anims[] = {
/* keep magicless formats first */
{ "GIF", IMG_isGIF, IMG_LoadGIFAnimation_RW },
+ { "WEBP", IMG_isWEBP, IMG_LoadWEBPAnimation_RW },
};
const SDL_version *IMG_Linked_Version(void)
diff --git a/IMG_webp.c b/IMG_webp.c
index fa68ca94..15ac54e8 100644
--- a/IMG_webp.c
+++ b/IMG_webp.c
@@ -40,6 +40,7 @@
#define MACOS
#endif
#include <webp/decode.h>
+#include <webp/demux.h>
static struct {
int loaded;
@@ -53,6 +54,10 @@ static struct {
uint8_t* (*WebPDecodeRGBInto) (const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
uint8_t* (*WebPDecodeRGBAInto) (const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
#endif
+ WebPDemuxer* (*WebPDemux)(const WebPData* data);
+ int (*WebPDemuxGetFrame)(const WebPDemuxer* dmux, int frame_number, WebPIterator* iter);
+ uint32_t (*WebPDemuxGetI)(const WebPDemuxer* dmux, WebPFormatFeature feature);
+ void (*WebPDemuxDelete)(WebPDemuxer* dmux);
} lib;
#ifdef LOAD_WEBP_DYNAMIC
@@ -83,6 +88,10 @@ int IMG_InitWEBP()
FUNCTION_LOADER(WebPDecodeRGBInto, uint8_t * (*) (const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride))
FUNCTION_LOADER(WebPDecodeRGBAInto, uint8_t * (*) (const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride))
#endif
+ FUNCTION_LOADER(WebPDemux, WebPDemuxer* (*)(const WebPData* data))
+ FUNCTION_LOADER(WebPDemuxGetFrame, int (*)(const WebPDemuxer* dmux, int frame_number, WebPIterator* iter))
+ FUNCTION_LOADER(WebPDemuxGetI, uint32_t (*)(const WebPDemuxer* dmux, WebPFormatFeature feature));
+ FUNCTION_LOADER(WebPDemuxDelete, void (*)(WebPDemuxer* dmux))
}
++lib.loaded;
@@ -265,6 +274,138 @@ SDL_Surface *IMG_LoadWEBP_RW(SDL_RWops *src)
return(NULL);
}
+IMG_Animation *IMG_LoadWEBPAnimation_RW(SDL_RWops *src)
+{
+ Sint64 start;
+ const char *error = NULL;
+ Uint32 Rmask;
+ Uint32 Gmask;
+ Uint32 Bmask;
+ Uint32 Amask;
+ WebPBitstreamFeatures features;
+ struct WebPDemuxer* dmuxer = NULL;
+ WebPIterator iter;
+ IMG_Animation *anim = NULL;
+ int raw_data_size;
+ uint8_t *raw_data = NULL;
+ int r;
+ uint8_t *ret;
+
+ if ( !src ) {
+ /* The error message has been set in SDL_RWFromFile */
+ return NULL;
+ }
+
+ start = SDL_RWtell(src);
+
+ if ( (IMG_Init(IMG_INIT_WEBP) & IMG_INIT_WEBP) == 0 ) {
+ goto error;
+ }
+
+ raw_data_size = -1;
+ if ( !webp_getinfo( src, &raw_data_size ) ) {
+ error = "Invalid WEBP Animation";
+ goto error;
+ }
+
+ raw_data = (uint8_t*) SDL_malloc( raw_data_size );
+ if ( raw_data == NULL ) {
+ error = "Failed to allocate enough buffer for WEBP Animation";
+ goto error;
+ }
+
+ r = (int)SDL_RWread(src, raw_data, 1, raw_data_size );
+ if ( r != raw_data_size ) {
+ error = "Failed to read WEBP Animation";
+ goto error;
+ }
+
+ if ( lib.WebPGetFeaturesInternal( raw_data, raw_data_size, &features, WEBP_DECODER_ABI_VERSION ) != VP8_STATUS_OK ) {
+ error = "WebPGetFeatures has failed";
+ goto error;
+ }
+
+ /* Check if it's ok !*/
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+ Rmask = 0x000000FF;
+ Gmask = 0x0000FF00;
+ Bmask = 0x00FF0000;
+ Amask = (features.has_alpha) ? 0xFF000000 : 0;
+#else
+ {
+ int s = (features.has_alpha) ? 0 : 8;
+ Rmask = 0xFF000000 >> s;
+ Gmask = 0x00FF0000 >> s;
+ Bmask = 0x0000FF00 >> s;
+ Amask = 0x000000FF >> s;
+ }
+#endif
+
+ WebPData wd = { raw_data , raw_data_size};
+ dmuxer = lib.WebPDemux(&wd);
+ anim = (IMG_Animation *)SDL_malloc(sizeof(IMG_Animation));
+ anim->w = features.width;
+ anim->h = features.height;
+ anim->count = lib.WebPDemuxGetI(dmuxer, WEBP_FF_FRAME_COUNT);
+ anim->frames = (SDL_Surface **)SDL_calloc(anim->count, sizeof(*anim->frames));
+ anim->delays = (int *)SDL_calloc(anim->count, sizeof(*anim->delays));
+ for (int frame_idx = 0; frame_idx < (anim->count); frame_idx++) {
+ if (lib.WebPDemuxGetFrame(dmuxer, frame_idx, &iter) == 0) {
+ break;
+ }
+ SDL_Surface* curr = SDL_CreateRGBSurface(SDL_SWSURFACE,
+ features.width, features.height,
+ features.has_alpha?32:24, Rmask,Gmask,Bmask,Amask);
+ if ( curr == NULL ) {
+ error = "Failed to allocate SDL_Surface";
+ goto error;
+ }
+ anim->frames[frame_idx] = curr;
+ anim->delays[frame_idx] = iter.duration;
+ if ( features.has_alpha ) {
+ ret = lib.WebPDecodeRGBAInto(
+ iter.fragment.bytes,
+ iter.fragment.size,
+ (uint8_t *)curr->pixels,
+ curr->pitch * curr->h,
+ curr->pitch);
+ } else {
+ ret = lib.WebPDecodeRGBInto(
+ iter.fragment.bytes, iter.fragment.size,
+ (uint8_t *)curr->pixels,
+ curr->pitch * curr->h,
+ curr->pitch);
+ }
+ if (ret == NULL) {
+ break;
+ }
+ }
+ if (dmuxer) {
+ lib.WebPDemuxDelete(dmuxer);
+ }
+
+ if ( raw_data ) {
+ SDL_free( raw_data );
+ }
+ return anim;
+error:
+ if (anim) {
+ IMG_FreeAnimation(anim);
+ }
+ if (dmuxer) {
+ lib.WebPDemuxDelete(dmuxer);
+ }
+ if (raw_data) {
+ SDL_free( raw_data );
+ }
+
+ if (error) {
+ IMG_SetError( "%s", error );
+ }
+ SDL_RWseek(src, start, RW_SEEK_SET);
+ return(NULL);
+}
+
#else
#if _MSC_VER >= 1300
#pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */
@@ -292,4 +433,8 @@ SDL_Surface *IMG_LoadWEBP_RW(SDL_RWops *src)
return(NULL);
}
+IMG_Animation *IMG_LoadWEBPAnimation_RW(SDL_RWops *src) {
+ return(NULL);
+}
+
#endif /* LOAD_WEBP */
diff --git a/SDL_image.h b/SDL_image.h
index 03ceeb6f..b74b51f5 100644
--- a/SDL_image.h
+++ b/SDL_image.h
@@ -2150,6 +2150,26 @@ extern DECLSPEC void SDLCALL IMG_FreeAnimation(IMG_Animation *anim);
*/
extern DECLSPEC IMG_Animation * SDLCALL IMG_LoadGIFAnimation_RW(SDL_RWops *src);
+/**
+ * Load a WEBP animation directly.
+ *
+ * If you know you definitely have a WEBP image, you can call this function,
+ * which will skip SDL_image's file format detection routines. Generally it's
+ * better to use the abstract interfaces; also, there is only an SDL_RWops
+ * interface available here.
+ *
+ * \param src an SDL_RWops that data will be read from.
+ * \returns a new IMG_Animation, or NULL on error.
+ *
+ * \since This function is available since SDL_image 2.6.0.
+ *
+ * \sa IMG_LoadAnimation
+ * \sa IMG_LoadAnimation_RW
+ * \sa IMG_LoadAnimationTyped_RW
+ * \sa IMG_FreeAnimation
+ */
+extern DECLSPEC IMG_Animation * SDLCALL IMG_LoadWEBPAnimation_RW(SDL_RWops *src);
+
/**
* Report SDL_image errors
*