summaryrefslogtreecommitdiff
path: root/src/modules
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 /src/modules
parenta8538bacce1457a91e28b39a6417eaadb4744b50 (diff)
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
Diffstat (limited to 'src/modules')
-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
2 files changed, 563 insertions, 0 deletions
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