This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch master
in repository efl.

View the commit online.

commit 8576ac912887819d274e39e4c76d55d640256d02
Author: Vincent Torri <vto...@outlook.fr>
AuthorDate: Wed Aug 16 12:17:57 2023 +0200

    Evas: add 'qoi' image loader and saver
---
 src/lib/evas/common/evas_image_load.c              |   6 +-
 src/lib/evas/common/evas_image_save.c              |   2 +
 src/lib/evas/file/evas_module.c                    |   8 +
 src/lib/evas/meson.build                           |   2 +
 .../evas/image_loaders/qoi/evas_image_load_qoi.c   | 416 +++++++++++++++++++++
 .../evas/image_savers/qoi/evas_image_save_qoi.c    | 264 +++++++++++++
 6 files changed, 696 insertions(+), 2 deletions(-)

diff --git a/src/lib/evas/common/evas_image_load.c b/src/lib/evas/common/evas_image_load.c
index 3e2b567948..5dd111fd05 100644
--- a/src/lib/evas/common/evas_image_load.c
+++ b/src/lib/evas/common/evas_image_load.c
@@ -68,6 +68,8 @@ static const struct ext_loader_s loaders[] =
 
    MATCHING(".jxl", "jxl"),
 
+   MATCHING(".qoi", "qoi"),
+
    MATCHING(".avif", "avif"),
    MATCHING(".avifs", "avif"),
 
@@ -195,8 +197,8 @@ static const struct ext_loader_s loaders[] =
 static const char *loaders_name[] =
 { /* in order of most likely needed */
   "png", "jpeg", "eet", "xpm", "tiff", "gif", "svg", "webp", "pmaps",
-  "bmp", "tga", "wbmp", "ico", "psd", "jp2k", "dds", "jxl", "avif", "heif",
-  "generic"
+  "bmp", "tga", "wbmp", "ico", "psd", "jp2k", "dds", "jxl", "qoi", "avif",
+  "heif", "generic"
 };
 
 struct evas_image_foreach_loader_data
diff --git a/src/lib/evas/common/evas_image_save.c b/src/lib/evas/common/evas_image_save.c
index dc0843eda1..07885dfa7d 100644
--- a/src/lib/evas/common/evas_image_save.c
+++ b/src/lib/evas/common/evas_image_save.c
@@ -37,6 +37,8 @@ evas_common_save_image_to_file(RGBA_Image *im, const char *file, const char *key
           saver = "avif";
         if (!strcasecmp(p, "jxl"))
           saver = "jxl";
+        if (!strcasecmp(p, "qoi"))
+          saver = "qoi";
      }
 
    if (saver)
diff --git a/src/lib/evas/file/evas_module.c b/src/lib/evas/file/evas_module.c
index a3c4f87881..541a9adee6 100644
--- a/src/lib/evas/file/evas_module.c
+++ b/src/lib/evas/file/evas_module.c
@@ -210,6 +210,7 @@ EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, jxl);
 EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, pmaps);
 EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, png);
 EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, psd);
+EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, qoi);
 EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, svg);
 EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, tga);
 EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, tiff);
@@ -230,6 +231,7 @@ EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, eet);
 EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, jpeg);
 EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, jxl);
 EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, png);
+EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, qoi);
 EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, tiff);
 EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, webp);
 EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, tgv);
@@ -334,6 +336,9 @@ static const struct {
 #ifdef EVAS_STATIC_BUILD_PSD
   EVAS_EINA_STATIC_MODULE_USE(image_loader, psd),
 #endif
+#ifdef EVAS_STATIC_BUILD_QOI
+  EVAS_EINA_STATIC_MODULE_USE(image_loader, qoi),
+#endif
 #ifdef EVAS_STATIC_BUILD_SVG
   EVAS_EINA_STATIC_MODULE_USE(image_loader, svg),
 #endif
@@ -380,6 +385,9 @@ static const struct {
 #ifdef EVAS_STATIC_BUILD_PNG
   EVAS_EINA_STATIC_MODULE_USE(image_saver, png),
 #endif
+#ifdef EVAS_STATIC_BUILD_QOI
+  EVAS_EINA_STATIC_MODULE_USE(image_saver, qoi),
+#endif
 #ifdef EVAS_STATIC_BUILD_TIFF
   EVAS_EINA_STATIC_MODULE_USE(image_saver, tiff),
 #endif
diff --git a/src/lib/evas/meson.build b/src/lib/evas/meson.build
index 38e9e250a1..5bb4c753e3 100644
--- a/src/lib/evas/meson.build
+++ b/src/lib/evas/meson.build
@@ -29,6 +29,7 @@ evas_image_loaders_file = [
      ['pmaps',   'shared', []],
      ['png',     'static', [png]],
      ['psd',     'shared', []],
+     ['qoi',     'shared', []],
      ['tga',     'shared', []],
      ['tgv',     'shared', [rg_etc, lz4]],
      ['tiff',    'shared', [tiff]],
@@ -43,6 +44,7 @@ evas_image_savers_file = [
      ['jpeg',    'static', [jpeg]],
      ['jxl' ,    'shared', [libjxl, libjxl_threads]],
      ['png',     'static', [png]],
+     ['qoi',     'shared', []],
      ['tgv',     'shared', [rg_etc, lz4]],
      ['tiff',    'shared', [tiff]],
      ['webp',    'shared', [webp]],
diff --git a/src/modules/evas/image_loaders/qoi/evas_image_load_qoi.c b/src/modules/evas/image_loaders/qoi/evas_image_load_qoi.c
new file mode 100644
index 0000000000..0cb0f24333
--- /dev/null
+++ b/src/modules/evas/image_loaders/qoi/evas_image_load_qoi.c
@@ -0,0 +1,416 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "Evas_Loader.h"
+#include "evas_common_private.h"
+
+/*
+ * code based on original qoi.h code (MIT license):
+ * https://github.com/phoboslab/qoi/blob/master/qoi.h
+ * date: 2023 march the 14th
+ */
+
+#define QOI_ZEROARR(a) memset((a),0,sizeof(a))
+
+#define QOI_OP_INDEX  0x00 /* 00xxxxxx */
+#define QOI_OP_DIFF   0x40 /* 01xxxxxx */
+#define QOI_OP_LUMA   0x80 /* 10xxxxxx */
+#define QOI_OP_RUN    0xc0 /* 11xxxxxx */
+#define QOI_OP_RGB    0xfe /* 11111110 */
+#define QOI_OP_RGBA   0xff /* 11111111 */
+
+#define QOI_MASK_2    0xc0 /* 11000000 */
+
+#define QOI_COLOR_HASH(C) (C.rgba.r*3 + C.rgba.g*5 + C.rgba.b*7 + C.rgba.a*11)
+
+#define QOI_MAGIC \
+	(((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \
+	 ((unsigned int)'i') <<  8 | ((unsigned int)'f'))
+
+#define QOI_HEADER_SIZE 14
+
+#define QOI_PIXELS_MAX ((unsigned int)400000000)
+
+#ifdef ERR
+# undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_evas_loader_qoi_log_dom, __VA_ARGS__)
+
+#ifdef WRN
+# undef WRN
+#endif
+#define WRN(...) EINA_LOG_DOM_WARN(_evas_loader_qoi_log_dom, __VA_ARGS__)
+
+#ifdef INF
+# undef INF
+#endif
+#define INF(...) EINA_LOG_DOM_INFO(_evas_loader_qoi_log_dom, __VA_ARGS__)
+
+typedef struct _Evas_Loader_Internal Evas_Loader_Internal;
+struct _Evas_Loader_Internal
+{
+   Eina_File *f;
+   Evas_Image_Load_Opts *opts;
+   Evas_Image_Animated *animated;
+};
+
+typedef union {
+	struct { unsigned char r, g, b, a; } rgba;
+	unsigned int v;
+} qoi_rgba_t;
+
+static int _evas_loader_qoi_log_dom = -1;
+
+static const unsigned char qoi_padding[8] = {0,0,0,0,0,0,0,1};
+
+static unsigned int read_32(const unsigned char *data, int *p)
+{
+   unsigned int a = data[(*p)++];
+   unsigned int b = data[(*p)++];
+   unsigned int c = data[(*p)++];
+   unsigned int d = data[(*p)++];
+   return a << 24 | b << 16 | c << 8 | d;
+}
+
+static Eina_Bool
+evas_image_load_file_head_qoi_internal(Evas_Loader_Internal *loader EINA_UNUSED,
+                                       Emile_Image_Property *prop,
+                                       void *map, size_t length,
+                                       int *error)
+{
+   const unsigned char *bytes;
+   unsigned int magic;
+   unsigned char channels;
+   unsigned char colorspace;
+   int p = 0;
+   Eina_Bool ret;
+
+   ret = EINA_FALSE;
+   prop->w = 0;
+   prop->h = 0;
+   prop->alpha = EINA_FALSE;
+
+   if (length < QOI_HEADER_SIZE + sizeof(qoi_padding))
+     {
+        *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
+        return ret;
+     }
+
+   bytes = (const unsigned char *)map;
+
+   magic = read_32(bytes, &p);
+   if (magic != QOI_MAGIC)
+     {
+        *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
+        return ret;
+     }
+
+   prop->w = read_32(bytes, &p);
+   prop->h = read_32(bytes, &p);
+   if ((prop->w < 1) ||
+       (prop->h < 1) ||
+       (prop->h >= QOI_PIXELS_MAX / prop->w) ||
+       (prop->w > IMG_MAX_SIZE) ||
+       (prop->h > IMG_MAX_SIZE) ||
+       IMG_TOO_BIG(prop->w, prop->h))
+     {
+        *error= EVAS_LOAD_ERROR_GENERIC;
+        return ret;
+     }
+
+   channels = bytes[p++];
+   colorspace = bytes[p++];
+
+   if ((channels < 3) ||
+       (channels > 4) ||
+       (colorspace > 1))
+     {
+        *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
+        return ret;
+     }
+
+   prop->alpha = channels == 4;
+
+   *error = EVAS_LOAD_ERROR_NONE;
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+evas_image_load_file_data_qoi_internal(Evas_Loader_Internal *loader EINA_UNUSED,
+                                       Emile_Image_Property *prop,
+                                       void *pixels,
+                                       void *map, size_t length,
+                                       int *error)
+{
+   qoi_rgba_t index[64];
+   const unsigned char *bytes;
+   qoi_rgba_t px;
+   unsigned int *iter;
+   unsigned int magic;
+   unsigned char channels;
+   unsigned char colorspace;
+   int p = 0;
+   int run = 0;
+   int chunks_len;
+   size_t px_len;
+   size_t px_pos;
+   Eina_Bool ret;
+
+   ret = EINA_FALSE;
+   prop->w = 0;
+   prop->h = 0;
+   prop->alpha = EINA_FALSE;
+
+   if (length < QOI_HEADER_SIZE + sizeof(qoi_padding))
+     {
+        *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
+        return ret;
+     }
+
+   bytes = (const unsigned char *)map;
+
+   magic = read_32(bytes, &p);
+   if (magic != QOI_MAGIC)
+     {
+        *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
+        return ret;
+     }
+
+   prop->w = read_32(bytes, &p);
+   prop->h = read_32(bytes, &p);
+   if ((prop->w < 1) ||
+       (prop->h < 1) ||
+       (prop->h >= QOI_PIXELS_MAX / prop->w) ||
+       (prop->w > IMG_MAX_SIZE) ||
+       (prop->h > IMG_MAX_SIZE) ||
+       IMG_TOO_BIG(prop->w, prop->h))
+     {
+        *error= EVAS_LOAD_ERROR_GENERIC;
+        return ret;
+     }
+
+   channels = bytes[p++];
+   colorspace = bytes[p++];
+
+   if ((channels < 3) ||
+       (channels > 4) ||
+       (colorspace > 1))
+     {
+        *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
+        return ret;
+     }
+
+   prop->alpha = channels == 4;
+
+   px_len = prop->w * prop->h * channels;
+
+   QOI_ZEROARR(index);
+   px.rgba.r = 0;
+   px.rgba.g = 0;
+   px.rgba.b = 0;
+   px.rgba.a = 255;
+
+   iter = pixels;
+   chunks_len = length - (int)sizeof(qoi_padding);
+   for (px_pos = 0; px_pos < px_len; px_pos += channels, iter++)
+     {
+        if (run > 0)
+          {
+             run--;
+          }
+        else if (p < chunks_len)
+          {
+             int b1 = bytes[p++];
+
+             if (b1 == QOI_OP_RGB)
+               {
+                  px.rgba.r = bytes[p++];
+                  px.rgba.g = bytes[p++];
+                  px.rgba.b = bytes[p++];
+               }
+             else if (b1 == QOI_OP_RGBA)
+               {
+                  px.rgba.r = bytes[p++];
+                  px.rgba.g = bytes[p++];
+                  px.rgba.b = bytes[p++];
+                  px.rgba.a = bytes[p++];
+               }
+             else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX)
+               {
+                  px = index[b1];
+               }
+             else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF)
+               {
+                  px.rgba.r += ((b1 >> 4) & 0x03) - 2;
+                  px.rgba.g += ((b1 >> 2) & 0x03) - 2;
+                  px.rgba.b += ( b1       & 0x03) - 2;
+               }
+             else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA)
+               {
+                  int b2 = bytes[p++];
+                  int vg = (b1 & 0x3f) - 32;
+                  px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f);
+                  px.rgba.g += vg;
+                  px.rgba.b += vg - 8 +  (b2       & 0x0f);
+               }
+             else if ((b1 & QOI_MASK_2) == QOI_OP_RUN)
+               {
+                  run = (b1 & 0x3f);
+               }
+
+             index[QOI_COLOR_HASH(px) % 64] = px;
+          }
+
+        *iter = ((prop->alpha ? px.rgba.a : 255) << 24) | (px.rgba.r << 16) | (px.rgba.g << 8) | px.rgba.b;
+     }
+
+   *error = EVAS_LOAD_ERROR_NONE;
+   return EINA_TRUE;
+}
+
+static void *
+evas_image_load_file_open_qoi(Eina_File *f, Eina_Stringshare *key EINA_UNUSED,
+                              Evas_Image_Load_Opts *opts,
+                              Evas_Image_Animated *animated EINA_UNUSED,
+                              int *error)
+{
+   Evas_Loader_Internal *loader;
+
+   loader = calloc(1, sizeof (Evas_Loader_Internal));
+   if (!loader)
+     {
+        *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
+        return NULL;
+     }
+
+   loader->f = f;
+   loader->opts = opts;
+
+   return loader;
+}
+
+static void
+evas_image_load_file_close_qoi(void *loader_data)
+{
+   free(loader_data);
+}
+
+static Eina_Bool
+evas_image_load_file_head_qoi(void *loader_data,
+                              Evas_Image_Property *prop,
+                              int *error)
+{
+   Evas_Loader_Internal *loader = loader_data;
+   Eina_File *f;
+   void *map;
+   Eina_Bool val;
+
+   f = loader->f;
+
+   map = eina_file_map_all(f, EINA_FILE_RANDOM);
+   if (!map)
+     {
+        *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST;
+        return EINA_FALSE;
+     }
+
+   val = evas_image_load_file_head_qoi_internal(loader,
+                                                (Emile_Image_Property *)prop,
+                                                map, eina_file_size_get(f),
+                                                error);
+
+   eina_file_map_free(f, map);
+
+   return val;
+}
+
+static Eina_Bool
+evas_image_load_file_data_qoi(void *loader_data,
+                              Evas_Image_Property *prop,
+                              void *pixels,
+                              int *error)
+{
+   Evas_Loader_Internal *loader;
+   Eina_File *f;
+   void *map;
+   Eina_Bool val = EINA_FALSE;
+
+   loader = (Evas_Loader_Internal *)loader_data;
+   f = loader->f;
+
+   map = eina_file_map_all(f, EINA_FILE_WILLNEED);
+   if (!map)
+     {
+        *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST;
+        goto on_error;
+     }
+
+   val = evas_image_load_file_data_qoi_internal(loader,
+                                                (Emile_Image_Property *)prop,
+                                                pixels,
+                                                map, eina_file_size_get(f),
+                                                error);
+
+   eina_file_map_free(f, map);
+
+ on_error:
+   return val;
+}
+
+static Evas_Image_Load_Func evas_image_load_qoi_func =
+{
+   EVAS_IMAGE_LOAD_VERSION,
+   evas_image_load_file_open_qoi,
+   evas_image_load_file_close_qoi,
+   evas_image_load_file_head_qoi,
+   NULL,
+   evas_image_load_file_data_qoi,
+   NULL,
+   EINA_TRUE,
+   EINA_FALSE
+};
+
+static int
+module_open(Evas_Module *em)
+{
+   if (!em) return 0;
+
+   _evas_loader_qoi_log_dom = eina_log_domain_register("evas-qoi", EINA_COLOR_BLUE);
+   if (_evas_loader_qoi_log_dom < 0)
+     {
+        EINA_LOG_ERR("Can not create a module log domain.");
+        return 0;
+     }
+
+   em->functions = (void *)(&evas_image_load_qoi_func);
+
+   return 1;
+}
+
+static void
+module_close(Evas_Module *em EINA_UNUSED)
+{
+   if (_evas_loader_qoi_log_dom >= 0)
+     {
+        eina_log_domain_unregister(_evas_loader_qoi_log_dom);
+        _evas_loader_qoi_log_dom = -1;
+     }
+}
+
+static Evas_Module_Api evas_modapi =
+{
+   EVAS_MODULE_API_VERSION,
+   "qoi",
+   "none",
+   {
+     module_open,
+     module_close
+   }
+};
+
+EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, qoi);
+
+#ifndef EVAS_STATIC_BUILD_QOI
+EVAS_EINA_MODULE_DEFINE(image_loader, qoi);
+#endif
diff --git a/src/modules/evas/image_savers/qoi/evas_image_save_qoi.c b/src/modules/evas/image_savers/qoi/evas_image_save_qoi.c
new file mode 100644
index 0000000000..0a31d8f621
--- /dev/null
+++ b/src/modules/evas/image_savers/qoi/evas_image_save_qoi.c
@@ -0,0 +1,264 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "evas_common_private.h"
+#include "evas_private.h"
+
+/*
+ * code based on original qoi.h code (MIT license):
+ * https://github.com/phoboslab/qoi/blob/master/qoi.h
+ * date: 2023 march the 14th
+ */
+
+#define QOI_SRGB   0
+
+#define QOI_ZEROARR(a) memset((a),0,sizeof(a))
+
+#define QOI_OP_INDEX  0x00 /* 00xxxxxx */
+#define QOI_OP_DIFF   0x40 /* 01xxxxxx */
+#define QOI_OP_LUMA   0x80 /* 10xxxxxx */
+#define QOI_OP_RUN    0xc0 /* 11xxxxxx */
+#define QOI_OP_RGB    0xfe /* 11111110 */
+#define QOI_OP_RGBA   0xff /* 11111111 */
+
+#define QOI_COLOR_HASH(C) (C.rgba.r*3 + C.rgba.g*5 + C.rgba.b*7 + C.rgba.a*11)
+
+#define QOI_MAGIC \
+	(((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \
+	 ((unsigned int)'i') <<  8 | ((unsigned int)'f'))
+
+#define QOI_HEADER_SIZE 14
+
+#define QOI_PIXELS_MAX ((unsigned int)400000000)
+
+typedef union {
+	struct { unsigned char r, g, b, a; } rgba;
+	unsigned int v;
+} qoi_rgba_t;
+
+static const unsigned char qoi_padding[8] = {0,0,0,0,0,0,0,1};
+
+static int
+save_image_qoi(RGBA_Image *im, const char *file, int quality EINA_UNUSED)
+{
+   qoi_rgba_t index[64];
+   qoi_rgba_t px;
+   qoi_rgba_t px_prev;
+   FILE *f;
+   unsigned char *iter;
+   unsigned int channels;
+   unsigned int colorspace;
+   unsigned int v;
+   int run;
+   int px_len;
+   int px_end;
+   int px_pos;
+   int i;
+   int ret = 0;
+
+   if (!im || !im->image.data || !file || !*file)
+     return ret;
+
+   if (im->cache_entry.h >= QOI_PIXELS_MAX / im->cache_entry.w)
+     return ret;
+
+   f = fopen(file, "wb");
+   if (!f)
+     return ret;
+
+   v = (0xff000000 & QOI_MAGIC) >> 24;
+   if (fwrite(&v, 1, 1, f) != 1) goto close_f;
+   v = (0x00ff0000 & QOI_MAGIC) >> 16;
+   if (fwrite(&v, 1, 1, f) != 1) goto close_f;
+   v = (0x0000ff00 & QOI_MAGIC) >> 8;
+   if (fwrite(&v, 1, 1, f) != 1) goto close_f;
+   v = (0x000000ff & QOI_MAGIC);
+   if (fwrite(&v, 1, 1, f) != 1) goto close_f;
+
+   v = (0xff000000 & im->cache_entry.w) >> 24;
+   if (fwrite(&v, 1, 1, f) != 1) goto close_f;
+   v = (0x00ff0000 & im->cache_entry.w) >> 16;
+   if (fwrite(&v, 1, 1, f) != 1) goto close_f;
+   v = (0x0000ff00 & im->cache_entry.w) >> 8;
+   if (fwrite(&v, 1, 1, f) != 1) goto close_f;
+   v = (0x000000ff & im->cache_entry.w);
+   if (fwrite(&v, 1, 1, f) != 1) goto close_f;
+
+   v = (0xff000000 & im->cache_entry.h) >> 24;
+   if (fwrite(&v, 1, 1, f) != 1) goto close_f;
+   v = (0x00ff0000 & im->cache_entry.h) >> 16;
+   if (fwrite(&v, 1, 1, f) != 1) goto close_f;
+   v = (0x0000ff00 & im->cache_entry.h) >> 8;
+   if (fwrite(&v, 1, 1, f) != 1) goto close_f;
+   v = (0x000000ff & im->cache_entry.h);
+   if (fwrite(&v, 1, 1, f) != 1) goto close_f;
+
+   channels = 4;
+   if (fwrite(&channels, 1, 1, f) != 1) goto close_f;
+
+   colorspace = QOI_SRGB;
+   if (fwrite(&colorspace, 1, 1, f) != 1) goto close_f;
+
+   QOI_ZEROARR(index);
+
+   run = 0;
+   px_prev.rgba.r = 0;
+   px_prev.rgba.g = 0;
+   px_prev.rgba.b = 0;
+   px_prev.rgba.a = 255;
+   px = px_prev;
+
+   px_len = im->cache_entry.w * im->cache_entry.h * channels;
+   px_end = px_len - channels;
+
+   iter = (unsigned char *)im->image.data;
+   for (px_pos = 0; px_pos < px_len; px_pos += channels, iter +=4)
+     {
+        px.rgba.b = *(iter + 0);
+        px.rgba.g = *(iter + 1);
+        px.rgba.r = *(iter + 2);
+        px.rgba.a = *(iter + 3);
+
+        if (px.v == px_prev.v)
+          {
+             run++;
+             if (run == 62 || px_pos == px_end)
+               {
+                  unsigned char val = QOI_OP_RUN | (run - 1);
+                  if (fwrite(&val, 1, 1, f) != 1) goto close_f;
+                  run = 0;
+               }
+          }
+        else
+          {
+             int index_pos;
+
+             if (run > 0)
+               {
+                  unsigned char val = QOI_OP_RUN | (run - 1);
+                  if (fwrite(&val, 1, 1, f) != 1) goto close_f;
+                  run = 0;
+               }
+
+             index_pos = QOI_COLOR_HASH(px) % 64;
+
+             if (index[index_pos].v == px.v)
+               {
+                  unsigned char val = QOI_OP_INDEX | index_pos;
+                  if (fwrite(&val, 1, 1, f) != 1) goto close_f;
+               }
+             else
+               {
+                  index[index_pos] = px;
+
+                  if (px.rgba.a == px_prev.rgba.a)
+                    {
+                       signed char vr = px.rgba.r - px_prev.rgba.r;
+                       signed char vg = px.rgba.g - px_prev.rgba.g;
+                       signed char vb = px.rgba.b - px_prev.rgba.b;
+
+                       signed char vg_r = vr - vg;
+                       signed char vg_b = vb - vg;
+
+                       if (vr > -3 && vr < 2 &&
+                           vg > -3 && vg < 2 &&
+                           vb > -3 && vb < 2)
+                         {
+                            unsigned char val = QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2);
+                            if (fwrite(&val, 1, 1, f) != 1) goto close_f;
+                         }
+                       else if (vg_r >  -9 && vg_r <  8 &&
+                                vg   > -33 && vg   < 32 &&
+                                vg_b >  -9 && vg_b <  8)
+                         {
+                            unsigned char val;
+
+                            val = QOI_OP_LUMA | (vg   + 32);
+                            if (fwrite(&val, 1, 1, f) != 1) goto close_f;
+
+                            val = (vg_r + 8) << 4 | (vg_b +  8);
+                            if (fwrite(&val, 1, 1, f) != 1) goto close_f;
+                         }
+                       else
+                         {
+                            unsigned char val;
+
+                            val = QOI_OP_RGB;
+                            if (fwrite(&val, 1, 1, f) != 1) goto close_f;
+                            if (fwrite(&px.rgba.r, 1, 1, f) != 1) goto close_f;
+                            if (fwrite(&px.rgba.g, 1, 1, f) != 1) goto close_f;
+                            if (fwrite(&px.rgba.b, 1, 1, f) != 1) goto close_f;
+                         }
+                    }
+                  else
+                    {
+                       unsigned char val;
+
+                       val = QOI_OP_RGBA;
+                       if (fwrite(&val, 1, 1, f) != 1) goto close_f;
+                       if (fwrite(&px.rgba.r, 1, 1, f) != 1) goto close_f;
+                       if (fwrite(&px.rgba.g, 1, 1, f) != 1) goto close_f;
+                       if (fwrite(&px.rgba.b, 1, 1, f) != 1) goto close_f;
+                       if (fwrite(&px.rgba.a, 1, 1, f) != 1) goto close_f;
+                    }
+               }
+          }
+        px_prev = px;
+     }
+
+   for (i = 0; i < (int)sizeof(qoi_padding); i++)
+     {
+        if (fwrite(&qoi_padding[i], 1, 1, f) != 1) goto close_f;
+     }
+
+   ret = 1;
+
+ close_f:
+   fclose(f);
+
+   return ret;
+}
+
+
+static int evas_image_save_file_qoi(RGBA_Image *im, const char *file, const char *key EINA_UNUSED,
+                                     int quality, int compress EINA_UNUSED, const char *encoding EINA_UNUSED)
+{
+   return save_image_qoi(im, file, quality);
+}
+
+
+static Evas_Image_Save_Func evas_image_save_qoi_func =
+{
+   evas_image_save_file_qoi
+};
+
+static int
+module_open(Evas_Module *em)
+{
+   if (!em) return 0;
+   em->functions = (void *)(&evas_image_save_qoi_func);
+   return 1;
+}
+
+static void
+module_close(Evas_Module *em EINA_UNUSED)
+{
+}
+
+static Evas_Module_Api evas_modapi =
+{
+   EVAS_MODULE_API_VERSION,
+   "qoi",
+   "none",
+   {
+     module_open,
+     module_close
+   }
+};
+
+EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_SAVER, image_saver, qoi);
+
+#ifndef EVAS_STATIC_BUILD_QOI
+EVAS_EINA_MODULE_DEFINE(image_saver, qoi);
+#endif

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

Reply via email to