SDL_image: Added support for AVIF images (https://github.com/AOMediaCodec/libavif)

From 00b71a12a5b0ea376a890fbcdd22475ae9694a39 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 9 May 2022 12:44:16 -0700
Subject: [PATCH] Added support for AVIF images
 (https://github.com/AOMediaCodec/libavif)

Fixes https://github.com/libsdl-org/SDL_image/issues/188
---
 .gitmodules      |   8 ++
 CHANGES.txt      |   2 +
 IMG.c            |  11 ++
 IMG_avif.c       | 332 +++++++++++++++++++++++++++++++++++++++++++++++
 Makefile.am      |   1 +
 Makefile.in      |  45 ++++---
 SDL_image.h      |  13 +-
 configure        | 278 +++++++++++++++++++++++++++++++++++++--
 configure.ac     |  58 +++++++++
 external/dav1d   |   1 +
 external/libavif |   1 +
 11 files changed, 712 insertions(+), 38 deletions(-)
 create mode 100644 IMG_avif.c
 create mode 160000 external/dav1d
 create mode 160000 external/libavif

diff --git a/.gitmodules b/.gitmodules
index 88ed07b..5b93a7f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -22,3 +22,11 @@
 	path = external/libjxl
 	url = https://github.com/libsdl-org/libjxl.git
 	branch = v0.6.1-SDL
+[submodule "external/libavif"]
+	path = external/libavif
+	url = https://github.com/libsdl-org/libavif.git
+	branch = v0.10.1-SDL
+[submodule "external/dav1d"]
+	path = external/dav1d
+	url = https://github.com/libsdl-org/dav1d.git
+	branch = 1.0.0-SDL
diff --git a/CHANGES.txt b/CHANGES.txt
index 7b1a1c4..70c3a90 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,6 @@
 2.0.6:
+Sam Lantinga - Mon May  9 12:42:05 PDT 2022
+ * Added support for AVIF images (https://github.com/AOMediaCodec/libavif)
 Sylvain Becker - Sun May  8 21:59:27 PDT 2022
  * Added IMG_ReadXPMFromArrayToRGB888()
 Sam Lantinga - Sun May  8 14:31:27 PDT 2022
diff --git a/IMG.c b/IMG.c
index 68b7992..e48ed24 100644
--- a/IMG.c
+++ b/IMG.c
@@ -58,6 +58,7 @@ static struct {
 } supported[] = {
     /* keep magicless formats first */
     { "TGA", NULL,      IMG_LoadTGA_RW },
+    { "AVIF",IMG_isAVIF,IMG_LoadAVIF_RW },
     { "CUR", IMG_isCUR, IMG_LoadCUR_RW },
     { "ICO", IMG_isICO, IMG_LoadICO_RW },
     { "BMP", IMG_isBMP, IMG_LoadBMP_RW },
@@ -94,6 +95,8 @@ const SDL_version *IMG_Linked_Version(void)
     return(&linked_version);
 }
 
+extern int IMG_InitAVIF(void);
+extern void IMG_QuitAVIF(void);
 extern int IMG_InitJPG(void);
 extern void IMG_QuitJPG(void);
 extern int IMG_InitJXL(void);
@@ -112,6 +115,11 @@ int IMG_Init(int flags)
 {
     int result = 0;
 
+    if (flags & IMG_INIT_AVIF) {
+        if ((initialized & IMG_INIT_AVIF) || IMG_InitAVIF() == 0) {
+            result |= IMG_INIT_AVIF;
+        }
+    }
     if (flags & IMG_INIT_JPG) {
         if ((initialized & IMG_INIT_JPG) || IMG_InitJPG() == 0) {
             result |= IMG_INIT_JPG;
@@ -144,6 +152,9 @@ int IMG_Init(int flags)
 
 void IMG_Quit()
 {
+    if (initialized & IMG_INIT_AVIF) {
+        IMG_QuitAVIF();
+    }
     if (initialized & IMG_INIT_JPG) {
         IMG_QuitJPG();
     }
diff --git a/IMG_avif.c b/IMG_avif.c
new file mode 100644
index 0000000..940850b
--- /dev/null
+++ b/IMG_avif.c
@@ -0,0 +1,332 @@
+/*
+  SDL_image:  An example image loading library for use with SDL
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+/* This is a AVIF image file loading framework */
+
+#include "SDL_image.h"
+
+#ifdef LOAD_AVIF
+
+#include <avif/avif.h>
+
+
+static struct {
+    int loaded;
+    void *handle;
+    avifBool (*avifPeekCompatibleFileType)(const avifROData * input);
+    avifDecoder * (*avifDecoderCreate)(void);
+    void (*avifDecoderDestroy)(avifDecoder * decoder);
+    void (*avifDecoderSetIO)(avifDecoder * decoder, avifIO * io);
+    avifResult (*avifDecoderParse)(avifDecoder * decoder);
+    avifResult (*avifDecoderNextImage)(avifDecoder * decoder);
+    avifResult (*avifImageYUVToRGB)(const avifImage * image, avifRGBImage * rgb);
+} lib;
+
+#ifdef LOAD_AVIF_DYNAMIC
+#define FUNCTION_LOADER(FUNC, SIG) \
+    lib.FUNC = (SIG) SDL_LoadFunction(lib.handle, #FUNC); \
+    if (lib.FUNC == NULL) { SDL_UnloadObject(lib.handle); return -1; }
+#else
+#define FUNCTION_LOADER(FUNC, SIG) \
+    lib.FUNC = FUNC; \
+    if (lib.FUNC == NULL) { IMG_SetError("Missing avif.framework"); return -1; }
+#endif
+
+int IMG_InitAVIF()
+{
+    if ( lib.loaded == 0 ) {
+#ifdef LOAD_AVIF_DYNAMIC
+        lib.handle = SDL_LoadObject(LOAD_AVIF_DYNAMIC);
+        if ( lib.handle == NULL ) {
+            return -1;
+        }
+#endif
+        FUNCTION_LOADER(avifPeekCompatibleFileType, avifBool (*)(const avifROData * input))
+        FUNCTION_LOADER(avifDecoderCreate, avifDecoder *(*)(void))
+        FUNCTION_LOADER(avifDecoderDestroy, void (*)(avifDecoder * decoder))
+        FUNCTION_LOADER(avifDecoderSetIO, void (*)(avifDecoder * decoder, avifIO * io))
+        FUNCTION_LOADER(avifDecoderParse, avifResult (*)(avifDecoder * decoder))
+        FUNCTION_LOADER(avifDecoderNextImage, avifResult (*)(avifDecoder * decoder))
+        FUNCTION_LOADER(avifImageYUVToRGB, avifResult (*)(const avifImage * image, avifRGBImage * rgb))
+    }
+    ++lib.loaded;
+
+    return 0;
+}
+void IMG_QuitAVIF()
+{
+    if ( lib.loaded == 0 ) {
+        return;
+    }
+    if ( lib.loaded == 1 ) {
+#ifdef LOAD_AVIF_DYNAMIC
+        SDL_UnloadObject(lib.handle);
+#endif
+    }
+    --lib.loaded;
+}
+
+static SDL_bool ReadAVIFHeader(SDL_RWops *src, Uint8 **header_data, size_t *header_size)
+{
+    Uint8 magic[16];
+    size_t size;
+    size_t read = 0;
+    Uint8 *data;
+
+    *header_data = NULL;
+    *header_size = 0;
+
+    if (!SDL_RWread(src, magic, 8, 1)) {
+        return SDL_FALSE;
+    }
+    read += 8;
+
+    if (SDL_memcmp(&magic[4], "ftyp", 4) != 0) {
+        return SDL_FALSE;
+    }
+
+    size = (((size_t)magic[0] << 24) |
+            ((size_t)magic[1] << 16) |
+            ((size_t)magic[2] << 8) |
+            ((size_t)magic[3] << 0));
+    if (size == 1) {
+        /* 64-bit header size */
+        if (!SDL_RWread(src, &magic[8], 8, 1)) {
+            return SDL_FALSE;
+        }
+        read += 8;
+
+        size = (((size_t)magic[8] << 56) |
+                ((size_t)magic[9] << 48) |
+                ((size_t)magic[10] << 40) |
+                ((size_t)magic[11] << 32) |
+                ((size_t)magic[12] << 24) |
+                ((size_t)magic[13] << 16) |
+                ((size_t)magic[14] << 8) |
+                ((size_t)magic[15] << 0));
+    }
+
+    if (size <= read) {
+        return SDL_FALSE;
+    }
+
+    /* Read in the header */
+    data = (Uint8 *)SDL_malloc(size);
+    if (!data) {
+        return SDL_FALSE;
+    }
+    SDL_memcpy(data, magic, read);
+
+    if (!SDL_RWread(src, &data[read], (size - read), 1)) {
+        SDL_free(data);
+        return SDL_FALSE;
+    }
+    *header_data = data;
+    *header_size = size;
+    return SDL_TRUE;
+}
+
+/* See if an image is contained in a data source */
+int IMG_isAVIF(SDL_RWops *src)
+{
+    Sint64 start;
+    int is_AVIF;
+    Uint8 *data;
+    size_t size;
+
+    if ( !src )
+        return 0;
+
+    start = SDL_RWtell(src);
+    is_AVIF = 0;
+    if (ReadAVIFHeader(src, &data, &size)) {
+        /* This might be AVIF, do more thorough checks */
+        if ((IMG_Init(IMG_INIT_AVIF) & IMG_INIT_AVIF) != 0) {
+            avifROData header;
+
+            header.data = data;
+            header.size = size;
+            is_AVIF = lib.avifPeekCompatibleFileType(&header);
+        }
+        SDL_free(data);
+    }
+    SDL_RWseek(src, start, RW_SEEK_SET);
+    return(is_AVIF);
+}
+
+/* Context for AFIF I/O operations */
+typedef struct
+{
+    SDL_RWops *src;
+    Uint64 start;
+    uint8_t *data;
+    size_t size;
+} avifIOContext;
+
+static avifResult ReadAVIFIO(struct avifIO * io, uint32_t readFlags, uint64_t offset, size_t size, avifROData * out)
+{
+    avifIOContext *context = (avifIOContext *)io->data;
+
+    /* The AVIF reader bounces all over, so always seek to the correct offset */
+    if (SDL_RWseek(context->src, context->start + offset, RW_SEEK_SET) < 0) {
+        return AVIF_RESULT_IO_ERROR;
+    }
+
+    if (size > context->size) {
+        uint8_t *data = (uint8_t *)SDL_realloc(context->data, size);
+        if (!data) {
+            return AVIF_RESULT_IO_ERROR;
+        }
+        context->data = data;
+        context->size = size; 
+    }
+
+    out->data = context->data;
+    out->size = SDL_RWread(context->src, context->data, 1, size);
+    if (out->size == 0) {
+        return AVIF_RESULT_IO_ERROR;
+    }
+
+    return AVIF_RESULT_OK;
+}
+
+static void DestroyAVIFIO(struct avifIO * io)
+{
+    avifIOContext *context = (avifIOContext *)io->data;
+
+    if (context->data) {
+        SDL_free(context->data);
+        context->data = NULL;
+    }
+}
+
+/* Load a AVIF type image from an SDL datasource */
+SDL_Surface *IMG_LoadAVIF_RW(SDL_RWops *src)
+{
+    Sint64 start;
+    avifDecoder *decoder = NULL;
+    avifIO io;
+    avifIOContext context;
+    avifRGBImage rgb;
+    avifResult result;
+    SDL_Surface *surface = NULL;
+
+    if (!src) {
+        /* The error message has been set in SDL_RWFromFile */
+        return NULL;
+    }
+    start = SDL_RWtell(src);
+
+    if ((IMG_Init(IMG_INIT_AVIF) & IMG_INIT_AVIF) == 0) {
+        return NULL;
+    }
+
+    SDL_zero(context);
+    SDL_zero(io);
+    SDL_zero(rgb);
+
+    decoder = lib.avifDecoderCreate();
+    if (!decoder) {
+        IMG_SetError("Couldn't create AVIF decoder");
+        goto done;
+    }
+
+    context.src = src;
+    context.start = start;
+    io.destroy = DestroyAVIFIO;
+    io.read = ReadAVIFIO;
+    io.data = &context;
+    lib.avifDecoderSetIO(decoder, &io);
+
+    result = lib.avifDecoderParse(decoder);
+    if (result != AVIF_RESULT_OK) {
+        IMG_SetError("Couldn't parse AVIF image: %d", result);
+        goto done;
+    }
+
+    result = lib.avifDecoderNextImage(decoder);
+    if (result != AVIF_RESULT_OK) {
+        IMG_SetError("Couldn't get AVIF image: %d", result);
+        goto done;
+    }
+
+    surface = SDL_CreateRGBSurfaceWithFormat(0, decoder->image->width, decoder->image->height, 0, SDL_PIXELFORMAT_ARGB8888);
+    if (!surface) {
+        goto done;
+    }
+
+    /* Convert the YUV image to RGB */
+    rgb.width = surface->w;
+    rgb.height = surface->h;
+    rgb.depth = 8;
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+    rgb.format = AVIF_RGB_FORMAT_BGRA;
+#else
+    rgb.format = AVIF_RGB_FORMAT_ARGB;
+#endif
+    rgb.pixels = (uint8_t *)surface->pixels;
+    rgb.rowBytes = (uint32_t)surface->pitch;
+    result = lib.avifImageYUVToRGB(decoder->image, &rgb);
+    if (result != AVIF_RESULT_OK) {
+        IMG_SetError("Couldn't convert AVIF image to RGB: %d", result);
+        SDL_FreeSurface(surface);
+        surface = NULL;
+        goto done;
+    }
+
+done:
+    if (decoder) {
+        lib.avifDecoderDestroy(decoder);
+    }
+    if (!surface) {
+        SDL_RWseek(src, start, RW_SEEK_SET);
+    }
+    return surface;
+}
+
+#else
+#if _MSC_VER >= 1300
+#pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */
+#endif
+
+int IMG_InitAVIF()
+{
+    IMG_SetError("AVIF images are not supported");
+    return(-1);
+}
+
+void IMG_QuitAVIF()
+{
+}
+
+/* See if an image is contained in a data source */
+int IMG_isAVIF(SDL_RWops *src)
+{
+    return(0);
+}
+
+/* Load a AVIF type image from an SDL datasource */
+SDL_Surface *IMG_LoadAVIF_RW(SDL_RWops *src)
+{
+    return(NULL);
+}
+
+#endif /* LOAD_AVIF */
diff --git a/Makefile.am b/Makefile.am
index 77a61a2..11b8a0a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -13,6 +13,7 @@ endif
 
 libSDL2_image_la_SOURCES =	\
 	IMG.c			\
+	IMG_avif.c		\
 	IMG_bmp.c		\
 	IMG_gif.c		\
 	IMG_jpg.c		\
diff --git a/Makefile.in b/Makefile.in
index 57b42f5..60e4b95 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -145,16 +145,17 @@ am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" \
 	"$(DESTDIR)$(libSDL2_imageincludedir)"
 LTLIBRARIES = $(lib_LTLIBRARIES)
 am__DEPENDENCIES_1 =
-am__libSDL2_image_la_SOURCES_DIST = IMG.c IMG_bmp.c IMG_gif.c \
-	IMG_jpg.c IMG_jxl.c IMG_lbm.c IMG_pcx.c IMG_png.c IMG_pnm.c \
-	IMG_qoi.c IMG_svg.c IMG_tga.c IMG_tif.c IMG_xcf.c IMG_xpm.c \
-	IMG_xv.c IMG_webp.c IMG_WIC.c IMG_ImageIO.m miniz.h nanosvg.h \
-	nanosvgrast.h qoi.h
+am__libSDL2_image_la_SOURCES_DIST = IMG.c IMG_avif.c IMG_bmp.c \
+	IMG_gif.c IMG_jpg.c IMG_jxl.c IMG_lbm.c IMG_pcx.c IMG_png.c \
+	IMG_pnm.c IMG_qoi.c IMG_svg.c IMG_tga.c IMG_tif.c IMG_xcf.c \
+	IMG_xpm.c IMG_xv.c IMG_webp.c IMG_WIC.c IMG_ImageIO.m miniz.h \
+	nanosvg.h nanosvgrast.h qoi.h
 @USE_IMAGEIO_TRUE@am__objects_1 = IMG_ImageIO.lo
-am_libSDL2_image_la_OBJECTS = IMG.lo IMG_bmp.lo IMG_gif.lo IMG_jpg.lo \
-	IMG_jxl.lo IMG_lbm.lo IMG_pcx.lo IMG_png.lo IMG_pnm.lo \
-	IMG_qoi.lo IMG_svg.lo IMG_tga.lo IMG_tif.lo IMG_xcf.lo \
-	IMG_xpm.lo IMG_xv.lo IMG_webp.lo IMG_WIC.lo $(am__objects_1)
+am_libSDL2_image_la_OBJECTS = IMG.lo IMG_avif.lo IMG_bmp.lo IMG_gif.lo \
+	IMG_jpg.lo IMG_jxl.lo IMG_lbm.lo IMG_pcx.lo IMG_png.lo \
+	IMG_pnm.lo IMG_qoi.lo IMG_svg.lo IMG_tga.lo IMG_tif.lo \
+	IMG_xcf.lo IMG_xpm.lo IMG_xv.lo IMG_webp.lo IMG_WIC.lo \
+	$(am__objects_1)
 libSDL2_image_la_OBJECTS = $(am_libSDL2_image_la_OBJECTS)
 AM_V_lt = $(am__v_lt_@AM_V@)
 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
@@ -185,16 +186,16 @@ DEFAULT_INCLUDES = -I.@am__isrc@
 depcomp = $(SHELL) $(top_srcdir)/depcomp
 am__maybe_remake_depfiles = depfiles
 am__depfiles_remade = ./$(DEPDIR)/IMG.Plo ./$(DEPDIR)/IMG_ImageIO.Plo \
-	./$(DEPDIR)/IMG_WIC.Plo ./$(DEPDIR)/IMG_bmp.Plo \
-	./$(DEPDIR)/IMG_gif.Plo ./$(DEPDIR)/IMG_jpg.Plo \
-	./$(DEPDIR)/IMG_jxl.Plo ./$(DEPDIR)/IMG_lbm.Plo \
-	./$(DEPDIR)/IMG_pcx.Plo ./$(DEPDIR)/IMG_png.Plo \
-	./$(DEPDIR)/IMG_pnm.Plo ./$(DEPDIR)/IMG_qoi.Plo \
-	./$(DEPDIR)/IMG_svg.Plo ./$(DEPDIR)/IMG_tga.Plo \
-	./$(DEPDIR)/IMG_tif.Plo ./$(DEPDIR)/IMG_webp.Plo \
-	./$(DEPDIR)/IMG_xcf.Plo ./$(DEPDIR)/IMG_xpm.Plo \
-	./$(DEPDIR)/IMG_xv.Plo ./$(DEPDIR)/showanim.Po \
-	./$(DEPDIR)/showimage.Po
+	./$(DEPDIR)/IMG_WIC.Plo ./$(DEPDIR)/IMG_avif.Plo \
+	./$(DEPDIR)/IMG_bmp.Plo ./$(DEPDIR)/IMG_gif.Plo \
+	./$(DEPDIR)/IMG_jpg.Plo ./$(DEPDIR)/IMG_jxl.Plo \
+	./$(DEPDIR)/IMG_lbm.Plo ./$(DEPDIR)/IMG_pcx.Plo \
+	./$(DEPDIR)/IMG_png.Plo ./$(DEPDIR)/IMG_pnm.Plo \
+	./$(DEPDIR)/IMG_qoi.Plo ./$(DEPDIR)/IMG_svg.Plo \
+	./$(DEPDIR)/IMG_tga.Plo ./$(DEPDIR)/IMG_tif.Plo \
+	./$(DEPDIR)/IMG_webp.Plo ./$(DEPDIR)/IMG_xcf.Plo \
+	./$(DEPDIR)/IMG_xpm.Plo ./$(DEPDIR)/IMG_xv.Plo \
+	./$(DEPDIR)/showanim.Po ./$(DEPDIR)/showimage.Po
 am__mv = mv -f
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
 	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -320,6 +321,8 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
+LIBAVIF_CFLAGS = @LIBAVIF_CFLAGS@
+LIBAVIF_LIBS = @LIBAVIF_LIBS@
 LIBJPEG_CFLAGS = @LIBJPEG_CFLAGS@
 LIBJPEG_LIBS = @LIBJPEG_LIBS@
 LIBJXL_CFLAGS = @LIBJXL_CFLAGS@
@@ -444,6 +447,7 @@ libSDL2_imageinclude_HEADERS = \
 @USE_IMAGEIO_TRUE@IMAGEIO_SOURCE = IMG_ImageIO.m
 libSDL2_image_la_SOURCES = \
 	IMG.c			\
+	IMG_avif.c		\
 	IMG_bmp.c		\
 	IMG_gif.c		\
 	IMG_jpg.c		\
@@ -598,6 +602,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IMG.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IMG_ImageIO.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IMG_WIC.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IMG_avif.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IMG_bmp.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IMG_gif.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IMG_jpg.Plo@am__quote@ # am--include-marker
@@ -996,6 +1001,7 @@ distclean: distclean-am
 		-rm -f ./$(DEPDIR)/IMG.Plo
 	-rm -f ./$(DEPDIR)/IMG_ImageIO.Plo
 	-rm -f ./$(DEPDIR)/IMG_WIC.Plo
+	-rm -f ./$(DEPDIR)/IMG_avif.Plo
 	-rm -f ./$(DEPDIR)/IMG_bmp.Plo
 	-rm -f ./$(DEPDIR)/IMG_gif.Plo
 	-rm -f ./$(DEPDIR)/IMG_jpg.Plo
@@ -1065,6 +1071,7 @@ maintainer-clean: maintainer-clean-am
 		-rm -f ./$(DEPDIR)/IMG.Plo
 	-rm -f ./$(DEPDIR)/IMG_ImageIO.Plo
 	-rm -f ./$(DEPDIR)/IMG_WIC.Plo
+	-rm -f ./$(DEPDIR)/IMG_avif.Plo
 	-rm -f ./$(DEPDIR)/IMG_bmp.Plo
 	-rm -f ./$(DEPDIR)/IMG_gif.Plo
 	-rm -f ./$(DEPDIR)/IMG_jpg.Plo
diff --git a/SDL_image.h b/SDL_image.h
index b03c752..3808cf5 100644
--- a/SDL_image.h
+++ b/SDL_image.h
@@ -79,11 +79,12 @@ extern DECLSPEC const SDL_version * SDLCALL IMG_Linked_Version(void);
 
 typedef enum
 {
-    IMG_INIT_JPG = 0x00000001,
-    IMG_INIT_PNG = 0x00000002,
-    IMG_INIT_TIF = 0x00000004,
-    IMG_INIT_WEBP = 0x00000008,
-    IMG_INIT_JXL = 0x00000010
+    IMG_INIT_JPG    = 0x00000001,
+    IMG_INIT_PNG    = 0x00000002,
+    IMG_INIT_TIF    = 0x00000004,
+    IMG_INIT_WEBP   = 0x00000008,
+    IMG_INIT_JXL    = 0x00000010,
+    IMG_INIT_AVIF   = 0x00000020
 } IMG_InitFlags;
 
 /* Loads dynamic libraries and prepares them for use.  Flags should be
@@ -117,6 +118,7 @@ extern DECLSPEC SDL_Texture * SDLCALL IMG_LoadTextureTyped_RW(SDL_Renderer *rend
 #endif /* SDL 2.0 */
 
 /* Functions to detect a file type, given a seekable source */
+extern DECLSPEC int SDLCALL IMG_isAVIF(SDL_RWops *src);
 extern DECLSPEC int SDLCALL IMG_isICO(SDL_RWops *src);
 extern DECLSPEC int SDLCALL IMG_isCUR(SDL_RWops *src);
 extern DECLSPEC int SDLCALL IMG_isBMP(SDL_RWops *src);
@@ -136,6 +138,7 @@ extern DECLSPEC int SDLCALL IMG_isXV(SDL_RWops *src);
 extern DECLSPEC int SDLCALL IMG_isWEBP(SDL_RWops *src);
 
 /* Individual loading functions */
+extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadAVIF_RW(SDL_RWops *src);
 extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadICO_RW(SDL_RWops *src);
 extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadCUR_RW(SDL_RWops *src);
 extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadBMP_RW(SDL_RWops *src);
diff --git a/configure b/configure
index 32a8543..0b4e874 100755
--- a/configure
+++ b/configure
@@ -788,6 +788,8 @@ LIBJXL_LIBS
 LIBJXL_CFLAGS
 LIBJPEG_LIBS
 LIBJPEG_CFLAGS
+LIBAVIF_LIBS
+LIBAVIF_CFLAGS
 SDL2_CONFIG
 SDL_LIBS
 SDL_CFLAGS
@@ -941,6 +943,8 @@ enable_libtool_lock
 enable_dependency_tracking
 enable_silent_rules
 enable_imageio
+enable_avif
+enable_avif_shared
 enable_bmp
 enable_gif
 enable_jpg
@@ -983,6 +987,8 @@ PKG_CONFIG_PATH
 PKG_CONFIG_LIBDIR
 SDL_CFLAGS
 SDL_LIBS
+LIBAVIF_CFLAGS
+LIBAVIF_LIBS
 LIBJPEG_CFLAGS
 LIBJPEG_LIBS
 LIBJXL_CFLAGS
@@ -1624,6 +1630,8 @@ Optional Features:
   --disable-silent-rules  verbose build output (undo: "make V=0")
   --enable-imageio        use native Mac OS X frameworks for loading images
                           [default=yes]
+  --enable-avif           support loading AVIF images [default=yes]
+  --enable-avif-shared    dynamically load AVIF support [default=yes]
   --enable-bmp            support loading BMP/ICO/CUR images [default=yes]
   --enable-gif            support loading GIF images [default=yes]
   --enable-jpg            support loading JPG images [default=yes]
@@ -1676,6 +1684,10 @@ Some influential environment variables:
               path overriding pkg-config's built-in search path
   SDL_CFLAGS  C compiler flags for SDL, overriding pkg-config
   SDL_LIBS    linker flags for SDL, overriding pkg-config
+  LIBAVIF_CFLAGS
+              C compiler flags for LIBAVIF, overriding pkg-config
+  LIBAVIF_LIBS
+              linker flags for LIBAVIF, overriding pkg-config
   LIBJPEG_CFLAGS
               C compiler flags for LIBJPEG, overriding pkg-config
   LIBJPEG_LIBS
@@ -4096,13 +4108,13 @@ if ${lt_cv_nm_interface+:} false; then :
 else
   lt_cv_nm_interface="BSD nm"
   echo "int some_variable = 0;" > conftest.$ac_ext
-  (eval echo "\"\$as_me:4099: $ac_compile\"" >&5)
+  (eval echo "\"\$as_me:4111: $ac_compile\"" >&5)
   (eval "$ac_compile" 2>conftest.err)
   cat conftest.err >&5
-  (eval echo "\"\$as_me:4102: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+  (eval echo "\"\$as_me:4114: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
   (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
   cat conftest.err >&5
-  (eval echo "\"\$as_me:4105: output\"" >&5)
+  (eval echo "\"\$as_me:4117: output\"" >&5)
   cat conftest.out >&5
   if $GREP 'External.*some_variable' conftest.out > /dev/null; then
     lt_cv_nm_interface="MS dumpbin"
@@ -5316,7 +5328,7 @@ ia64-*-hpux*)
   ;;
 *-*-irix6*)
   # Find out which ABI we are using.
-  echo '#line 5319 "configure"' > conftest.$ac_ext
+  echo '#line 5331 "configure"' > conftest.$ac_ext
   if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
   (eval $ac_compile) 2>&5
   ac_status=$?
@@ -7141,11 +7153,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:7144: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:7156: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:7148: \$? = $ac_status" >&5
+   echo "$as_me:7160: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -7490,11 +7502,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:7493: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:7505: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:7497: \$? = $ac_status" >&5
+   echo "$as_me:7509: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -7595,11 +7607,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:7598: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:7610: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:7602: \$? = $ac_status" >&5
+   echo "$as_me:7614: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -7650,11 +7662,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:7653: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:7665: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:7657: \$? = $ac_status" >&5
+   echo "$as_me:7669: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -10085,7 +10097,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 10088 "configure"
+#line 10100 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -10181,7 +10193,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 10184 "configure"
+#line 10196 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12783,6 +12795,20 @@ $as_echo "$have_gcc_fvisibility" >&6; }
     fi
 }
 
+# Check whether --enable-avif was given.
+if test "${enable_avif+set}" = set; then :
+  enableval=$enable_avif;
+else
+  enable_avif=yes
+fi
+
+# Check whether --enable-avif-shared was given.
+if test "${enable_avif_shared+set}" = set; then :
+  enableval=$enable_avif_shared;
+else
+  enable_avif_shared=yes
+fi
+
 # Check whether --enable-bmp was given.
 if test "${enable_bmp+set}" = set; then :
   enableval=$enable_bmp;
@@ -13262,6 +13288,213 @@ rm -f core conftest.err conftest.$ac_objext \
 CFLAGS="$CFLAGS $SDL_CFLAGS"
 LIBS="$LIBS $SDL_LIBS"
 
+if test x$enable_avif = xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libavif" >&5
+$as_echo_n "checking for libavif... " >&6; }
+
+if test -n "$LIBAVIF_CFLAGS"; then
+    pkg_cv_LIBAVIF_CFLAGS="$LIBAVIF_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libavif\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libavif") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_LIBAVIF_CFLAGS=`$PKG_CONFIG --cflags "libavif" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$LIBAVIF_LIBS"; then
+    pkg_cv_LIBAVIF_LIBS="$LIBAVIF_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libavif\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libavif") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_LIBAVIF_LIBS=`$PKG_CONFIG --libs "libavif" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+	        LIBAVIF_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libavif" 2>&1`
+        else
+	        LIBAVIF_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libavif" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$LIBAVIF_PKG_ERRORS" >&5
+
+	        ac_fn_c_check_header_mongrel "$LINENO" "avif/avif.h" "ac_cv_header_avif_avif_h" "$ac_includes_default"
+if test "x$ac_cv_header_avif_avif_h" = xyes; then :
+
+            have_avif_hdr=yes
+            LIBAVIF_CFLAGS=""
+
+fi
+
+
+        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for avifVersion in -lavif" >&5
+$as_echo_n "checking for avifVersion in -lavif... " >&6; }
+if ${ac_cv_lib_avif_avifVersion+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lavif  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char avifVersion ();
+int
+main ()
+{
+return avifVersion ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_avif_avifVersion=yes
+else
+  ac_cv_lib_avif_avifVersion=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_avif_avifVersion" >&5
+$as_echo "$ac_cv_lib_avif_avifVersion" >&6; }
+if test "x$ac_cv_lib_avif_avifVersion" = xyes; then :
+
+            have_avif_lib=yes
+            LIBAVIF_LIBS="-lavif"
+
+fi
+
+
+elif test $pkg_failed = untried; then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+	        ac_fn_c_check_header_mongrel "$LINENO" "avif/avif.h" "ac_cv_header_avif_avif_h" "$ac_includes_default"
+if test "x$ac_cv_header_avif_avif_h" = xyes; then :
+
+            have_avif_hdr=yes
+            LIBAVIF_CFLAGS=""
+
+fi
+
+
+        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for avifVersion in -lavif" >&5
+$as_echo_n "checking for avifVersion in -lavif... " >&6; }
+if ${ac_cv_lib_avif_avifVersion+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lavif  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char avifVersion ();
+int
+main ()
+{
+return avifVersion ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_avif_avifVersion=yes
+else
+  ac_cv_lib_avif_avifVersion=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_avif_avifVersion" >&5
+$as_echo "$ac_cv_lib_avif_avifVersion" >&6; }
+if test "x$ac_cv_lib_avif_avifVersion" = xyes; then :
+
+            have_avif_lib=yes
+            LIBAVIF_LIBS="-lavif"
+
+fi
+
+
+else
+	LIBAVIF_CFLAGS=$pkg_cv_LIBAVIF_CFLAGS
+	LIBAVIF_LIBS=$pkg_cv_LIBAVIF_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+	        have_avif_hdr=yes
+        have_avif_lib=yes
+        have_avif_pc=yes
+
+fi
+    if test x$have_avif_hdr = xyes -a x$have_avif_lib = xyes; then
+        if test x$enable_avif = xyes; then
+            $as_echo "#define LOAD_AVIF 1" >>confdefs.h
+
+        fi
+
+        case "$host" in
+            *-*-darwin*)
+                avif_lib=`find_lib libavif.dylib`
+                ;;
+            *-*-cygwin* | *-*-mingw*)
+                avif_lib=`find_lib "libavif*.dll"`
+                ;;
+            *)
+                avif_lib=`find_lib "libavif[0-9]*.so.*"`
+                if test x$avif_lib = x; then
+                    avif_lib=`find_lib "libavif.so.*"`
+                fi
+                ;;
+        esac
+    elif test x$enable_avif = xyes; then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** Unable to find AVIF library (https://github.com/AOMediaCodec/libavif)" >&5
+$as_echo "$as_me: WARNING: *** Unable to find AVIF library (https://github.com/AOMediaCodec/libavif)" >&2;}
+        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: AVIF image loading disabled" >&5
+$as_echo "$as_me: WARNING: AVIF image loading disabled" >&2;}
+    fi
+fi
+
 if (test x$enable_jpg = xyes || test x$enable_tif = xyes) && test x$enable_imageio != xyes; then
 
 pkg_failed=no
@@ -14373,6 +14606,23 @@ _ACEOF
     fi
 fi
 
+if test x$enable_avif = xyes -a x$have_avif_hdr = xyes -a x$have_avif_lib = xyes; then
+    CFLAGS="$LIBAVIF_CFLAGS $CFLAGS

(Patch may be truncated, please check the link at the top of this post.)