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..9700d3c286 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -495,6 +495,7 @@ 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 diff --git a/src/lib/evas/common/evas_image_load.c b/src/lib/evas/common/evas_image_load.c index 1d28253c7e..5d3e3aa30f 100644 --- a/src/lib/evas/common/evas_image_load.c +++ b/src/lib/evas/common/evas_image_load.c @@ -66,6 +66,8 @@ static const struct ext_loader_s loaders[] = MATCHING(".dds", "dds"), + MATCHING(".avif", "avif"), + /* xcf - gefenric */ MATCHING(".xcf", "generic"), MATCHING(".xcf.gz", "generic"), @@ -163,7 +165,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/file/evas_module.c b/src/lib/evas/file/evas_module.c index a1e8b623af..b99118698e 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); @@ -287,6 +288,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 diff --git a/src/lib/evas/meson.build b/src/lib/evas/meson.build index fca88309ad..315aa57a63 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]], 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..88469ddf04 --- /dev/null +++ b/src/modules/evas/image_loaders/avif/evas_image_load_avif.c @@ -0,0 +1,290 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include + +#include "Evas_Loader.h" + +typedef struct _Evas_Loader_Internal Evas_Loader_Internal; +struct _Evas_Loader_Internal +{ + Eina_File *f; + Evas_Image_Load_Opts *opts; +}; + +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(unsigned int *w, unsigned int *h, + unsigned char *alpha, + void *map, size_t length, + int *error) +{ + avifROData raw; + avifDecoder *decoder; + avifImage *image; + avifResult res; + + raw.size = length; + raw.data = (const uint8_t *)map; + + decoder = avifDecoderCreate(); + if (!decoder) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + + image = avifImageCreateEmpty(); + if (!image) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto destroy_decoder; + } + + res = avifDecoderRead(decoder, image, &raw); + if (res != AVIF_RESULT_OK) + { + ERR("avif file format invalid"); + *error = EVAS_LOAD_ERROR_GENERIC; + goto destroy_image; + } + + *w = image->width; + *h = image->height; + *alpha = !!image->alphaPlane; + *error = EVAS_LOAD_ERROR_NONE; + + avifImageDestroy(image); + avifDecoderDestroy(decoder); + + return EINA_TRUE; + + destroy_image: + avifImageDestroy(image); + destroy_decoder: + avifDecoderDestroy(decoder); + + return EINA_FALSE; +} + +static Eina_Bool +evas_image_load_file_data_avif_internal(void *pixels, + void *map, size_t length, + int *error) +{ + avifRGBImage rgb; + avifROData raw; + avifDecoder *decoder; + avifImage *image; + avifResult res; + + raw.size = length; + raw.data = (const uint8_t *)map; + + decoder = avifDecoderCreate(); + if (!decoder) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + + image = avifImageCreateEmpty(); + if (!image) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto destroy_decoder; + } + + res = avifDecoderRead(decoder, image, &raw); + if (res != AVIF_RESULT_OK) + { + ERR("avif file format invalid"); + *error = EVAS_LOAD_ERROR_GENERIC; + goto destroy_image; + } + + avifRGBImageSetDefaults(&rgb, image); + rgb.format = AVIF_RGB_FORMAT_BGRA; + rgb.depth = 8; + rgb.pixels = pixels; + rgb.rowBytes = 4 * image->width; + + avifImageYUVToRGB(image, &rgb); + + *error = EVAS_LOAD_ERROR_NONE; + + avifImageDestroy(image); + avifDecoderDestroy(decoder); + + return EINA_TRUE; + + destroy_image: + avifImageDestroy(image); + destroy_decoder: + avifDecoderDestroy(decoder); + + return EINA_FALSE; +} + +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 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_avif(void *loader_data) +{ + free(loader_data); +} + +static Eina_Bool +evas_image_load_file_head_avif(void *loader_data, + Emile_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(&prop->w, &prop->h, + &prop->alpha, + 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, + Emile_Image_Property *prop EINA_UNUSED, + void *pixels, + int *error) +{ + Evas_Loader_Internal *loader = loader_data; + Eina_File *f; + void *map; + Eina_Bool val = EINA_FALSE; + + 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(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_avif_func = +{ + EVAS_IMAGE_LOAD_VERSION, + evas_image_load_file_open_avif, + evas_image_load_file_close_avif, + (void*) evas_image_load_file_head_avif, + NULL, + (void*) evas_image_load_file_data_avif, + NULL, + 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 +