SDL_image: Added support for JXL images (https://jpegxl.info/)

From 742cd132b18c3612e00f5f7d47bc24dbae2e76f9 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 8 May 2022 14:35:18 -0700
Subject: [PATCH] Added support for JXL images (https://jpegxl.info/)

Fixes https://github.com/libsdl-org/SDL_image/issues/204
---
 .gitmodules  |   4 +
 CHANGES.txt  |   2 +
 IMG.c        |  11 ++
 IMG_jxl.c    | 263 ++++++++++++++++++++++++++++++++++++++++++++++++
 Makefile.am  |   1 +
 Makefile.in  |  33 +++---
 SDL_image.h  |   5 +-
 configure    | 277 ++++++++++++++++++++++++++++++++++++++++++++++++---
 configure.ac |  58 +++++++++++
 9 files changed, 626 insertions(+), 28 deletions(-)
 create mode 100644 IMG_jxl.c

diff --git a/.gitmodules b/.gitmodules
index 2303971c..88ed07bc 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -18,3 +18,7 @@
 	path = external/zlib
 	url = https://github.com/libsdl-org/zlib.git
 	branch = v1.2.12-SDL
+[submodule "external/libjxl"]
+	path = external/libjxl
+	url = https://github.com/libsdl-org/libjxl.git
+	branch = v0.6.1-SDL
diff --git a/CHANGES.txt b/CHANGES.txt
index 3bddcc79..728df2bc 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,6 @@
 2.0.6:
+Sam Lantinga - Sun May  8 14:31:27 PDT 2022
+ * Added support for JXL images (https://jpegxl.info/)
 Thomas Bernard - Mon Oct 26 23:32:10 2020
  * Fixed XCF regression introduced in 2.0.5
 Sam Lantinga - Thu Oct  3 18:09:17 PDT 2019
diff --git a/IMG.c b/IMG.c
index 456c6ef4..68b79921 100644
--- a/IMG.c
+++ b/IMG.c
@@ -63,6 +63,7 @@ static struct {
     { "BMP", IMG_isBMP, IMG_LoadBMP_RW },
     { "GIF", IMG_isGIF, IMG_LoadGIF_RW },
     { "JPG", IMG_isJPG, IMG_LoadJPG_RW },
+    { "JXL", IMG_isJXL, IMG_LoadJXL_RW },
     { "LBM", IMG_isLBM, IMG_LoadLBM_RW },
     { "PCX", IMG_isPCX, IMG_LoadPCX_RW },
     { "PNG", IMG_isPNG, IMG_LoadPNG_RW },
@@ -95,6 +96,8 @@ const SDL_version *IMG_Linked_Version(void)
 
 extern int IMG_InitJPG(void);
 extern void IMG_QuitJPG(void);
+extern int IMG_InitJXL(void);
+extern void IMG_QuitJXL(void);
 extern int IMG_InitPNG(void);
 extern void IMG_QuitPNG(void);
 extern int IMG_InitTIF(void);
@@ -114,6 +117,11 @@ int IMG_Init(int flags)
             result |= IMG_INIT_JPG;
         }
     }
+    if (flags & IMG_INIT_JXL) {
+        if ((initialized & IMG_INIT_JXL) || IMG_InitJXL() == 0) {
+            result |= IMG_INIT_JXL;
+        }
+    }
     if (flags & IMG_INIT_PNG) {
         if ((initialized & IMG_INIT_PNG) || IMG_InitPNG() == 0) {
             result |= IMG_INIT_PNG;
@@ -139,6 +147,9 @@ void IMG_Quit()
     if (initialized & IMG_INIT_JPG) {
         IMG_QuitJPG();
     }
+    if (initialized & IMG_INIT_JXL) {
+        IMG_QuitJXL();
+    }
     if (initialized & IMG_INIT_PNG) {
         IMG_QuitPNG();
     }
diff --git a/IMG_jxl.c b/IMG_jxl.c
new file mode 100644
index 00000000..0e51d3e9
--- /dev/null
+++ b/IMG_jxl.c
@@ -0,0 +1,263 @@
+/*
+  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 JXL image file loading framework */
+
+#include "SDL_image.h"
+
+#ifdef LOAD_JXL
+
+#include <jxl/decode.h>
+
+
+static struct {
+    int loaded;
+    void *handle;
+    JxlDecoder* (*JxlDecoderCreate)(const JxlMemoryManager* memory_manager);
+    JxlDecoderStatus (*JxlDecoderSubscribeEvents)(JxlDecoder* dec, int events_wanted);
+    JxlDecoderStatus (*JxlDecoderSetInput)(JxlDecoder* dec, const uint8_t* data, size_t size);
+    JxlDecoderStatus (*JxlDecoderProcessInput)(JxlDecoder* dec);
+    JxlDecoderStatus (*JxlDecoderGetBasicInfo)(const JxlDecoder* dec, JxlBasicInfo* info);
+    JxlDecoderStatus (*JxlDecoderImageOutBufferSize)(const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size);
+    JxlDecoderStatus (*JxlDecoderSetImageOutBuffer)(JxlDecoder* dec, const JxlPixelFormat* format, void* buffer, size_t size);
+    void (*JxlDecoderDestroy)(JxlDecoder* dec);
+} lib;
+
+#ifdef LOAD_JXL_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;
+#endif
+
+int IMG_InitJXL()
+{
+    if ( lib.loaded == 0 ) {
+#ifdef LOAD_JXL_DYNAMIC
+        lib.handle = SDL_LoadObject(LOAD_JXL_DYNAMIC);
+        if ( lib.handle == NULL ) {
+            return -1;
+        }
+#endif
+        FUNCTION_LOADER(JxlDecoderCreate, JxlDecoder* (*)(const JxlMemoryManager* memory_manager))
+        FUNCTION_LOADER(JxlDecoderSubscribeEvents, JxlDecoderStatus (*)(JxlDecoder* dec, int events_wanted))
+        FUNCTION_LOADER(JxlDecoderSetInput, JxlDecoderStatus (*)(JxlDecoder* dec, const uint8_t* data, size_t size))
+        FUNCTION_LOADER(JxlDecoderProcessInput, JxlDecoderStatus (*)(JxlDecoder* dec))
+        FUNCTION_LOADER(JxlDecoderGetBasicInfo, JxlDecoderStatus (*)(const JxlDecoder* dec, JxlBasicInfo* info))
+        FUNCTION_LOADER(JxlDecoderImageOutBufferSize, JxlDecoderStatus (*)(const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size))
+        FUNCTION_LOADER(JxlDecoderSetImageOutBuffer, JxlDecoderStatus (*)(JxlDecoder* dec, const JxlPixelFormat* format, void* buffer, size_t size))
+        FUNCTION_LOADER(JxlDecoderDestroy, void (*)(JxlDecoder* dec))
+    }
+    ++lib.loaded;
+
+    return 0;
+}
+void IMG_QuitJXL()
+{
+    if ( lib.loaded == 0 ) {
+        return;
+    }
+    if ( lib.loaded == 1 ) {
+#ifdef LOAD_JXL_DYNAMIC
+        SDL_UnloadObject(lib.handle);
+#endif
+    }
+    --lib.loaded;
+}
+
+/* See if an image is contained in a data source */
+int IMG_isJXL(SDL_RWops *src)
+{
+    Sint64 start;
+    int is_JXL;
+    Uint8 magic[12];
+
+    if ( !src )
+        return 0;
+    start = SDL_RWtell(src);
+    is_JXL = 0;
+    if ( SDL_RWread(src, magic, 2, 1) ) {
+        if ( magic[0] == 0xFF && magic[1] == 0x0A ) {
+            /* This is a JXL codestream */
+            is_JXL = 1;
+        } else {
+            if ( SDL_RWread(src, &magic[2], sizeof(magic) - 2, 1) ) {
+                if ( magic[0] == 0x00 && magic[1] == 0x00 &&
+                     magic[2] == 0x00 && magic[3] == 0x0C &&
+                     magic[4] == 'J' && magic[5] == 'X' &&
+                     magic[6] == 'L' && magic[7] == ' ' &&
+                     magic[8] == 0x0D && magic[9] == 0x0A &&
+                     magic[10] == 0x87 && magic[11] == 0x0A ) {
+                    /* This is a JXL container */
+                    is_JXL = 1;
+                }
+            }
+        }
+    }
+    SDL_RWseek(src, start, RW_SEEK_SET);
+    return(is_JXL);
+}
+
+/* Load a JXL type image from an SDL datasource */
+SDL_Surface *IMG_LoadJXL_RW(SDL_RWops *src)
+{
+    Sint64 start;
+    unsigned char *data;
+    size_t datasize;
+    JxlDecoder *decoder = NULL;
+    JxlBasicInfo info;
+    JxlPixelFormat format = { 4, JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0 };
+    size_t outputsize;
+    void *pixels = NULL;
+    int pitch = 0;
+    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_JXL) & IMG_INIT_JXL) == 0) {
+        return NULL;
+    }
+
+    data = (unsigned char *)SDL_LoadFile_RW(src, &datasize, SDL_FALSE);
+    if (!data) {
+        return NULL;
+    }
+
+    decoder = lib.JxlDecoderCreate(NULL);
+    if (!decoder) {
+        IMG_SetError("Couldn't create JXL decoder");
+        goto done;
+    }
+
+    if (lib.JxlDecoderSubscribeEvents(decoder, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE) != JXL_DEC_SUCCESS) {
+        IMG_SetError("Couldn't subscribe to JXL events");
+        goto done;
+    }
+
+    if (lib.JxlDecoderSetInput(decoder, data, datasize) != JXL_DEC_SUCCESS) {
+        IMG_SetError("Couldn't set JXL input");
+        goto done;
+    }
+
+    SDL_zero(info);
+
+    for ( ; ; ) {
+        JxlDecoderStatus status = lib.JxlDecoderProcessInput(decoder);
+
+        switch (status ) {
+        case JXL_DEC_ERROR:
+            IMG_SetError("JXL decoder error");
+            goto done;
+        case JXL_DEC_NEED_MORE_INPUT:
+            IMG_SetError("Incomplete JXL image");
+            goto done;
+        case JXL_DEC_BASIC_INFO:
+            if (lib.JxlDecoderGetBasicInfo(decoder, &info) != JXL_DEC_SUCCESS) {
+                IMG_SetError("Couldn't get JXL image info");
+                goto done;
+            }
+            break;
+        case JXL_DEC_NEED_IMAGE_OUT_BUFFER:
+            if (lib.JxlDecoderImageOutBufferSize(decoder, &format, &outputsize) != JXL_DEC_SUCCESS) {
+                IMG_SetError("Couldn't get JXL image size");
+                goto done;
+            }
+            if (info.xsize == 0 || info.ysize == 0) {
+                IMG_SetError("Couldn't get pixels for %dx%d JXL image", info.xsize, info.ysize);
+                goto done;
+            }
+            if (pixels) {
+                SDL_free(pixels);
+            }
+            pixels = SDL_malloc(outputsize);
+            if (!pixels) {
+                SDL_OutOfMemory();
+                goto done;
+            }
+            pitch = outputsize / info.ysize;
+            if (lib.JxlDecoderSetImageOutBuffer(decoder, &format, pixels, outputsize) != JXL_DEC_SUCCESS) {
+                IMG_SetError("Couldn't set JXL output buffer");
+                goto done;
+            }
+            break;
+        case JXL_DEC_FULL_IMAGE:
+            /* We have a full image - in the case of an animation, keep decoding until the last frame */
+            break;
+        case JXL_DEC_SUCCESS:
+            /* All done! */
+            surface = SDL_CreateRGBSurfaceWithFormatFrom(pixels, info.xsize, info.ysize, 0, pitch, SDL_PIXELFORMAT_RGBA32);
+            goto done;
+        default:
+            IMG_SetError("Unknown JXL decoding status: %d", status);
+            goto done;
+        }
+    }
+
+done:
+    if (decoder) {
+        lib.JxlDecoderDestroy(decoder);
+    }
+    if (data) {
+        SDL_free(data);
+    }
+    if (pixels) {
+        SDL_free(pixels);
+    }
+    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_InitJXL()
+{
+    IMG_SetError("JXL images are not supported");
+    return(-1);
+}
+
+void IMG_QuitJXL()
+{
+}
+
+/* See if an image is contained in a data source */
+int IMG_isJXL(SDL_RWops *src)
+{
+    return(0);
+}
+
+/* Load a JXL type image from an SDL datasource */
+SDL_Surface *IMG_LoadJXL_RW(SDL_RWops *src)
+{
+    return(NULL);
+}
+
+#endif /* LOAD_JXL */
diff --git a/Makefile.am b/Makefile.am
index c57491a4..77a61a27 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -16,6 +16,7 @@ libSDL2_image_la_SOURCES =	\
 	IMG_bmp.c		\
 	IMG_gif.c		\
 	IMG_jpg.c		\
+	IMG_jxl.c		\
 	IMG_lbm.c		\
 	IMG_pcx.c		\
 	IMG_png.c		\
diff --git a/Makefile.in b/Makefile.in
index 90d20a7f..57b42f54 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -146,15 +146,15 @@ am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" \
 LTLIBRARIES = $(lib_LTLIBRARIES)
 am__DEPENDENCIES_1 =
 am__libSDL2_image_la_SOURCES_DIST = IMG.c IMG_bmp.c IMG_gif.c \
-	IMG_jpg.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 \
+	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_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)
+	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@)
@@ -187,13 +187,14 @@ 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_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_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)
@@ -321,6 +322,8 @@ LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBJPEG_CFLAGS = @LIBJPEG_CFLAGS@
 LIBJPEG_LIBS = @LIBJPEG_LIBS@
+LIBJXL_CFLAGS = @LIBJXL_CFLAGS@
+LIBJXL_LIBS = @LIBJXL_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
 LIBPNG_LIBS = @LIBPNG_LIBS@
@@ -444,6 +447,7 @@ libSDL2_image_la_SOURCES = \
 	IMG_bmp.c		\
 	IMG_gif.c		\
 	IMG_jpg.c		\
+	IMG_jxl.c		\
 	IMG_lbm.c		\
 	IMG_pcx.c		\
 	IMG_png.c		\
@@ -597,6 +601,7 @@ distclean-compile:
 @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
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IMG_jxl.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IMG_lbm.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IMG_pcx.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IMG_png.Plo@am__quote@ # am--include-marker
@@ -994,6 +999,7 @@ distclean: distclean-am
 	-rm -f ./$(DEPDIR)/IMG_bmp.Plo
 	-rm -f ./$(DEPDIR)/IMG_gif.Plo
 	-rm -f ./$(DEPDIR)/IMG_jpg.Plo
+	-rm -f ./$(DEPDIR)/IMG_jxl.Plo
 	-rm -f ./$(DEPDIR)/IMG_lbm.Plo
 	-rm -f ./$(DEPDIR)/IMG_pcx.Plo
 	-rm -f ./$(DEPDIR)/IMG_png.Plo
@@ -1062,6 +1068,7 @@ maintainer-clean: maintainer-clean-am
 	-rm -f ./$(DEPDIR)/IMG_bmp.Plo
 	-rm -f ./$(DEPDIR)/IMG_gif.Plo
 	-rm -f ./$(DEPDIR)/IMG_jpg.Plo
+	-rm -f ./$(DEPDIR)/IMG_jxl.Plo
 	-rm -f ./$(DEPDIR)/IMG_lbm.Plo
 	-rm -f ./$(DEPDIR)/IMG_pcx.Plo
 	-rm -f ./$(DEPDIR)/IMG_png.Plo
diff --git a/SDL_image.h b/SDL_image.h
index 4af45d31..3834e27b 100644
--- a/SDL_image.h
+++ b/SDL_image.h
@@ -82,7 +82,8 @@ typedef enum
     IMG_INIT_JPG = 0x00000001,
     IMG_INIT_PNG = 0x00000002,
     IMG_INIT_TIF = 0x00000004,
-    IMG_INIT_WEBP = 0x00000008
+    IMG_INIT_WEBP = 0x00000008,
+    IMG_INIT_JXL = 0x00000010
 } IMG_InitFlags;
 
 /* Loads dynamic libraries and prepares them for use.  Flags should be
@@ -121,6 +122,7 @@ extern DECLSPEC int SDLCALL IMG_isCUR(SDL_RWops *src);
 extern DECLSPEC int SDLCALL IMG_isBMP(SDL_RWops *src);
 extern DECLSPEC int SDLCALL IMG_isGIF(SDL_RWops *src);
 extern DECLSPEC int SDLCALL IMG_isJPG(SDL_RWops *src);
+extern DECLSPEC int SDLCALL IMG_isJXL(SDL_RWops *src);
 extern DECLSPEC int SDLCALL IMG_isLBM(SDL_RWops *src);
 extern DECLSPEC int SDLCALL IMG_isPCX(SDL_RWops *src);
 extern DECLSPEC int SDLCALL IMG_isPNG(SDL_RWops *src);
@@ -139,6 +141,7 @@ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadCUR_RW(SDL_RWops *src);
 extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadBMP_RW(SDL_RWops *src);
 extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadGIF_RW(SDL_RWops *src);
 extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadJPG_RW(SDL_RWops *src);
+extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadJXL_RW(SDL_RWops *src);
 extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadLBM_RW(SDL_RWops *src);
 extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadPCX_RW(SDL_RWops *src);
 extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadPNG_RW(SDL_RWops *src);
diff --git a/configure b/configure
index 68e9800b..32a85438 100755
--- a/configure
+++ b/configure
@@ -784,6 +784,8 @@ LIBTIFF_LIBS
 LIBTIFF_CFLAGS
 LIBPNG_LIBS
 LIBPNG_CFLAGS
+LIBJXL_LIBS
+LIBJXL_CFLAGS
 LIBJPEG_LIBS
 LIBJPEG_CFLAGS
 SDL2_CONFIG
@@ -943,6 +945,8 @@ enable_bmp
 enable_gif
 enable_jpg
 enable_jpg_shared
+enable_jxl
+enable_jxl_shared
 enable_lbm
 enable_pcx
 enable_png
@@ -981,6 +985,8 @@ SDL_CFLAGS
 SDL_LIBS
 LIBJPEG_CFLAGS
 LIBJPEG_LIBS
+LIBJXL_CFLAGS
+LIBJXL_LIBS
 LIBPNG_CFLAGS
 LIBPNG_LIBS
 LIBTIFF_CFLAGS
@@ -1622,6 +1628,8 @@ Optional Features:
   --enable-gif            support loading GIF images [default=yes]
   --enable-jpg            support loading JPG images [default=yes]
   --enable-jpg-shared     dynamically load JPG support [default=yes]
+  --enable-jxl            support loading JXL images [default=yes]
+  --enable-jxl-shared     dynamically load JXL support [default=yes]
   --enable-lbm            support loading LBM images [default=yes]
   --enable-pcx            support loading PCX images [default=yes]
   --enable-png            support loading PNG images [default=yes]
@@ -1672,6 +1680,9 @@ Some influential environment variables:
               C compiler flags for LIBJPEG, overriding pkg-config
   LIBJPEG_LIBS
               linker flags for LIBJPEG, overriding pkg-config
+  LIBJXL_CFLAGS
+              C compiler flags for LIBJXL, overriding pkg-config
+  LIBJXL_LIBS linker flags for LIBJXL, overriding pkg-config
   LIBPNG_CFLAGS
               C compiler flags for LIBPNG, overriding pkg-config
   LIBPNG_LIBS linker flags for LIBPNG, overriding pkg-config
@@ -4085,13 +4096,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:4088: $ac_compile\"" >&5)
+  (eval echo "\"\$as_me:4099: $ac_compile\"" >&5)
   (eval "$ac_compile" 2>conftest.err)
   cat conftest.err >&5
-  (eval echo "\"\$as_me:4091: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+  (eval echo "\"\$as_me:4102: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
   (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
   cat conftest.err >&5
-  (eval echo "\"\$as_me:4094: output\"" >&5)
+  (eval echo "\"\$as_me:4105: output\"" >&5)
   cat conftest.out >&5
   if $GREP 'External.*some_variable' conftest.out > /dev/null; then
     lt_cv_nm_interface="MS dumpbin"
@@ -5305,7 +5316,7 @@ ia64-*-hpux*)
   ;;
 *-*-irix6*)
   # Find out which ABI we are using.
-  echo '#line 5308 "configure"' > conftest.$ac_ext
+  echo '#line 5319 "configure"' > conftest.$ac_ext
   if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
   (eval $ac_compile) 2>&5
   ac_status=$?
@@ -7130,11 +7141,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:7133: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:7144: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:7137: \$? = $ac_status" >&5
+   echo "$as_me:7148: \$? = $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.
@@ -7479,11 +7490,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:7482: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:7493: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:7486: \$? = $ac_status" >&5
+   echo "$as_me:7497: \$? = $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.
@@ -7584,11 +7595,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:7587: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:7598: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:7591: \$? = $ac_status" >&5
+   echo "$as_me:7602: \$? = $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
@@ -7639,11 +7650,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:7642: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:7653: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:7646: \$? = $ac_status" >&5
+   echo "$as_me:7657: \$? = $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
@@ -10074,7 +10085,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 10077 "configure"
+#line 10088 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -10170,7 +10181,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 10173 "configure"
+#line 10184 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12800,6 +12811,20 @@ else
   enable_jpg_shared=yes
 fi
 
+# Check whether --enable-jxl was given.
+if test "${enable_jxl+set}" = set; then :
+  enableval=$enable_jxl;
+else
+  enable_jxl=yes
+fi
+
+# Check whether --enable-jxl-shared was given.
+if test "${enable_jxl_shared+set}" = set; then :
+  enableval=$enable_jxl_shared;
+else
+  enable_jxl_shared=yes
+fi
+
 # Check whether --enable-lbm was given.
 if test "${enable_lbm+set}" = set; then :
   enableval=$enable_lbm;
@@ -13444,6 +13469,213 @@ $as_echo "$as_me: WARNING: JPG image loading disabled" >&2;}
     fi
 fi
 
+if test x$enable_jxl = xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libjxl" >&5
+$as_echo_n "checking for libjxl... " >&6; }
+
+if test -n "$LIBJXL_CFLAGS"; then
+    pkg_cv_LIBJXL_CFLAGS="$LIBJXL_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libjxl\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libjxl") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_LIBJXL_CFLAGS=`$PKG_CONFIG --cflags "libjxl" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$LIBJXL_LIBS"; then
+    pkg_cv_LIBJXL_LIBS="$LIBJXL_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libjxl\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libjxl") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_LIBJXL_LIBS=`$PKG_CONFIG --libs "libjxl" 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
+	        LIBJXL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libjxl" 2>&1`
+        else
+	        LIBJXL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libjxl" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$LIBJXL_PKG_ERRORS" >&5
+
+	        ac_fn_c_check_header_mongrel "$LINENO" "jxl/decode.h" "ac_cv_header_jxl_decode_h" "$ac_includes_default"
+if test "x$ac_cv_header_jxl_decode_h" = xyes; then :
+
+            have_jxl_hdr=yes
+            LIBJXL_CFLAGS=""
+
+fi
+
+
+        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for JxlSignatureCheck in -ljxl" >&5
+$as_echo_n "checking for JxlSignatureCheck in -ljxl... " >&6; }
+if ${ac_cv_lib_jxl_JxlSignatureCheck+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ljxl  $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 JxlSignatureCheck ();
+int
+main ()
+{
+return JxlSignatureCheck ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_jxl_JxlSignatureCheck=yes
+else
+  ac_cv_lib_jxl_JxlSignatureCheck=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_jxl_JxlSignatureCheck" >&5
+$as_echo "$ac_cv_lib_jxl_JxlSignatureCheck" >&6; }
+if test "x$ac_cv_lib_jxl_JxlSignatureCheck" = xyes; then :
+
+            have_jxl_lib=yes
+            LIBJXL_LIBS="-ljxl"
+
+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" "jxl/decode.h" "ac_cv_header_jxl_decode_h" "$ac_includes_default"
+if test "x$ac_cv_header_jxl_decode_h" = xyes; then :
+
+            have_jxl_hdr=yes
+            LIBJXL_CFLAGS=""
+
+fi
+
+
+        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for JxlSignatureCheck in -ljxl" >&5
+$as_echo_n "checking for JxlSignatureCheck in -ljxl... " >&6; }
+if ${ac_cv_lib_jxl_JxlSignatureCheck+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ljxl  $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 JxlSignatureCheck ();
+int
+main ()
+{
+return JxlSignatureCheck ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_jxl_JxlSignatureCheck=yes
+else
+  ac_cv_lib_jxl_JxlSignatureCheck=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_jxl_JxlSignatureCheck" >&5
+$as_echo "$ac_cv_lib_jxl_JxlSignatureCheck" >&6; }
+if test "x$ac_cv_lib_jxl_JxlSignatureCheck" = xyes; then :
+
+            have_jxl_lib=yes
+            LIBJXL_LIBS="-ljxl"
+
+fi
+
+
+else
+	LIBJXL_CFLAGS=$pkg_cv_LIBJXL_CFLAGS
+	LIBJXL_LIBS=$pkg_cv_LIBJXL_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+	        have_jxl_hdr=yes
+        have_jxl_lib=yes
+        have_jxl_pc=yes
+
+fi
+    if test x$have_jxl_hdr = xyes -a x$have_jxl_lib = xyes; then
+        if test x$enable_jxl = xyes; then
+            $as_echo "#define LOAD_JXL 1" >>confdefs.h
+
+        fi
+
+        case "$host" in
+            *-*-darwin*)
+                jxl_lib=`find_lib libjxl.dylib`
+                ;;
+            *-*-cygwin* | *-*-mingw*)
+                jxl_lib=`find_lib "libjxl*.dll"`
+                ;;
+            *)
+                jxl_lib=`find_lib "libjxl[0-9]*.so.*"`
+                if test x$jxl_lib = x; then
+                    jxl_lib=`find_lib "libjxl.so.*"`
+                fi
+                ;;
+        esac
+    elif test x$enable_jxl = xyes; then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** Unable to find JXL library (https://jpegxl.info/)" >&5
+$as_echo "$as_me: WARNING: *** Unable to find JXL library (https://jpegxl.info/)" >&2;}
+        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: JXL image loading disabled" >&5
+$as_echo "$as_me: WARNING: JXL image loading disabled" >&2;}
+    fi
+fi
+
 if test x$enable_png = xyes -a x$enable_imageio != xyes; then
 
 pkg_failed=no
@@ -14179,6 +14411,23 @@ _ACEOF
         fi
     fi
 fi
+if test x$enable_jxl = xyes -a x$have_jxl_hdr = xyes -a x$have_jxl_lib = xyes; then
+    CFLAGS="$LIBJXL_CFLAGS $CFLAGS"
+    if test x$enable_jxl_shared = xyes && test x$jxl_lib != x; then
+        echo "-- dynamic libjxl -> $jxl_lib"
+        cat >>confdefs.h <<_ACEOF
+#define LOAD_JXL_DYNAMIC "$jxl_lib"
+_ACEOF
+
+    else
+        IMG_LIBS="$LIBJXL_LIBS $IMG_LIBS"
+        if test x$have_jxl_pc = xyes; then
+            PC_REQUIRES="libjxl $PC_REQUIRES"
+        else
+            PC_LIBS="$LIBJXL_LIBS $PC_LIBS"
+        fi
+    fi
+fi
 if test x$enable_png = xyes -a x$have_png_hdr = xyes -a x$have_png_lib = xyes; then
     CFLAGS="$LIBPNG_CFLAGS $CFLAGS"
     if test x$enable_png_shared = xyes && test x$png_lib != x; then
diff --git a/configure.ac b/configure.ac
index e37c543f..a2d25475 100644
--- a/configure.ac
+++ b/configure.ac
@@ -204,6 +204,10 @@ AC_ARG_ENABLE([jpg], [AS_HELP_STRING([--enable-jpg], [support loading JPG images
  [], [enable_jpg=yes])
 AC_ARG_ENABLE([jpg-shared], [AS_HELP_STRING([--enable-jpg-shared], [dynamically load JPG support [default=yes]])],
  [], [enable_jpg_shared=yes])
+AC_ARG_ENABLE([jxl], [AS_HELP_STRING([--enable-jxl], [support loading JXL images [default=yes]])],
+ [], [enable_jxl=yes])
+AC_ARG_ENABLE([jxl-shared], [AS_HELP_STRING([--enable-jxl-shared], [dynamically load JXL support [default=yes]])],
+ [], [enable_jxl_shared=yes])
 AC_ARG_ENABLE([lbm], [AS_HELP_STRING([--enable-lbm], [support loading LBM images [default=yes]])],
  [], [enable_lbm=yes])
 AC_ARG_ENABLE([pcx], [AS_HELP_ST

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