summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Torri <vincent.torri@gmail.com>2020-07-15 18:51:27 +0100
committerCarsten Haitzler (Rasterman) <raster@rasterman.com>2020-07-15 18:51:27 +0100
commitfd24e89144d8808e4e7ee20efe599ff4a6d3d37e (patch)
tree11634a58ed8a143580142b8aa7f52eac8271ee36
parenta8538bacce1457a91e28b39a6417eaadb4744b50 (diff)
downloadefl-fd24e89144d8808e4e7ee20efe599ff4a6d3d37e.tar.gz
Evas: add avif evas loader and saver
Summary: Add AV1 image file loader and saver to Evas The loader can be tested with this code : ``` #include <stdlib.h> #include <stdio.h> #include <Eina.h> #include <Ecore.h> #include <Evas.h> #include <Ecore_Evas.h> static int i = 0; static unsigned char _timer(void *data) { Evas_Object *o = (Evas_Object *)data; if (i < evas_object_image_animated_frame_count_get(o)) { evas_object_image_animated_frame_set(o, i); i++; return ECORE_CALLBACK_RENEW; } return ECORE_CALLBACK_DONE; } static void _quit(Ecore_Evas *ee) { ecore_main_loop_quit(); (void)ee; } int main(int argc, char *argv[]) { Ecore_Evas *ee; Evas *evas; Evas_Object *o; int w,h; Evas_Load_Error err; if (argc < 2) { printf("usage : %s file\n", argv[0]); return 1; } ecore_evas_init(); ee = ecore_evas_new(NULL, 0, 0, 1, 1, NULL); if (!ee) { printf("no ee\n"); return 0; } evas = ecore_evas_get(ee); ecore_evas_title_set(ee, "avif test"); ecore_evas_callback_delete_request_set(ee, _quit); o = evas_object_image_add(evas); evas_object_image_file_set(o, argv[1], NULL); err = evas_object_image_load_error_get(o); if (err != EVAS_LOAD_ERROR_NONE) { fprintf(stderr, "could not load image '%s'. error string is \"%s\"\n", argv[1], evas_load_error_str(err)); return 1; } evas_object_image_size_get(o, &w, &h); evas_object_image_fill_set(o, 0, 0, w, h); evas_object_move(o, 0, 0); evas_object_resize(o, w, h); evas_object_show(o); printf("animated : %s\n", evas_object_image_animated_get(o) ? "yes" : "no"); fflush(stdout); if (evas_object_image_animated_get(o)) { Ecore_Timer *timer; printf("frame count : %d\n", evas_object_image_animated_frame_count_get(o)); printf("duration : %f\n", evas_object_image_animated_frame_duration_get(o,1,0)); printf("loop count : %d\n", evas_object_image_animated_loop_count_get(o)); fflush(stdout); timer = ecore_timer_add(evas_object_image_animated_frame_duration_get(o,1,0), _timer, o); } ecore_evas_resize(ee, w, h); ecore_evas_show(ee); ecore_main_loop_begin(); ecore_evas_shutdown(); return 0; } ``` non animated files : https://github.com/AOMediaCodec/libavif/tree/master/tests/data/originals animated files : https://github.com/AOMediaCodec/av1-avif/tree/master/testFiles/Netflix/avifs to test the saver : ``` #include <stdlib.h> #include <stdio.h> #include <math.h> #include <Eina.h> #include <Ecore.h> #include <Evas.h> #include <Ecore_Evas.h> void _quit(Ecore_Evas *ee) { ecore_main_loop_quit(); (void)ee; } static Evas_Object * display_data(int w, int h, const char *title, unsigned int *data) { Ecore_Evas *ee; Evas *evas; Evas_Object *o; unsigned int *d; ee = ecore_evas_new(NULL, 0, 0, w, h, NULL); if (!ee) return NULL; evas = ecore_evas_get(ee); ecore_evas_title_set(ee, title); ecore_evas_callback_delete_request_set(ee, _quit); o = evas_object_image_add(evas); evas_object_image_fill_set(o, 0, 0, w, h); evas_object_image_size_set(o, w, h); d = evas_object_image_data_get(o, 1); for (int i = 0; i < w*h; i++) d[i] = data[i]; evas_object_image_data_set(o, d); evas_object_image_data_update_add(o, 0, 0, w, h); evas_object_move(o, 0, 0); evas_object_resize(o, w, h); evas_object_show(o); ecore_evas_show(ee); return o; } static unsigned int * display_file(const char *title, const char *filename, int *w, int *h) { Ecore_Evas *ee; Evas *evas; Evas_Object *o; Evas_Load_Error err; unsigned int *data; ee = ecore_evas_new(NULL, 0, 0, 1, 1, NULL); if (!ee) return NULL; evas = ecore_evas_get(ee); ecore_evas_title_set(ee, title); ecore_evas_callback_delete_request_set(ee, _quit); o = evas_object_image_add(evas); evas_object_image_file_set(o, filename, NULL); err = evas_object_image_load_error_get(o); if (err != EVAS_LOAD_ERROR_NONE) { fprintf(stderr, "could not load image '%s'. error string is \"%s\"\n", filename, evas_load_error_str(err)); fflush(stderr); return NULL; } evas_object_image_size_get(o, w, h); evas_object_image_fill_set(o, 0, 0, *w, *h); evas_object_image_size_set(o, *w, *h); evas_object_move(o, 0, 0); evas_object_resize(o, *w, *h); evas_object_show(o); ecore_evas_resize(ee, *w, *h); ecore_evas_show(ee); data = evas_object_image_data_get(o, 1); return data; } double psnr(int w, int h, unsigned int *data_orig, unsigned int *data) { unsigned char *iter_orig; unsigned char *iter; double psnr; psnr = 0.0; iter_orig = (unsigned char *)data_orig; iter = (unsigned char *)data; for (int i = 0; i < 4 * w * h; i++, iter_orig++, iter++) psnr += (*iter_orig - *iter) * (*iter_orig - *iter); psnr /= 4 * w * h; psnr = 10 * log10(255.0 * 255.0 / psnr); return psnr; } void compare(int quality, int w, int h, unsigned int *data_orig) { char title[1024]; char filename[1024]; unsigned char *data; unsigned int *data_jpeg; unsigned int *data_avif; unsigned char *iter_orig; unsigned char *iter_jpeg; unsigned char *iter_avif; double psnr_jpeg; double psnr_avif; Eina_File *f_jpeg; Eina_File *f_avif; size_t size_jpeg; size_t size_avif; /* jpeg */ snprintf(title, sizeof(title), "jpeg test quality %d", quality); snprintf(filename, sizeof(filename), "test_%d.jpg", quality); data_jpeg = display_file(title, filename, &w, &h); if (!data_jpeg) return; f_jpeg = eina_file_open(filename, EINA_FALSE); size_jpeg = eina_file_size_get(f_jpeg); eina_file_close(f_jpeg); fprintf(stderr, "size : %u\n", (unsigned int)size_jpeg); fflush(stderr); /* avif */ snprintf(title, sizeof(title), "avif test quality %d", quality); snprintf(filename, sizeof(filename), "test_%d.avif", quality); data_avif = display_file(title, filename, &w, &h); if (!data_avif) return; f_avif = eina_file_open(filename, EINA_FALSE); size_avif = eina_file_size_get(f_avif); eina_file_close(f_avif); fprintf(stderr, "size : %u\n", (unsigned int)size_avif); fflush(stderr); psnr_jpeg = psnr(w, h, data_orig, data_jpeg); fprintf(stderr, "psnr jpeg : %f\n", psnr_jpeg); fflush(stderr); snprintf(title, sizeof(title), "jpeg vs orig (psnr: %.2f, size: %u b)", psnr_jpeg, (unsigned int)size_jpeg); iter_orig = (unsigned char *)data_orig; iter_jpeg = (unsigned char *)data_jpeg; data = malloc(4*w*h); for (int i = 0; i < 4*w*h; i++, iter_orig++, iter_jpeg++) data[i] = abs(*iter_jpeg - *iter_orig); display_data(w, h, title, (unsigned int *)data); psnr_avif = psnr(w, h, data_orig, data_avif); fprintf(stderr, "psnr avif : %f\n", psnr_avif); fflush(stderr); snprintf(title, sizeof(title), "avif vs orig (psnr: %.2f, size: %u b)", psnr_avif, (unsigned int)size_avif); iter_orig = (unsigned char *)data_orig; iter_avif = (unsigned char *)data_avif; data = malloc(4*w*h); for (int i = 0; i < 4*w*h; i++, iter_orig++, iter_avif++) data[i] = abs(*iter_avif - *iter_orig); display_data(w, h, title, (unsigned int *)data); } int main() { Ecore_Evas *ee; Evas *evas; Evas_Object *o; Evas_Load_Error err; unsigned int *data; int w,h; ecore_evas_init(); ee = ecore_evas_new(NULL, 0, 0, 1, 1, NULL); if (!ee) return 1; evas = ecore_evas_get(ee); ecore_evas_title_set(ee, "original"); ecore_evas_callback_delete_request_set(ee, _quit); o = evas_object_image_add(evas); evas_object_image_file_set(o, "x1d-II-sample-02.fff", NULL); err = evas_object_image_load_error_get(o); if (err != EVAS_LOAD_ERROR_NONE) { fprintf(stderr, "could not load image '%s'. error string is \"%s\"\n", "x1d-II-sample-02.fff", evas_load_error_str(err)); fflush(stderr); return 1; } evas_object_image_size_get(o, &w, &h); evas_object_image_fill_set(o, 0, 0, w, h); evas_object_image_size_set(o, w, h); evas_object_move(o, 0, 0); evas_object_resize(o, w, h); evas_object_show(o); data = evas_object_image_data_get(o, 1); ecore_evas_resize(ee, w, h); ecore_evas_show(ee); /* evas_object_image_save(o, "test_100.jpg", NULL, "quality=100"); */ evas_object_image_save(o, "test_90.jpg", NULL, "quality=90"); /* evas_object_image_save(o, "test_70.jpg", NULL, "quality=70"); */ /* evas_object_image_save(o, "test_50.jpg", NULL, "quality=50"); */ /* evas_object_image_save(o, "test_100.avif", NULL, "quality=100"); */ evas_object_image_save(o, "test_90.avif", NULL, "quality=90"); /* evas_object_image_save(o, "test_70.avif", NULL, "quality=70"); */ /* evas_object_image_save(o, "test_50.avif", NULL, "quality=50"); */ compare(90, w, h, data); ecore_main_loop_begin(); ecore_evas_shutdown(); return 0; } ``` the raw file canbe found here : https://www.hasselblad.com/learn/sample-images/ Test Plan: test executable with avif files found in libavif project Reviewers: raster, q66 Reviewed By: q66 Subscribers: q66, cedric, #reviewers, #committers Tags: #efl Differential Revision: https://phab.enlightenment.org/D12051
-rw-r--r--meson_options.txt4
-rw-r--r--po/POTFILES.in2
-rw-r--r--src/lib/evas/common/evas_image_load.c6
-rw-r--r--src/lib/evas/common/evas_image_save.c2
-rw-r--r--src/lib/evas/file/evas_module.c8
-rw-r--r--src/lib/evas/meson.build3
-rw-r--r--src/modules/evas/image_loaders/avif/evas_image_load_avif.c382
-rw-r--r--src/modules/evas/image_savers/avif/evas_image_save_avif.c181
8 files changed, 585 insertions, 3 deletions
diff --git a/meson_options.txt b/meson_options.txt
index c6f749e1f7..c891630f04 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -189,8 +189,8 @@ option('unmount-path',
option('evas-loaders-disabler',
type : 'array',
description : 'List of modular image/vector loaders to disable in efl',
- choices : ['gst', 'pdf', 'ps', 'raw', 'svg', 'rsvg', 'xcf', 'bmp', 'dds', 'eet', 'generic', 'gif', 'ico', 'jp2k', 'jpeg', 'pmaps', 'png', 'psd', 'tga', 'tgv', 'tiff', 'wbmp', 'webp', 'xpm', 'json'],
- value : ['json']
+ choices : ['gst', 'pdf', 'ps', 'raw', 'svg', 'rsvg', 'xcf', 'bmp', 'dds', 'eet', 'generic', 'gif', 'ico', 'jp2k', 'jpeg', 'pmaps', 'png', 'psd', 'tga', 'tgv', 'tiff', 'wbmp', 'webp', 'xpm', 'json', 'avif'],
+ value : ['json', 'avif']
)
option('ecore-imf-loaders-disabler',
diff --git a/po/POTFILES.in b/po/POTFILES.in
index d6879babc1..fa4061f352 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -495,12 +495,14 @@ src/modules/evas/image_loaders/pmaps/evas_image_load_pmaps.c
src/modules/evas/image_loaders/generic/evas_image_load_generic.c
src/modules/evas/image_loaders/gif/evas_image_load_gif.c
src/modules/evas/image_loaders/tgv/evas_image_load_tgv.c
+src/modules/evas/image_loaders/avif/evas_image_load_avif.c
src/modules/evas/image_savers/jpeg/evas_image_save_jpeg.c
src/modules/evas/image_savers/png/evas_image_save_png.c
src/modules/evas/image_savers/tiff/evas_image_save_tiff.c
src/modules/evas/image_savers/eet/evas_image_save_eet.c
src/modules/evas/image_savers/webp/evas_image_save_webp.c
src/modules/evas/image_savers/tgv/evas_image_save_tgv.c
+src/modules/evas/image_savers/avif/evas_image_save_avif.c
src/modules/evas/vg_loaders/json/evas_vg_load_json.c
src/modules/evas/vg_loaders/eet/evas_vg_load_eet.c
src/modules/evas/vg_loaders/svg/evas_vg_load_svg.c
diff --git a/src/lib/evas/common/evas_image_load.c b/src/lib/evas/common/evas_image_load.c
index 1d28253c7e..96caa1627e 100644
--- a/src/lib/evas/common/evas_image_load.c
+++ b/src/lib/evas/common/evas_image_load.c
@@ -66,6 +66,9 @@ static const struct ext_loader_s loaders[] =
MATCHING(".dds", "dds"),
+ MATCHING(".avif", "avif"),
+ MATCHING(".avifs", "avif"),
+
/* xcf - gefenric */
MATCHING(".xcf", "generic"),
MATCHING(".xcf.gz", "generic"),
@@ -93,6 +96,7 @@ static const struct ext_loader_s loaders[] =
MATCHING(".dcr", "generic"),
MATCHING(".dng", "generic"),
MATCHING(".erf", "generic"),
+ MATCHING(".fff", "generic"),
MATCHING(".k25", "generic"),
MATCHING(".kdc", "generic"),
MATCHING(".mrw", "generic"),
@@ -163,7 +167,7 @@ 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", "generic"
+ "bmp", "tga", "wbmp", "ico", "psd", "jp2k", "dds", "avif", "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 9a6a7621c1..45718c5b9f 100644
--- a/src/lib/evas/common/evas_image_save.c
+++ b/src/lib/evas/common/evas_image_save.c
@@ -33,6 +33,8 @@ evas_common_save_image_to_file(RGBA_Image *im, const char *file, const char *key
saver = "webp";
if (!strcasecmp(p, "tgv"))
saver = "tgv";
+ if (!strcasecmp(p, "avif"))
+ saver = "avif";
}
if (saver)
diff --git a/src/lib/evas/file/evas_module.c b/src/lib/evas/file/evas_module.c
index a1e8b623af..83f1d804df 100644
--- a/src/lib/evas/file/evas_module.c
+++ b/src/lib/evas/file/evas_module.c
@@ -196,6 +196,7 @@ EVAS_EINA_STATIC_MODULE_DEFINE(vg_loader, json);
#endif
#if !EVAS_MODULE_NO_IMAGE_LOADERS
+EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, avif);
EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, bmp);
EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, dds);
EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, eet);
@@ -222,6 +223,7 @@ EVAS_EINA_STATIC_MODULE_DEFINE(vg_saver, svg);
#endif
#if !EVAS_MODULE_NO_IMAGE_SAVERS
+EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, avif);
EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, eet);
EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, jpeg);
EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, png);
@@ -287,6 +289,9 @@ static const struct {
#endif
#endif
#if !EVAS_MODULE_NO_IMAGE_LOADERS
+#ifdef EVAS_STATIC_BUILD_AVIF
+ EVAS_EINA_STATIC_MODULE_USE(image_loader, avif),
+#endif
#ifdef EVAS_STATIC_BUILD_BMP
EVAS_EINA_STATIC_MODULE_USE(image_loader, bmp),
#endif
@@ -351,6 +356,9 @@ static const struct {
#endif
#endif
#if !EVAS_MODULE_NO_IMAGE_SAVERS
+#ifdef EVAS_STATIC_BUILD_AVIF
+ EVAS_EINA_STATIC_MODULE_USE(image_saver, avif),
+#endif
#ifdef EVAS_STATIC_BUILD_EET
EVAS_EINA_STATIC_MODULE_USE(image_saver, eet),
#endif
diff --git a/src/lib/evas/meson.build b/src/lib/evas/meson.build
index fca88309ad..56dabb9eb4 100644
--- a/src/lib/evas/meson.build
+++ b/src/lib/evas/meson.build
@@ -10,8 +10,10 @@ giflib = cc.find_library('gif')
webp = dependency('libwebp', version: ['>=0.5.0'], required: get_option('evas-loaders-disabler').contains('webp') == false)
webpdemux = dependency('libwebpdemux', version: ['>=0.5.0'], required: get_option('evas-loaders-disabler').contains('webp') == false)
libopenjp2 = dependency('libopenjp2', required: get_option('evas-loaders-disabler').contains('jp2k') == false)
+libavif = dependency('libavif', required: get_option('evas-loaders-disabler').contains('avif') == false)
evas_image_loaders_file = [
+ ['avif', 'shared', [libavif]],
['bmp', 'shared', []],
['eet', 'static', [eet]],
['generic', 'shared', [rt]],
@@ -31,6 +33,7 @@ evas_image_loaders_file = [
]
evas_image_savers_file = [
+ ['avif', 'shared', [libavif]],
['eet', 'static', [eet]],
['jpeg', 'static', [jpeg]],
['png', 'static', [png]],
diff --git a/src/modules/evas/image_loaders/avif/evas_image_load_avif.c b/src/modules/evas/image_loaders/avif/evas_image_load_avif.c
new file mode 100644
index 0000000000..34b6da9bd3
--- /dev/null
+++ b/src/modules/evas/image_loaders/avif/evas_image_load_avif.c
@@ -0,0 +1,382 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <avif/avif.h>
+
+#include "Evas_Loader.h"
+#include "evas_common_private.h"
+
+typedef struct _Evas_Loader_Internal Evas_Loader_Internal;
+struct _Evas_Loader_Internal
+{
+ Eina_File *f;
+ Evas_Image_Load_Opts *opts;
+ Evas_Image_Animated *animated;
+ avifDecoder *decoder;
+ double duration;
+};
+
+static int _evas_loader_avif_log_dom = -1;
+
+#ifdef ERR
+# undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_evas_loader_avif_log_dom, __VA_ARGS__)
+
+#ifdef WRN
+# undef WRN
+#endif
+#define WRN(...) EINA_LOG_DOM_WARN(_evas_loader_avif_log_dom, __VA_ARGS__)
+
+#ifdef INF
+# undef INF
+#endif
+#define INF(...) EINA_LOG_DOM_INFO(_evas_loader_avif_log_dom, __VA_ARGS__)
+
+static Eina_Bool
+evas_image_load_file_head_avif_internal(Evas_Loader_Internal *loader,
+ Emile_Image_Property *prop,
+ void *map, size_t length,
+ int *error)
+{
+ Evas_Image_Animated *animated;
+ avifROData raw;
+ avifDecoder *decoder;
+ avifResult res;
+ Eina_Bool ret;
+
+ animated = loader->animated;
+
+ ret = EINA_FALSE;
+ prop->w = 0;
+ prop->h = 0;
+ prop->alpha = EINA_FALSE;
+
+ raw.size = length;
+ raw.data = (const uint8_t *)map;
+
+ decoder = avifDecoderCreate();
+ if (!decoder)
+ {
+ *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
+ return ret;
+ }
+
+ res = avifDecoderParse(decoder, &raw);
+ if (res != AVIF_RESULT_OK)
+ {
+ ERR("avif file format invalid");
+ *error = EVAS_LOAD_ERROR_GENERIC;
+ goto destroy_decoder;
+ }
+
+ if (decoder->imageCount < 1)
+ {
+ ERR("avif file format invalid");
+ *error = EVAS_LOAD_ERROR_GENERIC;
+ goto destroy_decoder;
+ }
+
+ res = avifDecoderNextImage(decoder);
+ if (res != AVIF_RESULT_OK)
+ {
+ ERR("avif file format invalid");
+ *error = EVAS_LOAD_ERROR_GENERIC;
+ goto destroy_decoder;
+ }
+
+ prop->w = decoder->image->width;
+ prop->h = decoder->image->height;
+
+ /* if size is invalid, we exit */
+ if ((prop->w < 1) || (prop->h < 1) ||
+ (prop->w > IMG_MAX_SIZE) || (prop->h > IMG_MAX_SIZE) ||
+ IMG_TOO_BIG(prop->w, prop->h))
+ {
+ if (IMG_TOO_BIG(prop->w, prop->h))
+ *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
+ else
+ *error= EVAS_LOAD_ERROR_GENERIC;
+ goto destroy_decoder;
+ }
+
+ prop->alpha = !!decoder->image->alphaPlane;
+
+ if (decoder->imageCount > 1)
+ {
+ animated->loop_hint = EVAS_IMAGE_ANIMATED_HINT_NONE;
+ animated->frame_count = decoder->imageCount;
+ animated->loop_count = 1;
+ animated->animated = EINA_TRUE;
+ loader->duration = decoder->duration / decoder->imageCount;
+ }
+
+ *error = EVAS_LOAD_ERROR_NONE;
+ ret = EINA_TRUE;
+
+ destroy_decoder:
+ avifDecoderDestroy(decoder);
+
+ return ret;
+}
+
+static Eina_Bool
+evas_image_load_file_data_avif_internal(Evas_Loader_Internal *loader,
+ void *pixels,
+ void *map, size_t length,
+ int *error)
+{
+ avifRGBImage rgb;
+ avifDecoder *decoder;
+ avifResult res;
+ Evas_Image_Animated *animated;
+ Eina_Bool ret;
+
+ ret = EINA_FALSE;
+
+ /* FIXME: create decoder in evas_image_load_file_data_avif instead ? */
+ decoder = loader->decoder;
+ if (!decoder)
+ {
+ avifROData raw;
+ decoder = avifDecoderCreate();
+ if (!decoder)
+ {
+ *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
+ return EINA_FALSE;
+ }
+
+ raw.size = length;
+ raw.data = (const uint8_t *)map;
+
+ res = avifDecoderParse(decoder, &raw);
+ if (res != AVIF_RESULT_OK)
+ {
+ *error = EVAS_LOAD_ERROR_GENERIC;
+ goto on_error;
+ }
+
+ loader->decoder = decoder;
+ }
+
+ animated = loader->animated;
+ if (animated->animated)
+ {
+ /* FIXME: next image instead ? */
+ res = avifDecoderNthImage(decoder, animated->cur_frame + 1);
+ if (res != AVIF_RESULT_OK)
+ {
+ *error = EVAS_LOAD_ERROR_GENERIC;
+ goto on_error;
+ }
+ }
+ else
+ {
+ res = avifDecoderNextImage(decoder);
+ if (res != AVIF_RESULT_OK)
+ {
+ *error = EVAS_LOAD_ERROR_GENERIC;
+ goto on_error;
+ }
+ }
+
+ avifRGBImageSetDefaults(&rgb, decoder->image);
+#ifdef WORDS_BIGENDIAN
+ rgb.format = AVIF_RGB_FORMAT_ARGB;
+#else
+ rgb.format = AVIF_RGB_FORMAT_BGRA;
+#endif
+ rgb.depth = 8;
+ rgb.pixels = pixels;
+ rgb.rowBytes = 4 * decoder->image->width;
+
+ avifImageYUVToRGB(decoder->image, &rgb);
+
+ *error = EVAS_LOAD_ERROR_NONE;
+
+ ret = EINA_TRUE;
+
+ on_error:
+
+ return ret;
+}
+
+static void *
+evas_image_load_file_open_avif(Eina_File *f, Eina_Stringshare *key EINA_UNUSED,
+ Evas_Image_Load_Opts *opts,
+ Evas_Image_Animated *animated,
+ 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;
+ loader->animated = animated;
+
+ return loader;
+}
+
+static void
+evas_image_load_file_close_avif(void *loader_data)
+{
+ Evas_Loader_Internal *loader;
+
+ loader = loader_data;
+ avifDecoderDestroy(loader->decoder);
+ free(loader_data);
+}
+
+static Eina_Bool
+evas_image_load_file_head_avif(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_avif_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_avif(void *loader_data,
+ Evas_Image_Property *prop EINA_UNUSED,
+ 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_avif_internal(loader,
+ pixels,
+ map, eina_file_size_get(f),
+ error);
+
+ eina_file_map_free(f, map);
+
+ on_error:
+ return val;
+}
+
+static double
+evas_image_load_frame_duration_avif(void *loader_data,
+ int start_frame,
+ int frame_num)
+{
+ Evas_Loader_Internal *loader;
+ Evas_Image_Animated *animated;
+
+ loader = (Evas_Loader_Internal *)loader_data;
+ animated = loader->animated;
+
+ if (!animated->animated)
+ return -1.0;
+
+ if (frame_num < 0)
+ return -1.0;
+
+ if ((start_frame + frame_num) > animated->frame_count)
+ return -1.0;
+
+ if (frame_num < 1)
+ frame_num = 1;
+
+ return loader->duration;
+}
+
+static Evas_Image_Load_Func evas_image_load_avif_func =
+{
+ EVAS_IMAGE_LOAD_VERSION,
+ evas_image_load_file_open_avif,
+ evas_image_load_file_close_avif,
+ evas_image_load_file_head_avif,
+ NULL,
+ evas_image_load_file_data_avif,
+ evas_image_load_frame_duration_avif,
+ EINA_TRUE,
+ EINA_TRUE
+};
+
+static int
+module_open(Evas_Module *em)
+{
+ if (!em) return 0;
+
+ _evas_loader_avif_log_dom = eina_log_domain_register("evas-avif", EINA_COLOR_BLUE);
+ if (_evas_loader_avif_log_dom < 0)
+ {
+ EINA_LOG_ERR("Can not create a module log domain.");
+ return 0;
+ }
+
+ em->functions = (void *)(&evas_image_load_avif_func);
+
+ return 1;
+}
+
+static void
+module_close(Evas_Module *em EINA_UNUSED)
+{
+ if (_evas_loader_avif_log_dom >= 0)
+ {
+ eina_log_domain_unregister(_evas_loader_avif_log_dom);
+ _evas_loader_avif_log_dom = -1;
+ }
+}
+
+static Evas_Module_Api evas_modapi =
+{
+ EVAS_MODULE_API_VERSION,
+ "avif",
+ "none",
+ {
+ module_open,
+ module_close
+ }
+};
+
+EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, avif);
+
+#ifndef EVAS_STATIC_BUILD_AVIF
+EVAS_EINA_MODULE_DEFINE(image_loader, avif);
+#endif
+
diff --git a/src/modules/evas/image_savers/avif/evas_image_save_avif.c b/src/modules/evas/image_savers/avif/evas_image_save_avif.c
new file mode 100644
index 0000000000..61fefcefc7
--- /dev/null
+++ b/src/modules/evas/image_savers/avif/evas_image_save_avif.c
@@ -0,0 +1,181 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <avif/avif.h>
+
+#include "evas_common_private.h"
+#include "evas_private.h"
+
+
+static int
+save_image_avif(RGBA_Image *im, const char *file, int quality)
+{
+ FILE *f;
+ avifRGBImage rgb;
+ avifRWData output;
+ avifImage *image;
+ avifEncoder * encoder;
+ avifPixelFormat format;
+ avifResult res;
+ size_t size;
+ int threads_count;
+ int quantizer;
+ int color;
+ int transfer;
+ int matrix;
+ int ret = 0;
+
+ if (!im || !im->image.data || !file || !*file)
+ return 0;
+
+ f = fopen(file, "wb");
+ if (!f)
+ return ret;
+
+ if (quality < 60)
+ {
+ format = AVIF_PIXEL_FORMAT_YUV420;
+#if (AVIF_VERSION < 704)
+ matrix = AVIF_NCLX_MATRIX_COEFFICIENTS_BT601;
+ color = AVIF_NCLX_COLOUR_PRIMARIES_BT601;
+#else
+ matrix = AVIF_MATRIX_COEFFICIENTS_BT601;
+ color = AVIF_COLOR_PRIMARIES_BT601;
+#endif
+ }
+ else if (quality >= 90)
+ {
+ format = AVIF_PIXEL_FORMAT_YUV444;
+#if (AVIF_VERSION < 704)
+ matrix = AVIF_NCLX_MATRIX_COEFFICIENTS_BT709;
+ color = AVIF_NCLX_COLOUR_PRIMARIES_BT709;
+#else
+ matrix = AVIF_MATRIX_COEFFICIENTS_BT709;
+ color = AVIF_COLOR_PRIMARIES_BT709;
+#endif
+ }
+ else
+ {
+ format = AVIF_PIXEL_FORMAT_YUV422;
+#if (AVIF_VERSION < 704)
+ matrix = AVIF_NCLX_MATRIX_COEFFICIENTS_BT709;
+ color = AVIF_NCLX_COLOUR_PRIMARIES_BT709;
+#else
+ matrix = AVIF_MATRIX_COEFFICIENTS_BT709;
+ color = AVIF_COLOR_PRIMARIES_BT709;
+#endif
+ }
+
+#if (AVIF_VERSION < 704)
+ transfer = AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB;
+#else
+ transfer = AVIF_TRANSFER_CHARACTERISTICS_SRGB;
+#endif
+
+ image = avifImageCreate(im->cache_entry.w, im->cache_entry.h, 8, format);
+ if (!image)
+ goto close_f;
+
+#if (AVIF_VERSION < 704)
+ image->nclx.colourPrimaries = color;
+ image->nclx.transferCharacteristics = transfer;
+ image->nclx.matrixCoefficients = matrix;
+#else
+ image->colorPrimaries = color;
+ image->transferCharacteristics = transfer;
+ image->matrixCoefficients = matrix;
+#endif
+ image->yuvRange = AVIF_RANGE_FULL;
+
+ avifRGBImageSetDefaults(&rgb, image);
+#ifdef WORDS_BIGENDIAN
+ rgb.format = AVIF_RGB_FORMAT_ARGB;
+#else
+ rgb.format = AVIF_RGB_FORMAT_BGRA;
+#endif
+ rgb.depth = 8;
+ rgb.pixels = (uint8_t *)im->image.data;
+ rgb.rowBytes = 4 * im->cache_entry.w;
+ avifImageRGBToYUV(image, &rgb);
+
+ output.data = NULL;
+ output.size = 0;
+ encoder = avifEncoderCreate();
+ if (!encoder)
+ goto destroy_image;
+
+ threads_count = 1;
+ if (eina_cpu_count() > 2)
+ threads_count = eina_cpu_count() - 1;
+
+ quantizer = ((100 - quality) * AVIF_QUANTIZER_WORST_QUALITY) / 100;
+
+ encoder->maxThreads = threads_count;
+ encoder->minQuantizer = quantizer;
+ encoder->maxQuantizer = quantizer;
+ res = avifEncoderWrite(encoder, image, &output);
+
+ if (res != AVIF_RESULT_OK)
+ goto destroy_encoder;
+
+ size = fwrite(output.data, output.size, 1, f);
+ if (size != output.size)
+ goto destroy_encoder;
+
+ ret = 1;
+
+ destroy_encoder:
+ avifEncoderDestroy(encoder);
+ avifRWDataFree(&output);
+ destroy_image:
+ avifImageDestroy(image);
+ close_f:
+ fclose(f);
+
+ return ret;
+}
+
+static int evas_image_save_file_avif(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_avif(im, file, quality);
+}
+
+
+static Evas_Image_Save_Func evas_image_save_avif_func =
+{
+ evas_image_save_file_avif
+};
+
+static int
+module_open(Evas_Module *em)
+{
+ if (!em) return 0;
+ em->functions = (void *)(&evas_image_save_avif_func);
+ return 1;
+}
+
+static void
+module_close(Evas_Module *em EINA_UNUSED)
+{
+}
+
+static Evas_Module_Api evas_modapi =
+{
+ EVAS_MODULE_API_VERSION,
+ "avif",
+ "none",
+ {
+ module_open,
+ module_close
+ }
+};
+
+EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_SAVER, image_saver, avif);
+
+#ifndef EVAS_STATIC_BUILD_AVIF
+EVAS_EINA_MODULE_DEFINE(image_saver, avif);
+#endif