This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch master
in repository legacy-imlib2.
View the commit online.
commit a55f36133d575a46a19d45718b81378078085037
Author: Kim Woelders <k...@woelders.dk>
AuthorDate: Wed Apr 26 07:18:48 2023 +0200
Add JXL saver
https://git.enlightenment.org/old/legacy-imlib2/issues/9
---
src/lib/Imlib2_Loader.h | 12 +++
src/modules/loaders/loader_jxl.c | 180 ++++++++++++++++++++++++++++++++++++++-
test/test_save.cpp | 2 +-
3 files changed, 192 insertions(+), 2 deletions(-)
diff --git a/src/lib/Imlib2_Loader.h b/src/lib/Imlib2_Loader.h
index 8279e2b..910b3c6 100644
--- a/src/lib/Imlib2_Loader.h
+++ b/src/lib/Imlib2_Loader.h
@@ -65,6 +65,18 @@ typedef struct _ImlibImage ImlibImage;
#define PIXEL_G(argb) (((argb) >> 8) & 0xff)
#define PIXEL_B(argb) (((argb) ) & 0xff)
+#ifndef WORDS_BIGENDIAN
+#define A_VAL(p) ((uint8_t *)(p))[3]
+#define R_VAL(p) ((uint8_t *)(p))[2]
+#define G_VAL(p) ((uint8_t *)(p))[1]
+#define B_VAL(p) ((uint8_t *)(p))[0]
+#else
+#define A_VAL(p) ((uint8_t *)(p))[0]
+#define R_VAL(p) ((uint8_t *)(p))[1]
+#define G_VAL(p) ((uint8_t *)(p))[2]
+#define B_VAL(p) ((uint8_t *)(p))[3]
+#endif
+
/* debug.h */
#if IMLIB2_DEBUG
diff --git a/src/modules/loaders/loader_jxl.c b/src/modules/loaders/loader_jxl.c
index 96ad698..c9037c1 100644
--- a/src/modules/loaders/loader_jxl.c
+++ b/src/modules/loaders/loader_jxl.c
@@ -4,6 +4,7 @@
#define MAX_RUNNERS 4 /* Maybe set to Ncpu/2? */
#include <jxl/decode.h>
+#include <jxl/encode.h>
#if MAX_RUNNERS > 0
#include <jxl/thread_parallel_runner.h>
#endif
@@ -215,4 +216,181 @@ _load(ImlibImage * im, int load_data)
return rc;
}
-IMLIB_LOADER(_formats, _load, NULL);
+static int
+_save(ImlibImage * im)
+{
+ int rc;
+ JxlEncoderStatus jst;
+ JxlEncoder *enc;
+ JxlBasicInfo info;
+ JxlEncoderFrameSettings *opts;
+
+ JxlPixelFormat pbuf_fmt = {
+ .data_type = JXL_TYPE_UINT8,
+ .endianness = JXL_NATIVE_ENDIAN,
+ };
+ ImlibImageTag *tag;
+ const uint32_t *ptr;
+ uint8_t *buffer = NULL, *buf_ptr;
+ size_t buf_len, i, npix;
+
+#if MAX_RUNNERS > 0
+ size_t n_runners;
+ JxlParallelRunner *runner = NULL;
+#endif
+
+ rc = LOAD_FAIL;
+
+ enc = JxlEncoderCreate(NULL);
+ if (!enc)
+ goto quit;
+
+#if MAX_RUNNERS > 0
+ n_runners = JxlThreadParallelRunnerDefaultNumWorkerThreads();
+ if (n_runners > MAX_RUNNERS)
+ n_runners = MAX_RUNNERS;
+ D("n_runners = %ld\n", n_runners);
+ runner = JxlThreadParallelRunnerCreate(NULL, n_runners);
+ if (!runner)
+ goto quit;
+
+ jst = JxlEncoderSetParallelRunner(enc, JxlThreadParallelRunner, runner);
+ if (jst != JXL_ENC_SUCCESS)
+ goto quit;
+#endif /* MAX_RUNNERS */
+
+ JxlEncoderInitBasicInfo(&info);
+ info.xsize = im->w;
+ info.ysize = im->h;
+ if (im->has_alpha)
+ {
+ info.alpha_bits = 8;
+ info.num_extra_channels = 1;
+ }
+
+ jst = JxlEncoderSetBasicInfo(enc, &info);
+ if (jst != JXL_ENC_SUCCESS)
+ goto quit;
+
+ opts = JxlEncoderFrameSettingsCreate(enc, NULL);
+ if (!opts)
+ goto quit;
+
+ tag = __imlib_GetTag(im, "quality");
+ if (tag)
+ {
+ int quality;
+ float distance;
+
+ quality = (tag->val) >= 0 ? tag->val : 0;
+ if (quality >= 100)
+ {
+ D("Quality=%d: Lossless\n", quality);
+ JxlEncoderSetFrameDistance(opts, 0.f);
+ JxlEncoderSetFrameLossless(opts, JXL_TRUE);
+ }
+ else
+ {
+ distance = 15.f * (1.f - .01 * quality); // 0 - 100 -> 15 - 0
+ D("Quality=%d: Distance=%.1f\n", quality, distance);
+ JxlEncoderSetFrameLossless(opts, JXL_FALSE);
+ JxlEncoderSetFrameDistance(opts, distance);
+ }
+ }
+
+ tag = __imlib_GetTag(im, "compression");
+ if (tag)
+ {
+ int compression;
+
+ compression = (tag->val < 1) ? 1 : (tag->val > 9) ? 9 : tag->val;
+ D("Compression=%d\n", compression);
+ JxlEncoderFrameSettingsSetOption(opts, JXL_ENC_FRAME_SETTING_EFFORT,
+ compression);
+ }
+
+ // Create buffer for format conversion and output
+ pbuf_fmt.num_channels = (im->has_alpha) ? 4 : 3;
+ npix = im->w * im->h;
+ buf_len = pbuf_fmt.num_channels * npix;
+ if (buf_len < 4096)
+ buf_len = 4096; // Not too small for output
+
+ buffer = malloc(buf_len);
+ if (!buffer)
+ QUIT_WITH_RC(LOAD_OOM);
+
+ // Convert format for libjxl
+ ptr = im->data;
+ buf_ptr = buffer;
+ if (pbuf_fmt.num_channels == 3)
+ {
+ for (i = 0; i < npix; i++, ptr++, buf_ptr += 3)
+ {
+ buf_ptr[0] = R_VAL(ptr);
+ buf_ptr[1] = G_VAL(ptr);
+ buf_ptr[2] = B_VAL(ptr);
+ }
+ }
+ else
+ {
+ for (i = 0; i < npix; i++, ptr++, buf_ptr += 4)
+ {
+ buf_ptr[0] = R_VAL(ptr);
+ buf_ptr[1] = G_VAL(ptr);
+ buf_ptr[2] = B_VAL(ptr);
+ buf_ptr[3] = A_VAL(ptr);
+ }
+ }
+
+ jst = JxlEncoderAddImageFrame(opts, &pbuf_fmt, buffer, buf_len);
+ if (jst != JXL_ENC_SUCCESS)
+ goto quit;
+
+ JxlEncoderCloseInput(enc);
+
+ for (;;)
+ {
+ uint8_t *next_out;
+ size_t avail_out;
+
+ next_out = buffer;
+ avail_out = buf_len;
+ jst = JxlEncoderProcessOutput(enc, &next_out, &avail_out);
+ switch (jst)
+ {
+ default:
+ goto quit;
+ case JXL_ENC_SUCCESS:
+ case JXL_ENC_NEED_MORE_OUTPUT:
+ D("Write: jst=%d %d\n", jst, (int)(buf_len - avail_out));
+ if (next_out == buffer)
+ goto quit;
+
+ if (fwrite(buffer, 1, buf_len - avail_out, im->fi->fp) !=
+ buf_len - avail_out)
+ goto quit;
+
+ if (jst == JXL_ENC_SUCCESS)
+ goto done;
+
+ break;
+ }
+ }
+ done:
+
+ rc = LOAD_SUCCESS;
+
+ quit:
+ free(buffer);
+#if MAX_RUNNERS > 0
+ if (runner)
+ JxlThreadParallelRunnerDestroy(runner);
+#endif
+ if (enc)
+ JxlEncoderDestroy(enc);
+
+ return rc;
+}
+
+IMLIB_LOADER(_formats, _load, _save);
diff --git a/test/test_save.cpp b/test/test_save.cpp
index fdb4b96..ab4375e 100644
--- a/test/test_save.cpp
+++ b/test/test_save.cpp
@@ -27,7 +27,7 @@ static const test_rec_t exts[] = {
// { "id3", { 0, 0 } },
// { "j2k", { 0, 0 } },
{ "jpeg", { 2458451111, 3483232328 } },
-// { "jxl", { 0, 0 } },
+ { "jxl", { 2681286418, 774897965 } },
// { "lbm", { 0, 0 } },
// { "lzma", { 0, 0 } },
{ "png", { 1153555547, 2937827957 } },
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.