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 285dc8bd05da4fc6d9d534182354a3bae3f5e1a8
Author: NRK <[email protected]>
AuthorDate: Thu Jun 15 11:32:18 2023 +0600

    add a new QOI decoder
---
 src/lib/loaders.c                |   2 +
 src/modules/loaders/Makefile.am  |   6 ++
 src/modules/loaders/loader_qoi.c | 203 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 211 insertions(+)

diff --git a/src/lib/loaders.c b/src/lib/loaders.c
index a3af200..4e19ec6 100644
--- a/src/lib/loaders.c
+++ b/src/lib/loaders.c
@@ -50,6 +50,7 @@ static const char  *const ext_png[] = { "png", NULL };
 #endif
 static const char  *const ext_pnm[] =
    { "pnm", "ppm", "pgm", "pbm", "pam", NULL };
+static const char  *const ext_qoi[] = { "qoi", NULL };
 #ifdef BUILD_PS_LOADER
 static const char  *const ext_ps[] = { "ps", "eps", NULL };
 #endif
@@ -112,6 +113,7 @@ static const KnownLoader loaders_known[] = {
    {"ps", ext_ps},
 #endif
    {"pnm", ext_pnm},
+   {"qoi", ext_qoi},
 #ifdef BUILD_SVG_LOADER
    {"svg", ext_svg},
 #endif
diff --git a/src/modules/loaders/Makefile.am b/src/modules/loaders/Makefile.am
index 5e92de4..41c4a93 100644
--- a/src/modules/loaders/Makefile.am
+++ b/src/modules/loaders/Makefile.am
@@ -12,6 +12,7 @@ ff.la \
 ico.la \
 lbm.la \
 pnm.la \
+qoi.la \
 tga.la \
 xbm.la \
 xpm.la
@@ -134,6 +135,11 @@ pnm_la_LDFLAGS       = -module -avoid-version
 pnm_la_LIBADD        = $(top_builddir)/src/lib/libImlib2.la
 pnm_la_LIBTOOLFLAGS  = --tag=disable-static
 
+qoi_la_SOURCES       = loader_qoi.c
+qoi_la_LDFLAGS       = -module -avoid-version
+qoi_la_LIBADD        = $(top_builddir)/src/lib/libImlib2.la
+qoi_la_LIBTOOLFLAGS  = --tag=disable-static
+
 ps_la_SOURCES       = loader_ps.c
 ps_la_CPPFLAGS      = $(PS_CFLAGS) $(AM_CPPFLAGS)
 ps_la_LDFLAGS       = -module -avoid-version
diff --git a/src/modules/loaders/loader_qoi.c b/src/modules/loaders/loader_qoi.c
new file mode 100644
index 0000000..e749338
--- /dev/null
+++ b/src/modules/loaders/loader_qoi.c
@@ -0,0 +1,203 @@
+#include "config.h"
+#include "Imlib2_Loader.h"
+
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+
+// decoder code taken from commit 8691a3ba:
+// https://codeberg.org/NRK/slashtmp/src/branch/master/compression/qoi-dec.c
+//
+// simple qoi decoder: https://qoiformat.org/
+//
+// This is free and unencumbered software released into the public domain.
+// For more information, please refer to <https://unlicense.org/>
+#define QOIDEC_API static
+#define DBG_PFX "LDR-qoi"
+#if IMLIB2_DEBUG
+#define QOIDEC_ASSERT(X)   do { if (!(X)) D("%d: %s\n", __LINE__, #X); } while (0)
+#elif defined(__GNUC__)
+#define QOIDEC_ASSERT(X)   ((X) ? (void)0 : __builtin_unreachable())
+#else
+#define QOIDEC_ASSERT(X)   ((void)0)
+#endif
+
+static const char  *const _formats[] = { "qoi" };
+
+// API
+
+typedef struct {
+   uint32_t w, h;
+   // this is where the decoded ARGB32 pixels will be put.
+   // to be allocated by the caller by at least `.data_size` bytes before calling qoi_dec().
+   uint32_t *data;
+   ptrdiff_t npixels, data_size;
+   uint8_t channels, colorspace;
+
+   // private
+   const uint8_t *p, *end;
+} QoiDecCtx;
+
+typedef enum {
+   QOIDEC_OK,
+   QOIDEC_NOT_QOI, QOIDEC_CORRUPTED, QOIDEC_ZERO_DIM, QOIDEC_TOO_LARGE,
+} QoiDecStatus;
+QOIDEC_API QoiDecStatus qoi_dec_init(QoiDecCtx *ctx, const void *buffer, ptrdiff_t size);
+QOIDEC_API QoiDecStatus qoi_dec(QoiDecCtx *ctx);
+
+// implementation
+
+QOIDEC_API QoiDecStatus
+qoi_dec_init(QoiDecCtx *ctx, const void *buffer, ptrdiff_t size)
+{
+   QOIDEC_ASSERT(size >= 0);
+   *ctx = (QoiDecCtx){0};
+
+   ctx->p = buffer;
+   ctx->end = ctx->p + size;
+   if (size < 14 ||
+      !((ctx->p[0] == 'q') & (ctx->p[1] == 'o') &
+        (ctx->p[2] == 'i') & (ctx->p[3] == 'f')))
+   {
+      return QOIDEC_NOT_QOI;
+   }
+   ctx->p += 4;
+
+   ctx->w = (uint32_t)ctx->p[0] << 24 | ctx->p[1] << 16 | ctx->p[2] << 8 | ctx->p[3];
+   ctx->p += 4;
+   ctx->h = (uint32_t)ctx->p[0] << 24 | ctx->p[1] << 16 | ctx->p[2] << 8 | ctx->p[3];
+   ctx->p += 4;
+   if (ctx->w == 0 || ctx->h == 0) {
+      return QOIDEC_ZERO_DIM;
+   }
+   if ((PTRDIFF_MAX/4)/ctx->w < ctx->h) {
+      return QOIDEC_TOO_LARGE;
+   }
+   ctx->npixels = (ptrdiff_t)ctx->w * ctx->h;
+   ctx->data_size = ctx->npixels * 4;
+
+   ctx->channels   = *ctx->p++;
+   ctx->colorspace = *ctx->p++;
+   if (!(ctx->channels == 3 || ctx->channels == 4) ||
+       ctx->colorspace & ~0x1u || ctx->end - ctx->p < 8 /* end marker */)
+   {
+      return QOIDEC_CORRUPTED;
+   }
+
+   return QOIDEC_OK;
+}
+
+QOIDEC_API QoiDecStatus
+qoi_dec(QoiDecCtx *ctx)
+{
+   typedef struct { uint8_t b, g, r, a; } Clr;
+   Clr                 t[64] = {0};
+   Clr                 l = { .a = 0xFF };
+   uint8_t             lop = -1;
+   const uint8_t      *p = ctx->p, *end = ctx->end;
+
+   QOIDEC_ASSERT(ctx->data != NULL);
+   QOIDEC_ASSERT(ctx->p != NULL && ctx->end != NULL);
+   QOIDEC_ASSERT(ctx->end - ctx->p >= 8);
+   QOIDEC_ASSERT(ctx->p[-14] == 'q' && ctx->p[-13] == 'o');
+   QOIDEC_ASSERT(ctx->p[-12] == 'i' && ctx->p[-11] == 'f');
+
+   if ((*p >> 6) == 0x3 && (*p & 0x3F) < 62) { // ref: https://github.com/phoboslab/qoi/issues/258
+      t[(0xFF * 11) % 64] = l;
+   }
+   for (ptrdiff_t widx = 0; widx < ctx->npixels;) {
+      uint32_t            c;
+      uint8_t             tmp, op;
+      int                 dg;
+
+      QOIDEC_ASSERT(p <= end);
+      if (end - p < 8) {
+         return QOIDEC_CORRUPTED;
+      }
+      op = *p++;
+      switch (op) {
+      case 0xFF:
+         l.r = *p++; l.g = *p++; l.b = *p++; l.a = *p++;
+         break;
+      case 0xFE:
+         l.r = *p++; l.g = *p++; l.b = *p++;
+         break;
+      default:
+         switch (op >> 6) {
+         case 0x3:
+            tmp = (op & 0x3F) + 1;
+            if (ctx->npixels - widx < tmp) {
+               return QOIDEC_CORRUPTED;
+            }
+            c = (uint32_t)l.a << 24 | l.r << 16 | l.g << 8 | l.b;
+            for (int k = 0; k < tmp; ++k) {
+               ctx->data[widx++] = c;
+            }
+            goto no_write;
+            break;
+         case 0x0:
+            if (op == lop) {
+               return QOIDEC_CORRUPTED; // seriously?
+            }
+            l = t[op & 0x3F];
+            goto no_table;
+            break;
+         case 0x1:
+            l.r += ((op >> 4) & 0x3) - 2;
+            l.g += ((op >> 2) & 0x3) - 2;
+            l.b += ((op >> 0) & 0x3) - 2;
+            break;
+         case 0x2:
+            tmp = *p++;
+            QOIDEC_ASSERT((tmp >> 8)  == 0);
+            dg = (op & 0x3F) - 32;
+            l.r += dg + ((tmp >> 4)  - 8);
+            l.g += dg;
+            l.b += dg + ((tmp & 0xF) - 8);
+            break;
+         }
+         break;
+      }
+
+      t[(l.r*3 + l.g*5 + l.b*7 + l.a*11) % 64] = l;
+no_table:
+      ctx->data[widx++] = (uint32_t)l.a << 24 | l.r << 16 | l.g << 8 | l.b;
+no_write:
+      lop = op;
+   }
+   if (end - p < 8 ||
+      !((p[0] == 0) & (p[1] == 0) & (p[2] == 0) & (p[3] == 0) &
+        (p[4] == 0) & (p[5] == 0) & (p[6] == 0) & (p[7] == 1)))
+   {
+      return QOIDEC_CORRUPTED;
+   }
+
+   return QOIDEC_OK;
+}
+
+static int
+_load(ImlibImage * im, int load_data)
+{
+   QoiDecCtx           qoi;
+
+   if (qoi_dec_init(&qoi, im->fi->fdata, im->fi->fsize) != QOIDEC_OK)
+      return LOAD_FAIL;
+
+   im->w = qoi.w;
+   im->h = qoi.h;
+   im->has_alpha = qoi.channels == 4;
+   if (!IMAGE_DIMENSIONS_OK(im->w, im->h))
+      return LOAD_BADIMAGE;
+   if (!load_data)
+      return LOAD_SUCCESS;
+
+   if (!__imlib_AllocateData(im))
+      return LOAD_OOM;
+   qoi.data = ""
+   if (qoi_dec(&qoi) != QOIDEC_OK)
+      return LOAD_BADIMAGE;
+
+   return LOAD_SUCCESS;
+}
+
+IMLIB_LOADER(_formats, _load, NULL);

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to