From 97f95e736269fcdf5511077135ede9b37dd06854 Mon Sep 17 00:00:00 2001 From: thierry1970 Date: Sat, 6 Feb 2021 15:45:42 +0000 Subject: [PATCH] Added the heif loader Summary: that supports images : *.heif, *hiec and *.avif I have disabled *.avif images, there is already a loader. Reviewers: stefan_schmidt, raster Subscribers: raster, vtorri, cedric, #reviewers, #committers Tags: #efl Differential Revision: https://phab.enlightenment.org/D12135 --- README | 9 + data/evas/evas.xml | 13 + data/evas/meson.build | 4 + data/meson.build | 3 +- meson_options.txt | 4 +- src/lib/evas/common/evas_image_load.c | 7 +- src/lib/evas/file/evas_module.c | 4 + src/lib/evas/meson.build | 2 + .../image_loaders/heif/evas_image_load_heif.c | 300 ++++++++++++++++++ src/tests/evas/evas_test_image.c | 5 + 10 files changed, 347 insertions(+), 4 deletions(-) create mode 100644 data/evas/evas.xml create mode 100644 data/evas/meson.build create mode 100644 src/modules/evas/image_loaders/heif/evas_image_load_heif.c diff --git a/README b/README index ec3b27a737..03085056d3 100644 --- a/README +++ b/README @@ -404,6 +404,15 @@ This library acts as a porting library for Windows to provide missing libc calls not in Mingw32 that EFL needs. +**Heif:** + +//LGPL v3 license// + +The license doesnt affect efl or apps using efl, but gpl3 or lgpl3 +affects the entire os requiring any gpl/lgpl3 component be +user-replacable. +The end user must be able to modify the libheif code and still be +able to use the efl. COMPILING AND INSTALLING diff --git a/data/evas/evas.xml b/data/evas/evas.xml new file mode 100644 index 0000000000..7764c0c05e --- /dev/null +++ b/data/evas/evas.xml @@ -0,0 +1,13 @@ + + +>------ +>------>-------HEIF image file +>------>------- +>------>------- +>------ +>------ +>------>-------AVIF image file +>------>-------- +>------ + + diff --git a/data/evas/meson.build b/data/evas/meson.build new file mode 100644 index 0000000000..ddcc57dcad --- /dev/null +++ b/data/evas/meson.build @@ -0,0 +1,4 @@ +install_data(files('evas.xml'), + install_dir : join_paths(dir_data, 'mime', 'packages') +) + diff --git a/data/meson.build b/data/meson.build index 4e1f68d406..5944dad07c 100644 --- a/data/meson.build +++ b/data/meson.build @@ -1,4 +1,4 @@ -checkme_files = ['ecore', 'ecore_imf', 'ecore_x', 'eeze', 'efreet', 'elua', 'emotion', 'ethumb', 'ethumb_client', 'evas'] +checkme_files = ['ecore', 'ecore_imf', 'ecore_x', 'eeze', 'efreet', 'elua', 'emotion', 'ethumb', 'ethumb_client'] foreach checkme : checkme_files install_data(join_paths(checkme, 'checkme'), install_dir : join_paths(dir_data, checkme) @@ -10,3 +10,4 @@ subdir('edje') subdir('embryo') subdir(join_paths('ethumb', 'frames')) subdir('elementary') +subdir('evas') diff --git a/meson_options.txt b/meson_options.txt index f5ffb0e134..b224179366 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', 'avif'], - value : ['json', 'avif'] + 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', 'heif'], + value : ['json', 'avif', 'heif'] ) option('ecore-imf-loaders-disabler', diff --git a/src/lib/evas/common/evas_image_load.c b/src/lib/evas/common/evas_image_load.c index 8ed04dda2e..797fae37ea 100644 --- a/src/lib/evas/common/evas_image_load.c +++ b/src/lib/evas/common/evas_image_load.c @@ -69,6 +69,10 @@ static const struct ext_loader_s loaders[] = MATCHING(".avif", "avif"), MATCHING(".avifs", "avif"), + MATCHING(".heif", "heif"), + MATCHING(".heic", "heif"), + // MATCHING(".avif", "heif"), + /* xcf - gefenric */ MATCHING(".xcf", "generic"), MATCHING(".xcf.gz", "generic"), @@ -167,7 +171,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", "avif", "generic" + "bmp", "tga", "wbmp", "ico", "psd", "jp2k", "dds", "avif", "heif", + "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 6292c1bb42..1362a004d8 100644 --- a/src/lib/evas/file/evas_module.c +++ b/src/lib/evas/file/evas_module.c @@ -202,6 +202,7 @@ EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, dds); EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, eet); EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, generic); EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, gif); +EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, heif); EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, ico); EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, jpeg); EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, jp2k); @@ -307,6 +308,9 @@ static const struct { #ifdef EVAS_STATIC_BUILD_GIF EVAS_EINA_STATIC_MODULE_USE(image_loader, gif), #endif +#ifdef EVAS_STATIC_BUILD_HEIF + EVAS_EINA_STATIC_MODULE_USE(image_loader, heif), +#endif #ifdef EVAS_STATIC_BUILD_ICO EVAS_EINA_STATIC_MODULE_USE(image_loader, ico), #endif diff --git a/src/lib/evas/meson.build b/src/lib/evas/meson.build index 8dcfd98713..1bf5fa0801 100644 --- a/src/lib/evas/meson.build +++ b/src/lib/evas/meson.build @@ -11,6 +11,7 @@ webp = dependency('libwebp', version: ['>=0.5.0'], required: get_option('evas-lo 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, version: '>= 0.8.2') +heif= dependency('libheif', required: get_option('evas-loaders-disabler').contains('heif') == false) evas_image_loaders_file = [ ['avif', 'shared', [libavif]], @@ -18,6 +19,7 @@ evas_image_loaders_file = [ ['eet', 'static', [eet]], ['generic', 'shared', [rt]], ['gif', 'shared', [giflib]], + ['heif', 'shared', [heif]], ['ico', 'shared', []], ['jpeg', 'static', [jpeg]], ['jp2k', 'shared', [libopenjp2]], diff --git a/src/modules/evas/image_loaders/heif/evas_image_load_heif.c b/src/modules/evas/image_loaders/heif/evas_image_load_heif.c new file mode 100644 index 0000000000..bc9f2a1e06 --- /dev/null +++ b/src/modules/evas/image_loaders/heif/evas_image_load_heif.c @@ -0,0 +1,300 @@ +#define _XOPEN_SOURCE 600 + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef HAVE_NETINET_IN_H +# include +#endif + +#include + +#include "evas_common_private.h" +#include "evas_private.h" + +static int _evas_loader_heif_log_dom = -1; + +#ifdef ERR +# undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_evas_loader_heif_log_dom, __VA_ARGS__) + +#ifdef INF +# undef INF +#endif +#define INF(...) EINA_LOG_DOM_INFO(_evas_loader_heif_log_dom, __VA_ARGS__) + + +static void * +evas_image_load_file_open_heif(Eina_File *f, Eina_Stringshare *key EINA_UNUSED, + Evas_Image_Load_Opts *opts EINA_UNUSED, + Evas_Image_Animated *animated EINA_UNUSED, + int *error EINA_UNUSED) +{ + return f; +} + +static void +evas_image_load_file_close_heif(void *loader_data EINA_UNUSED) +{ +} + +static Eina_Bool +evas_image_load_file_head_heif(void *loader_data, + Emile_Image_Property *prop, + int *error) +{ + Eina_File *f = loader_data; + void *map; + size_t length; + struct heif_error err; + struct heif_context* hc = NULL; + struct heif_image_handle* hdl = NULL; + struct heif_image* img = NULL; + Eina_Bool r = EINA_FALSE; + + *error = EVAS_LOAD_ERROR_NONE; + + map = eina_file_map_all(f, EINA_FILE_RANDOM); + length = eina_file_size_get(f); + + // init prop struct with some default null values + prop->w = 0; + prop->h = 0; + + if (!map || length < 1) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto on_error; + } + + hc = heif_context_alloc(); + if (!hc) { + INF("cannot allocate heif_context"); + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto on_error; + } + + err = heif_context_read_from_memory_without_copy(hc, map, length, NULL); + if (err.code != heif_error_Ok) { + INF("%s", err.message); + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto on_error; + } + + err = heif_context_get_primary_image_handle(hc, &hdl); + if (err.code != heif_error_Ok) { + INF("%s", err.message); + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto on_error; + } + + int has_alpha = heif_image_handle_has_alpha_channel(hdl); + + err = heif_decode_image(hdl, &img, heif_colorspace_RGB, + has_alpha ? heif_chroma_interleaved_RGBA : heif_chroma_interleaved_RGB, + NULL); + if (err.code != heif_error_Ok) { + INF("%s", err.message); + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto on_error; + } + + prop->w = heif_image_get_width(img, heif_channel_interleaved); + prop->h = heif_image_get_height(img, heif_channel_interleaved); + if (has_alpha != 3) + prop->alpha = 1; + + r = EINA_TRUE; + + on_error: + if (img) { + heif_image_release(img); + } + + if (hdl) { + heif_image_handle_release(hdl); + } + + if (hc) { + heif_context_free(hc); + } + eina_file_map_free(f, map); + return r; +} + +static Eina_Bool +evas_image_load_file_data_heif(void *loader_data, + Emile_Image_Property *prop, + void *pixels, + int *error) +{ + Eina_File *f = loader_data; + + void *map; + size_t length; + struct heif_error err; + struct heif_context* hc = NULL; + struct heif_image_handle* hdl = NULL; + struct heif_image* img = NULL; + unsigned int x, y; + int stride, bps = 3; + const uint8_t* data; + uint8_t* dd = (uint8_t*)pixels, *ds = NULL; + Eina_Bool result = EINA_FALSE; + + map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + length = eina_file_size_get(f); + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + if (!map || length < 1) + goto on_error; + + *error = EVAS_LOAD_ERROR_GENERIC; + result = EINA_FALSE; + + hc = heif_context_alloc(); + if (!hc) { + INF("cannot allocate heif_context"); + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto on_error; + } + + err = heif_context_read_from_memory_without_copy(hc, map, length, NULL); + if (err.code != heif_error_Ok) { + INF("%s", err.message); + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto on_error; + } + + err = heif_context_get_primary_image_handle(hc, &hdl); + if (err.code != heif_error_Ok) { + INF("%s", err.message); + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto on_error; + } + + err = heif_decode_image(hdl, &img, heif_colorspace_RGB, + prop->alpha ? heif_chroma_interleaved_RGBA : heif_chroma_interleaved_RGB, + NULL); + if (err.code != heif_error_Ok) { + INF("%s", err.message); + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto on_error; + } + if (prop->alpha) bps = 4; + data = heif_image_get_plane_readonly(img, heif_channel_interleaved, &stride); + ds = (uint8_t*)data; + for (y = 0; y < prop->h; y++) + for (x = 0; x < prop->w; x++) + { + if (bps == 3) + { + dd[3] = 0xff; + dd[0] = ds[2]; + dd[1] = ds[1]; + dd[2] = ds[0]; + ds+=3; + dd+=4; + } + else + { + dd[0] = ds[2]; + dd[1] = ds[1]; + dd[2] = ds[0]; + dd[3] = ds[3]; + ds+=4; + dd+=4; + } + } + result = EINA_TRUE; + + *error = EVAS_LOAD_ERROR_NONE; + prop->premul = EINA_TRUE; + +on_error: + + if (map) eina_file_map_free(f, map); + + if (img) { + // Do not free the image here when we pass it to gdk-pixbuf, as its memory will still be used by gdk-pixbuf. + heif_image_release(img); + } + + if (hdl) { + heif_image_handle_release(hdl); + } + + if (hc) { + heif_context_free(hc); + } + + return result; +} + +static const Evas_Image_Load_Func evas_image_load_heif_func = { + EVAS_IMAGE_LOAD_VERSION, + evas_image_load_file_open_heif, + evas_image_load_file_close_heif, + (void*) evas_image_load_file_head_heif, + NULL, + (void*) evas_image_load_file_data_heif, + NULL, + EINA_TRUE, + EINA_FALSE +}; + +static int +module_open(Evas_Module *em) +{ + if (!em) return 0; + _evas_loader_heif_log_dom = eina_log_domain_register + ("evas-heif", EVAS_DEFAULT_LOG_COLOR); + if (_evas_loader_heif_log_dom < 0) + { + EINA_LOG_ERR("Can not create a module log domain."); + return 0; + } + + em->functions = (void *)(&evas_image_load_heif_func); + return 1; +} + +static void +module_close(Evas_Module *em EINA_UNUSED) +{ + if (_evas_loader_heif_log_dom >= 0) + { + eina_log_domain_unregister(_evas_loader_heif_log_dom); + _evas_loader_heif_log_dom = -1; + } +} + +static Evas_Module_Api evas_modapi = + { + EVAS_MODULE_API_VERSION, + "heif", + "none", + { + module_open, + module_close + } + }; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, heif); + + +#ifndef EVAS_STATIC_BUILD_HEIF +EVAS_EINA_MODULE_DEFINE(image_loader, heif); +#endif diff --git a/src/tests/evas/evas_test_image.c b/src/tests/evas/evas_test_image.c index c2530632a3..5753651f30 100644 --- a/src/tests/evas/evas_test_image.c +++ b/src/tests/evas/evas_test_image.c @@ -53,6 +53,11 @@ static const char *exts[] = { ,"jpeg" ,"jpg" #endif +#ifdef BUILD_LOADER_HEIF + ,"heif" + ,"heic" +// ,"avif" +#endif }; EFL_START_TEST(evas_object_image_loader)