*
*/
-#include "main.h"
+#include <config.h>
+
+#if HAVE_JPEGXL
-#include "image-load.h"
#include "image-load-jpegxl.h"
-#ifdef HAVE_JPEGXL
+#include <cstdint>
+#include <cstdlib>
+#include <memory>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <glib-object.h>
+#include <glib.h>
+#include <jxl/codestream_header.h>
+#include <jxl/decode.h> //TODO Use decode_cxx.h?
+#include <jxl/types.h>
+
+#include "debug.h"
+#include "image-load.h"
+
+namespace
+{
-#include "jxl/decode.h"
+struct ImageLoaderJPEGXL : public ImageLoaderBackend
+{
+public:
+ ~ImageLoaderJPEGXL() override;
-struct ImageLoaderJPEGXL {
- ImageLoaderBackendCbAreaUpdated area_updated_cb;
- ImageLoaderBackendCbSize size_cb;
- ImageLoaderBackendCbAreaPrepared area_prepared_cb;
+ void init(AreaUpdatedCb area_updated_cb, SizePreparedCb size_prepared_cb, AreaPreparedCb area_prepared_cb, gpointer data) override;
+ gboolean write(const guchar *buf, gsize &chunk_size, gsize count, GError **error) override;
+ GdkPixbuf *get_pixbuf() override;
+ gchar *get_format_name() override;
+ gchar **get_format_mime_types() override;
+
+private:
+ AreaUpdatedCb area_updated_cb;
gpointer data;
+
GdkPixbuf *pixbuf;
- guint requested_width;
- guint requested_height;
- gboolean abort;
};
-static void free_buffer(guchar *pixels, gpointer UNUSED(data))
+void free_buffer(guchar *pixels, gpointer)
{
g_free(pixels);
}
-static uint8_t *JxlMemoryToPixels(const uint8_t *next_in, size_t size, size_t *stride,
- size_t *xsize, size_t *ysize, int *has_alpha) {
- JxlDecoder *dec = JxlDecoderCreate(nullptr);
- *has_alpha = 1;
- uint8_t *pixels = nullptr;
- if (!dec) {
- log_printf("JxlDecoderCreate failed\n");
- return nullptr;
- }
- if (JXL_DEC_SUCCESS !=
- JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE)) {
- log_printf("JxlDecoderSubscribeEvents failed\n");
- JxlDecoderDestroy(dec);
- return nullptr;
- }
-
- /* Avoid compiler warning - used uninitialized */
- /* This file will be replaced by libjxl at some time */
- *stride = 0;
- *ysize = 0;
-
- JxlBasicInfo info;
- int success = 0;
- JxlPixelFormat format = {4, JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0};
- JxlDecoderSetInput(dec, next_in, size);
-
- for (;;) {
- JxlDecoderStatus status = JxlDecoderProcessInput(dec);
-
- if (status == JXL_DEC_ERROR) {
- log_printf("Decoder error\n");
- break;
- } else if (status == JXL_DEC_NEED_MORE_INPUT) {
- log_printf("Error, already provided all input\n");
- break;
- } else if (status == JXL_DEC_BASIC_INFO) {
- if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec, &info)) {
- log_printf("JxlDecoderGetBasicInfo failed\n");
- break;
- }
- *xsize = info.xsize;
- *ysize = info.ysize;
- *stride = info.xsize * 4;
- } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
- size_t buffer_size;
- if (JXL_DEC_SUCCESS !=
- JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)) {
- log_printf("JxlDecoderImageOutBufferSize failed\n");
- break;
- }
- if (buffer_size != *stride * *ysize) {
- log_printf("Invalid out buffer size %zu %zu\n", buffer_size,
- *stride * *ysize);
- break;
- }
- size_t pixels_buffer_size = buffer_size * sizeof(uint8_t);
- pixels = static_cast<uint8_t *>(malloc(pixels_buffer_size));
- auto pixels_buffer = (void *)pixels;
- if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec, &format,
- pixels_buffer,
- pixels_buffer_size)) {
- log_printf("JxlDecoderSetImageOutBuffer failed\n");
- break;
- }
- } else if (status == JXL_DEC_FULL_IMAGE) {
- // This means the decoder has decoded all pixels into the buffer.
- success = 1;
- break;
- } else if (status == JXL_DEC_SUCCESS) {
- log_printf("Decoding finished before receiving pixel data\n");
- break;
- } else {
- log_printf("Unexpected decoder status: %d\n", status);
- break;
- }
- }
-
- JxlDecoderDestroy(dec);
- if (success){
- return pixels;
- } else {
- free(pixels);
- return nullptr;
- }
+uint8_t *JxlMemoryToPixels(const uint8_t *next_in, size_t size, size_t &xsize, size_t &ysize, size_t &stride)
+{
+ std::unique_ptr<JxlDecoder, decltype(&JxlDecoderDestroy)> dec{JxlDecoderCreate(nullptr), JxlDecoderDestroy};
+ if (!dec)
+ {
+ log_printf("JxlDecoderCreate failed\n");
+ return nullptr;
+ }
+ if (JXL_DEC_SUCCESS != JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE))
+ {
+ log_printf("JxlDecoderSubscribeEvents failed\n");
+ return nullptr;
+ }
+
+ /* Avoid compiler warning - used uninitialized */
+ /* This file will be replaced by libjxl at some time */
+ ysize = 0;
+ stride = 0;
+
+ std::unique_ptr<uint8_t, decltype(&free)> pixels{nullptr, free};
+ JxlBasicInfo info;
+ JxlPixelFormat format = {4, JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0};
+ JxlDecoderSetInput(dec.get(), next_in, size);
+
+ for (;;)
+ {
+ JxlDecoderStatus status = JxlDecoderProcessInput(dec.get());
+
+ switch (status)
+ {
+ case JXL_DEC_ERROR:
+ log_printf("Decoder error\n");
+ return nullptr;
+ case JXL_DEC_NEED_MORE_INPUT:
+ log_printf("Error, already provided all input\n");
+ return nullptr;
+ case JXL_DEC_BASIC_INFO:
+ if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec.get(), &info))
+ {
+ log_printf("JxlDecoderGetBasicInfo failed\n");
+ return nullptr;
+ }
+ xsize = info.xsize;
+ ysize = info.ysize;
+ stride = info.xsize * 4;
+ break;
+ case JXL_DEC_NEED_IMAGE_OUT_BUFFER:
+ {
+ size_t buffer_size;
+ if (JXL_DEC_SUCCESS != JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size))
+ {
+ log_printf("JxlDecoderImageOutBufferSize failed\n");
+ return nullptr;
+ }
+ if (buffer_size != stride * ysize)
+ {
+ log_printf("Invalid out buffer size %zu %zu\n", buffer_size, stride * ysize);
+ return nullptr;
+ }
+ size_t pixels_buffer_size = buffer_size * sizeof(uint8_t);
+ pixels.reset(static_cast<uint8_t *>(malloc(pixels_buffer_size)));
+ if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format, pixels.get(), pixels_buffer_size))
+ {
+ log_printf("JxlDecoderSetImageOutBuffer failed\n");
+ return nullptr;
+ }
+ }
+ break;
+ case JXL_DEC_FULL_IMAGE:
+ // This means the decoder has decoded all pixels into the buffer.
+ return pixels.release();
+ case JXL_DEC_SUCCESS:
+ log_printf("Decoding finished before receiving pixel data\n");
+ return nullptr;
+ default:
+ log_printf("Unexpected decoder status: %d\n", status);
+ return nullptr;
+ }
+ }
+
+ return nullptr;
}
-static gboolean image_loader_jpegxl_load(gpointer loader, const guchar *buf, gsize count, GError **UNUSED(error))
+gboolean ImageLoaderJPEGXL::write(const guchar *buf, gsize &chunk_size, gsize count, GError **)
{
- auto ld = static_cast<ImageLoaderJPEGXL *>(loader);
gboolean ret = FALSE;
- size_t stride;
size_t xsize;
size_t ysize;
- int has_alpha;
- uint8_t *decoded = nullptr;
+ size_t stride;
+ uint8_t *pixels = nullptr;
- decoded = JxlMemoryToPixels(buf, count, &stride, &xsize, &ysize, &has_alpha);
+ pixels = JxlMemoryToPixels(buf, count, xsize, ysize, stride);
- if (decoded)
+ if (pixels)
{
- ld->pixbuf = gdk_pixbuf_new_from_data(decoded, GDK_COLORSPACE_RGB, has_alpha, 8, xsize, ysize, stride, free_buffer, nullptr);
+ pixbuf = gdk_pixbuf_new_from_data(pixels, GDK_COLORSPACE_RGB, TRUE, 8, xsize, ysize, stride, free_buffer, nullptr);
- ld->area_updated_cb(loader, 0, 0, xsize, ysize, ld->data);
+ area_updated_cb(nullptr, 0, 0, xsize, ysize, data);
+ chunk_size = count;
ret = TRUE;
}
return ret;
}
-static gpointer image_loader_jpegxl_new(ImageLoaderBackendCbAreaUpdated area_updated_cb, ImageLoaderBackendCbSize size_cb, ImageLoaderBackendCbAreaPrepared area_prepared_cb, gpointer data)
-{
- auto loader = g_new0(ImageLoaderJPEGXL, 1);
- loader->area_updated_cb = area_updated_cb;
- loader->size_cb = size_cb;
- loader->area_prepared_cb = area_prepared_cb;
- loader->data = data;
- return loader;
-}
-
-static void image_loader_jpegxl_set_size(gpointer loader, int width, int height)
+void ImageLoaderJPEGXL::init(AreaUpdatedCb area_updated_cb, SizePreparedCb, AreaPreparedCb, gpointer data)
{
- auto ld = static_cast<ImageLoaderJPEGXL *>(loader);
- ld->requested_width = width;
- ld->requested_height = height;
+ this->area_updated_cb = area_updated_cb;
+ this->data = data;
}
-static GdkPixbuf* image_loader_jpegxl_get_pixbuf(gpointer loader)
+GdkPixbuf *ImageLoaderJPEGXL::get_pixbuf()
{
- auto ld = static_cast<ImageLoaderJPEGXL *>(loader);
- return ld->pixbuf;
+ return pixbuf;
}
-static gchar* image_loader_jpegxl_get_format_name(gpointer UNUSED(loader))
+gchar *ImageLoaderJPEGXL::get_format_name()
{
return g_strdup("jxl");
}
-static gchar** image_loader_jpegxl_get_format_mime_types(gpointer UNUSED(loader))
+gchar **ImageLoaderJPEGXL::get_format_mime_types()
{
static const gchar *mime[] = {"image/jxl", nullptr};
return g_strdupv(const_cast<gchar **>(mime));
}
-static gboolean image_loader_jpegxl_close(gpointer UNUSED(loader), GError **UNUSED(error))
+ImageLoaderJPEGXL::~ImageLoaderJPEGXL()
{
- return TRUE;
+ if (pixbuf) g_object_unref(pixbuf);
}
-static void image_loader_jpegxl_abort(gpointer loader)
-{
- auto ld = static_cast<ImageLoaderJPEGXL *>(loader);
- ld->abort = TRUE;
-}
-
-static void image_loader_jpegxl_free(gpointer loader)
-{
- auto ld = static_cast<ImageLoaderJPEGXL *>(loader);
- if (ld->pixbuf) g_object_unref(ld->pixbuf);
- g_free(ld);
-}
+} // namespace
-void image_loader_backend_set_jpegxl(ImageLoaderBackend *funcs)
+std::unique_ptr<ImageLoaderBackend> get_image_loader_backend_jpegxl()
{
- funcs->loader_new = image_loader_jpegxl_new;
- funcs->set_size = image_loader_jpegxl_set_size;
- funcs->load = image_loader_jpegxl_load;
- funcs->write = nullptr;
- funcs->get_pixbuf = image_loader_jpegxl_get_pixbuf;
- funcs->close = image_loader_jpegxl_close;
- funcs->abort = image_loader_jpegxl_abort;
- funcs->free = image_loader_jpegxl_free;
- funcs->get_format_name = image_loader_jpegxl_get_format_name;
- funcs->get_format_mime_types = image_loader_jpegxl_get_format_mime_types;
+ return std::make_unique<ImageLoaderJPEGXL>();
}
#endif