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.

Reply via email to