summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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',
189option('evas-loaders-disabler', 189option('evas-loaders-disabler',
190 type : 'array', 190 type : 'array',
191 description : 'List of modular image/vector loaders to disable in efl', 191 description : 'List of modular image/vector loaders to disable in efl',
192 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'], 192 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'],
193 value : ['json'] 193 value : ['json', 'avif']
194) 194)
195 195
196option('ecore-imf-loaders-disabler', 196option('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
495src/modules/evas/image_loaders/generic/evas_image_load_generic.c 495src/modules/evas/image_loaders/generic/evas_image_load_generic.c
496src/modules/evas/image_loaders/gif/evas_image_load_gif.c 496src/modules/evas/image_loaders/gif/evas_image_load_gif.c
497src/modules/evas/image_loaders/tgv/evas_image_load_tgv.c 497src/modules/evas/image_loaders/tgv/evas_image_load_tgv.c
498src/modules/evas/image_loaders/avif/evas_image_load_avif.c
498src/modules/evas/image_savers/jpeg/evas_image_save_jpeg.c 499src/modules/evas/image_savers/jpeg/evas_image_save_jpeg.c
499src/modules/evas/image_savers/png/evas_image_save_png.c 500src/modules/evas/image_savers/png/evas_image_save_png.c
500src/modules/evas/image_savers/tiff/evas_image_save_tiff.c 501src/modules/evas/image_savers/tiff/evas_image_save_tiff.c
501src/modules/evas/image_savers/eet/evas_image_save_eet.c 502src/modules/evas/image_savers/eet/evas_image_save_eet.c
502src/modules/evas/image_savers/webp/evas_image_save_webp.c 503src/modules/evas/image_savers/webp/evas_image_save_webp.c
503src/modules/evas/image_savers/tgv/evas_image_save_tgv.c 504src/modules/evas/image_savers/tgv/evas_image_save_tgv.c
505src/modules/evas/image_savers/avif/evas_image_save_avif.c
504src/modules/evas/vg_loaders/json/evas_vg_load_json.c 506src/modules/evas/vg_loaders/json/evas_vg_load_json.c
505src/modules/evas/vg_loaders/eet/evas_vg_load_eet.c 507src/modules/evas/vg_loaders/eet/evas_vg_load_eet.c
506src/modules/evas/vg_loaders/svg/evas_vg_load_svg.c 508src/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[] =
66 66
67 MATCHING(".dds", "dds"), 67 MATCHING(".dds", "dds"),
68 68
69 MATCHING(".avif", "avif"),
70 MATCHING(".avifs", "avif"),
71
69 /* xcf - gefenric */ 72 /* xcf - gefenric */
70 MATCHING(".xcf", "generic"), 73 MATCHING(".xcf", "generic"),
71 MATCHING(".xcf.gz", "generic"), 74 MATCHING(".xcf.gz", "generic"),
@@ -93,6 +96,7 @@ static const struct ext_loader_s loaders[] =
93 MATCHING(".dcr", "generic"), 96 MATCHING(".dcr", "generic"),
94 MATCHING(".dng", "generic"), 97 MATCHING(".dng", "generic"),
95 MATCHING(".erf", "generic"), 98 MATCHING(".erf", "generic"),
99 MATCHING(".fff", "generic"),
96 MATCHING(".k25", "generic"), 100 MATCHING(".k25", "generic"),
97 MATCHING(".kdc", "generic"), 101 MATCHING(".kdc", "generic"),
98 MATCHING(".mrw", "generic"), 102 MATCHING(".mrw", "generic"),
@@ -163,7 +167,7 @@ static const struct ext_loader_s loaders[] =
163static const char *loaders_name[] = 167static const char *loaders_name[] =
164{ /* in order of most likely needed */ 168{ /* in order of most likely needed */
165 "png", "jpeg", "eet", "xpm", "tiff", "gif", "svg", "webp", "pmaps", 169 "png", "jpeg", "eet", "xpm", "tiff", "gif", "svg", "webp", "pmaps",
166 "bmp", "tga", "wbmp", "ico", "psd", "jp2k", "dds", "generic" 170 "bmp", "tga", "wbmp", "ico", "psd", "jp2k", "dds", "avif", "generic"
167}; 171};
168 172
169struct evas_image_foreach_loader_data 173struct 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
33 saver = "webp"; 33 saver = "webp";
34 if (!strcasecmp(p, "tgv")) 34 if (!strcasecmp(p, "tgv"))
35 saver = "tgv"; 35 saver = "tgv";
36 if (!strcasecmp(p, "avif"))
37 saver = "avif";
36 } 38 }
37 39
38 if (saver) 40 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);
196#endif 196#endif
197 197
198#if !EVAS_MODULE_NO_IMAGE_LOADERS 198#if !EVAS_MODULE_NO_IMAGE_LOADERS
199EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, avif);
199EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, bmp); 200EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, bmp);
200EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, dds); 201EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, dds);
201EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, eet); 202EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, eet);
@@ -222,6 +223,7 @@ EVAS_EINA_STATIC_MODULE_DEFINE(vg_saver, svg);
222#endif 223#endif
223 224
224#if !EVAS_MODULE_NO_IMAGE_SAVERS 225#if !EVAS_MODULE_NO_IMAGE_SAVERS
226EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, avif);
225EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, eet); 227EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, eet);
226EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, jpeg); 228EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, jpeg);
227EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, png); 229EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, png);
@@ -287,6 +289,9 @@ static const struct {
287#endif 289#endif
288#endif 290#endif
289#if !EVAS_MODULE_NO_IMAGE_LOADERS 291#if !EVAS_MODULE_NO_IMAGE_LOADERS
292#ifdef EVAS_STATIC_BUILD_AVIF
293 EVAS_EINA_STATIC_MODULE_USE(image_loader, avif),
294#endif
290#ifdef EVAS_STATIC_BUILD_BMP 295#ifdef EVAS_STATIC_BUILD_BMP
291 EVAS_EINA_STATIC_MODULE_USE(image_loader, bmp), 296 EVAS_EINA_STATIC_MODULE_USE(image_loader, bmp),
292#endif 297#endif
@@ -351,6 +356,9 @@ static const struct {
351#endif 356#endif
352#endif 357#endif
353#if !EVAS_MODULE_NO_IMAGE_SAVERS 358#if !EVAS_MODULE_NO_IMAGE_SAVERS
359#ifdef EVAS_STATIC_BUILD_AVIF
360 EVAS_EINA_STATIC_MODULE_USE(image_saver, avif),
361#endif
354#ifdef EVAS_STATIC_BUILD_EET 362#ifdef EVAS_STATIC_BUILD_EET
355 EVAS_EINA_STATIC_MODULE_USE(image_saver, eet), 363 EVAS_EINA_STATIC_MODULE_USE(image_saver, eet),
356#endif 364#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')
10webp = dependency('libwebp', version: ['>=0.5.0'], required: get_option('evas-loaders-disabler').contains('webp') == false) 10webp = dependency('libwebp', version: ['>=0.5.0'], required: get_option('evas-loaders-disabler').contains('webp') == false)
11webpdemux = dependency('libwebpdemux', version: ['>=0.5.0'], required: get_option('evas-loaders-disabler').contains('webp') == false) 11webpdemux = dependency('libwebpdemux', version: ['>=0.5.0'], required: get_option('evas-loaders-disabler').contains('webp') == false)
12libopenjp2 = dependency('libopenjp2', required: get_option('evas-loaders-disabler').contains('jp2k') == false) 12libopenjp2 = dependency('libopenjp2', required: get_option('evas-loaders-disabler').contains('jp2k') == false)
13libavif = dependency('libavif', required: get_option('evas-loaders-disabler').contains('avif') == false)
13 14
14evas_image_loaders_file = [ 15evas_image_loaders_file = [
16 ['avif', 'shared', [libavif]],
15 ['bmp', 'shared', []], 17 ['bmp', 'shared', []],
16 ['eet', 'static', [eet]], 18 ['eet', 'static', [eet]],
17 ['generic', 'shared', [rt]], 19 ['generic', 'shared', [rt]],
@@ -31,6 +33,7 @@ evas_image_loaders_file = [
31] 33]
32 34
33evas_image_savers_file = [ 35evas_image_savers_file = [
36 ['avif', 'shared', [libavif]],
34 ['eet', 'static', [eet]], 37 ['eet', 'static', [eet]],
35 ['jpeg', 'static', [jpeg]], 38 ['jpeg', 'static', [jpeg]],
36 ['png', 'static', [png]], 39 ['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 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#include <stdio.h>
6
7#include <avif/avif.h>
8
9#include "Evas_Loader.h"
10#include "evas_common_private.h"
11
12typedef struct _Evas_Loader_Internal Evas_Loader_Internal;
13struct _Evas_Loader_Internal
14{
15 Eina_File *f;
16 Evas_Image_Load_Opts *opts;
17 Evas_Image_Animated *animated;
18 avifDecoder *decoder;
19 double duration;
20};
21
22static int _evas_loader_avif_log_dom = -1;
23
24#ifdef ERR
25# undef ERR
26#endif
27#define ERR(...) EINA_LOG_DOM_ERR(_evas_loader_avif_log_dom, __VA_ARGS__)
28
29#ifdef WRN
30# undef WRN
31#endif
32#define WRN(...) EINA_LOG_DOM_WARN(_evas_loader_avif_log_dom, __VA_ARGS__)
33
34#ifdef INF
35# undef INF
36#endif
37#define INF(...) EINA_LOG_DOM_INFO(_evas_loader_avif_log_dom, __VA_ARGS__)
38
39static Eina_Bool
40evas_image_load_file_head_avif_internal(Evas_Loader_Internal *loader,
41 Emile_Image_Property *prop,
42 void *map, size_t length,
43 int *error)
44{
45 Evas_Image_Animated *animated;
46 avifROData raw;
47 avifDecoder *decoder;
48 avifResult res;
49 Eina_Bool ret;
50
51 animated = loader->animated;
52
53 ret = EINA_FALSE;
54 prop->w = 0;
55 prop->h = 0;
56 prop->alpha = EINA_FALSE;
57
58 raw.size = length;
59 raw.data = (const uint8_t *)map;
60
61 decoder = avifDecoderCreate();
62 if (!decoder)
63 {
64 *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
65 return ret;
66 }
67
68 res = avifDecoderParse(decoder, &raw);
69 if (res != AVIF_RESULT_OK)
70 {
71 ERR("avif file format invalid");
72 *error = EVAS_LOAD_ERROR_GENERIC;
73 goto destroy_decoder;
74 }
75
76 if (decoder->imageCount < 1)
77 {
78 ERR("avif file format invalid");
79 *error = EVAS_LOAD_ERROR_GENERIC;
80 goto destroy_decoder;
81 }
82
83 res = avifDecoderNextImage(decoder);
84 if (res != AVIF_RESULT_OK)
85 {
86 ERR("avif file format invalid");
87 *error = EVAS_LOAD_ERROR_GENERIC;
88 goto destroy_decoder;
89 }
90
91 prop->w = decoder->image->width;
92 prop->h = decoder->image->height;
93
94 /* if size is invalid, we exit */
95 if ((prop->w < 1) || (prop->h < 1) ||
96 (prop->w > IMG_MAX_SIZE) || (prop->h > IMG_MAX_SIZE) ||
97 IMG_TOO_BIG(prop->w, prop->h))
98 {
99 if (IMG_TOO_BIG(prop->w, prop->h))
100 *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
101 else
102 *error= EVAS_LOAD_ERROR_GENERIC;
103 goto destroy_decoder;
104 }
105
106 prop->alpha = !!decoder->image->alphaPlane;
107
108 if (decoder->imageCount > 1)
109 {
110 animated->loop_hint = EVAS_IMAGE_ANIMATED_HINT_NONE;
111 animated->frame_count = decoder->imageCount;
112 animated->loop_count = 1;
113 animated->animated = EINA_TRUE;
114 loader->duration = decoder->duration / decoder->imageCount;
115 }
116
117 *error = EVAS_LOAD_ERROR_NONE;
118 ret = EINA_TRUE;
119
120 destroy_decoder:
121 avifDecoderDestroy(decoder);
122
123 return ret;
124}
125
126static Eina_Bool
127evas_image_load_file_data_avif_internal(Evas_Loader_Internal *loader,
128 void *pixels,
129 void *map, size_t length,
130 int *error)
131{
132 avifRGBImage rgb;
133 avifDecoder *decoder;
134 avifResult res;
135 Evas_Image_Animated *animated;
136 Eina_Bool ret;
137
138 ret = EINA_FALSE;
139
140 /* FIXME: create decoder in evas_image_load_file_data_avif instead ? */
141 decoder = loader->decoder;
142 if (!decoder)
143 {
144 avifROData raw;
145 decoder = avifDecoderCreate();
146 if (!decoder)
147 {
148 *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
149 return EINA_FALSE;
150 }
151
152 raw.size = length;
153 raw.data = (const uint8_t *)map;
154
155 res = avifDecoderParse(decoder, &raw);
156 if (res != AVIF_RESULT_OK)
157 {
158 *error = EVAS_LOAD_ERROR_GENERIC;
159 goto on_error;
160 }
161
162 loader->decoder = decoder;
163 }
164
165 animated = loader->animated;
166 if (animated->animated)
167 {
168 /* FIXME: next image instead ? */
169 res = avifDecoderNthImage(decoder, animated->cur_frame + 1);
170 if (res != AVIF_RESULT_OK)
171 {
172 *error = EVAS_LOAD_ERROR_GENERIC;
173 goto on_error;
174 }
175 }
176 else
177 {
178 res = avifDecoderNextImage(decoder);
179 if (res != AVIF_RESULT_OK)
180 {
181 *error = EVAS_LOAD_ERROR_GENERIC;
182 goto on_error;
183 }
184 }
185
186 avifRGBImageSetDefaults(&rgb, decoder->image);
187#ifdef WORDS_BIGENDIAN
188 rgb.format = AVIF_RGB_FORMAT_ARGB;
189#else
190 rgb.format = AVIF_RGB_FORMAT_BGRA;
191#endif
192 rgb.depth = 8;
193 rgb.pixels = pixels;
194 rgb.rowBytes = 4 * decoder->image->width;
195
196 avifImageYUVToRGB(decoder->image, &rgb);
197
198 *error = EVAS_LOAD_ERROR_NONE;
199
200 ret = EINA_TRUE;
201
202 on_error:
203
204 return ret;
205}
206
207static void *
208evas_image_load_file_open_avif(Eina_File *f, Eina_Stringshare *key EINA_UNUSED,
209 Evas_Image_Load_Opts *opts,
210 Evas_Image_Animated *animated,
211 int *error)
212{
213 Evas_Loader_Internal *loader;
214
215 loader = calloc(1, sizeof (Evas_Loader_Internal));
216 if (!loader)
217 {
218 *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
219 return NULL;
220 }
221
222 loader->f = f;
223 loader->opts = opts;
224 loader->animated = animated;
225
226 return loader;
227}
228
229static void
230evas_image_load_file_close_avif(void *loader_data)
231{
232 Evas_Loader_Internal *loader;
233
234 loader = loader_data;
235 avifDecoderDestroy(loader->decoder);
236 free(loader_data);
237}
238
239static Eina_Bool
240evas_image_load_file_head_avif(void *loader_data,
241 Evas_Image_Property *prop,
242 int *error)
243{
244 Evas_Loader_Internal *loader = loader_data;
245 Eina_File *f;
246 void *map;
247 Eina_Bool val;
248
249 f = loader->f;
250
251 map = eina_file_map_all(f, EINA_FILE_RANDOM);
252 if (!map)
253 {
254 *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST;
255 return EINA_FALSE;
256 }
257
258 val = evas_image_load_file_head_avif_internal(loader,
259 (Emile_Image_Property *)prop,
260 map, eina_file_size_get(f),
261 error);
262
263 eina_file_map_free(f, map);
264
265 return val;
266}
267
268static Eina_Bool
269evas_image_load_file_data_avif(void *loader_data,
270 Evas_Image_Property *prop EINA_UNUSED,
271 void *pixels,
272 int *error)
273{
274 Evas_Loader_Internal *loader;
275 Eina_File *f;
276 void *map;
277 Eina_Bool val = EINA_FALSE;
278
279 loader = (Evas_Loader_Internal *)loader_data;
280 f = loader->f;
281
282 map = eina_file_map_all(f, EINA_FILE_WILLNEED);
283 if (!map)
284 {
285 *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST;
286 goto on_error;
287 }
288
289 val = evas_image_load_file_data_avif_internal(loader,
290 pixels,
291 map, eina_file_size_get(f),
292 error);
293
294 eina_file_map_free(f, map);
295
296 on_error:
297 return val;
298}
299
300static double
301evas_image_load_frame_duration_avif(void *loader_data,
302 int start_frame,
303 int frame_num)
304{
305 Evas_Loader_Internal *loader;
306 Evas_Image_Animated *animated;
307
308 loader = (Evas_Loader_Internal *)loader_data;
309 animated = loader->animated;
310
311 if (!animated->animated)
312 return -1.0;
313
314 if (frame_num < 0)
315 return -1.0;
316
317 if ((start_frame + frame_num) > animated->frame_count)
318 return -1.0;
319
320 if (frame_num < 1)
321 frame_num = 1;
322
323 return loader->duration;
324}
325
326static Evas_Image_Load_Func evas_image_load_avif_func =
327{
328 EVAS_IMAGE_LOAD_VERSION,
329 evas_image_load_file_open_avif,
330 evas_image_load_file_close_avif,
331 evas_image_load_file_head_avif,
332 NULL,
333 evas_image_load_file_data_avif,
334 evas_image_load_frame_duration_avif,
335 EINA_TRUE,
336 EINA_TRUE
337};
338
339static int
340module_open(Evas_Module *em)
341{
342 if (!em) return 0;
343
344 _evas_loader_avif_log_dom = eina_log_domain_register("evas-avif", EINA_COLOR_BLUE);
345 if (_evas_loader_avif_log_dom < 0)
346 {
347 EINA_LOG_ERR("Can not create a module log domain.");
348 return 0;
349 }
350
351 em->functions = (void *)(&evas_image_load_avif_func);
352
353 return 1;
354}
355
356static void
357module_close(Evas_Module *em EINA_UNUSED)
358{
359 if (_evas_loader_avif_log_dom >= 0)
360 {
361 eina_log_domain_unregister(_evas_loader_avif_log_dom);
362 _evas_loader_avif_log_dom = -1;
363 }
364}
365
366static Evas_Module_Api evas_modapi =
367{
368 EVAS_MODULE_API_VERSION,
369 "avif",
370 "none",
371 {
372 module_open,
373 module_close
374 }
375};
376
377EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, avif);
378
379#ifndef EVAS_STATIC_BUILD_AVIF
380EVAS_EINA_MODULE_DEFINE(image_loader, avif);
381#endif
382
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 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#include <stdio.h>
6
7#include <avif/avif.h>
8
9#include "evas_common_private.h"
10#include "evas_private.h"
11
12
13static int
14save_image_avif(RGBA_Image *im, const char *file, int quality)
15{
16 FILE *f;
17 avifRGBImage rgb;
18 avifRWData output;
19 avifImage *image;
20 avifEncoder * encoder;
21 avifPixelFormat format;
22 avifResult res;
23 size_t size;
24 int threads_count;
25 int quantizer;
26 int color;
27 int transfer;
28 int matrix;
29 int ret = 0;
30
31 if (!im || !im->image.data || !file || !*file)
32 return 0;
33
34 f = fopen(file, "wb");
35 if (!f)
36 return ret;
37
38 if (quality < 60)
39 {
40 format = AVIF_PIXEL_FORMAT_YUV420;
41#if (AVIF_VERSION < 704)
42 matrix = AVIF_NCLX_MATRIX_COEFFICIENTS_BT601;
43 color = AVIF_NCLX_COLOUR_PRIMARIES_BT601;
44#else
45 matrix = AVIF_MATRIX_COEFFICIENTS_BT601;
46 color = AVIF_COLOR_PRIMARIES_BT601;
47#endif
48 }
49 else if (quality >= 90)
50 {
51 format = AVIF_PIXEL_FORMAT_YUV444;
52#if (AVIF_VERSION < 704)
53 matrix = AVIF_NCLX_MATRIX_COEFFICIENTS_BT709;
54 color = AVIF_NCLX_COLOUR_PRIMARIES_BT709;
55#else
56 matrix = AVIF_MATRIX_COEFFICIENTS_BT709;
57 color = AVIF_COLOR_PRIMARIES_BT709;
58#endif
59 }
60 else
61 {
62 format = AVIF_PIXEL_FORMAT_YUV422;
63#if (AVIF_VERSION < 704)
64 matrix = AVIF_NCLX_MATRIX_COEFFICIENTS_BT709;
65 color = AVIF_NCLX_COLOUR_PRIMARIES_BT709;
66#else
67 matrix = AVIF_MATRIX_COEFFICIENTS_BT709;
68 color = AVIF_COLOR_PRIMARIES_BT709;
69#endif
70 }
71
72#if (AVIF_VERSION < 704)
73 transfer = AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB;
74#else
75 transfer = AVIF_TRANSFER_CHARACTERISTICS_SRGB;
76#endif
77
78 image = avifImageCreate(im->cache_entry.w, im->cache_entry.h, 8, format);
79 if (!image)
80 goto close_f;
81
82#if (AVIF_VERSION < 704)
83 image->nclx.colourPrimaries = color;
84 image->nclx.transferCharacteristics = transfer;
85 image->nclx.matrixCoefficients = matrix;
86#else
87 image->colorPrimaries = color;
88 image->transferCharacteristics = transfer;
89 image->matrixCoefficients = matrix;
90#endif
91 image->yuvRange = AVIF_RANGE_FULL;
92
93 avifRGBImageSetDefaults(&rgb, image);
94#ifdef WORDS_BIGENDIAN
95 rgb.format = AVIF_RGB_FORMAT_ARGB;
96#else
97 rgb.format = AVIF_RGB_FORMAT_BGRA;
98#endif
99 rgb.depth = 8;
100 rgb.pixels = (uint8_t *)im->image.data;
101 rgb.rowBytes = 4 * im->cache_entry.w;
102 avifImageRGBToYUV(image, &rgb);
103
104 output.data = NULL;
105 output.size = 0;
106 encoder = avifEncoderCreate();
107 if (!encoder)
108 goto destroy_image;
109
110 threads_count = 1;
111 if (eina_cpu_count() > 2)
112 threads_count = eina_cpu_count() - 1;
113
114 quantizer = ((100 - quality) * AVIF_QUANTIZER_WORST_QUALITY) / 100;
115
116 encoder->maxThreads = threads_count;
117 encoder->minQuantizer = quantizer;
118 encoder->maxQuantizer = quantizer;
119 res = avifEncoderWrite(encoder, image, &output);
120
121 if (res != AVIF_RESULT_OK)
122 goto destroy_encoder;
123
124 size = fwrite(output.data, output.size, 1, f);
125 if (size != output.size)
126 goto destroy_encoder;
127
128 ret = 1;
129
130 destroy_encoder:
131 avifEncoderDestroy(encoder);
132 avifRWDataFree(&output);
133 destroy_image:
134 avifImageDestroy(image);
135 close_f:
136 fclose(f);
137
138 return ret;
139}
140
141static int evas_image_save_file_avif(RGBA_Image *im, const char *file, const char *key EINA_UNUSED,
142 int quality, int compress EINA_UNUSED, const char *encoding EINA_UNUSED)
143{
144 return save_image_avif(im, file, quality);
145}
146
147
148static Evas_Image_Save_Func evas_image_save_avif_func =
149{
150 evas_image_save_file_avif
151};
152
153static int
154module_open(Evas_Module *em)
155{
156 if (!em) return 0;
157 em->functions = (void *)(&evas_image_save_avif_func);
158 return 1;
159}
160
161static void
162module_close(Evas_Module *em EINA_UNUSED)
163{
164}
165
166static Evas_Module_Api evas_modapi =
167{
168 EVAS_MODULE_API_VERSION,
169 "avif",
170 "none",
171 {
172 module_open,
173 module_close
174 }
175};
176
177EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_SAVER, image_saver, avif);
178
179#ifndef EVAS_STATIC_BUILD_AVIF
180EVAS_EINA_MODULE_DEFINE(image_saver, avif);
181#endif