diff options
author | Taehyub Kim <taehyub.kim@samsung.com> | 2020-05-29 11:40:37 +0900 |
---|---|---|
committer | Hermet Park <chuneon.park@samsung.com> | 2020-05-29 11:40:37 +0900 |
commit | df06418b6f39f3b8d73631bda33308b67736bb9d (patch) | |
tree | 956a06a4a860168cf9462204b9cd356d84dc9614 | |
parent | f88494aa2c2f7ad6edb9da5d626b9042db86f6c9 (diff) | |
download | efl-df06418b6f39f3b8d73631bda33308b67736bb9d.tar.gz |
Support WebP Animation Image Files
Summary:
Support WebP Animate Format Imaeg Files.
To support webp animation, apply webp animation decoder.
Test Plan:
1. compile src/exmaple/elementary/image_webp_example_01.c and 02.c
2. run the samples
Reviewers: Hermet, kimcinoo, jsuya, bu5hm4n
Reviewed By: Hermet, kimcinoo, jsuya
Subscribers: cedric, #reviewers, #committers
Tags: #efl
Differential Revision: https://phab.enlightenment.org/D11876
-rwxr-xr-x | data/elementary/images/animated_webp_image.webp | bin | 0 -> 4764 bytes | |||
-rw-r--r-- | data/elementary/images/static_webp_image.webp | bin | 0 -> 10474 bytes | |||
-rw-r--r-- | src/examples/elementary/image_webp_example_01.c | 38 | ||||
-rw-r--r-- | src/examples/elementary/image_webp_example_02.c | 41 | ||||
-rw-r--r-- | src/examples/elementary/meson.build | 2 | ||||
-rw-r--r-- | src/lib/evas/meson.build | 3 | ||||
-rw-r--r-- | src/modules/evas/image_loaders/webp/evas_image_load_webp.c | 257 |
7 files changed, 307 insertions, 34 deletions
diff --git a/data/elementary/images/animated_webp_image.webp b/data/elementary/images/animated_webp_image.webp Binary files differnew file mode 100755 index 0000000000..5b44046e2c --- /dev/null +++ b/data/elementary/images/animated_webp_image.webp diff --git a/data/elementary/images/static_webp_image.webp b/data/elementary/images/static_webp_image.webp Binary files differnew file mode 100644 index 0000000000..0da983e2ce --- /dev/null +++ b/data/elementary/images/static_webp_image.webp diff --git a/src/examples/elementary/image_webp_example_01.c b/src/examples/elementary/image_webp_example_01.c new file mode 100644 index 0000000000..24bc79ae72 --- /dev/null +++ b/src/examples/elementary/image_webp_example_01.c @@ -0,0 +1,38 @@ +//Compile with: +//gcc -g image_webp_example_01.c -o image_webp_example_01 `pkg-config --cflags --libs elementary` + +#include <Elementary.h> + +int +elm_main(int argc EINA_UNUSED, char **argv EINA_UNUSED) +{ + Evas_Object *win, *image; + char buf[PATH_MAX]; + + elm_app_info_set(elm_main, "elementary", "images/static_webp_image.webp"); + elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED); + + win = elm_win_util_standard_add("WebP Image", "WebP Image"); + elm_win_autodel_set(win, EINA_TRUE); + + snprintf(buf, sizeof(buf), "%s/images/static_webp_image.webp", elm_app_data_dir_get()); + + image = elm_image_add(win); + if (!elm_image_file_set(image, buf, NULL)) + { + printf("error: could not load image \"%s\"\n", buf); + return -1; + } + + evas_object_size_hint_weight_set(image, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + elm_win_resize_object_add(win, image); + evas_object_show(image); + + evas_object_resize(win, 320, 320); + evas_object_show(win); + + elm_run(); + + return 0; +} +ELM_MAIN() diff --git a/src/examples/elementary/image_webp_example_02.c b/src/examples/elementary/image_webp_example_02.c new file mode 100644 index 0000000000..3bfaf4a71c --- /dev/null +++ b/src/examples/elementary/image_webp_example_02.c @@ -0,0 +1,41 @@ +//Compile with: +//gcc -g image_webp_example_02.c -o image_webp_example_02 `pkg-config --cflags --libs elementary` + +#include <Elementary.h> + +int +elm_main(int argc EINA_UNUSED, char **argv EINA_UNUSED) +{ + Evas_Object *win, *image; + char buf[PATH_MAX]; + + elm_app_info_set(elm_main, "elementary", "images/animated_webp_image.webp"); + elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED); + + win = elm_win_util_standard_add("WebP Image", "WebP Image"); + elm_win_autodel_set(win, EINA_TRUE); + + snprintf(buf, sizeof(buf), "%s/images/animated_webp_image.webp", elm_app_data_dir_get()); + + image = elm_image_add(win); + if (!elm_image_file_set(image, buf, NULL)) + { + printf("error: could not load image \"%s\"\n", buf); + return -1; + } + + elm_image_animated_set(image, EINA_TRUE); + elm_image_animated_play_set(image, EINA_TRUE); + + evas_object_size_hint_weight_set(image, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + elm_win_resize_object_add(win, image); + evas_object_show(image); + + evas_object_resize(win, 320, 320); + evas_object_show(win); + + elm_run(); + + return 0; +} +ELM_MAIN() diff --git a/src/examples/elementary/meson.build b/src/examples/elementary/meson.build index 7876285349..7abffa8a2f 100644 --- a/src/examples/elementary/meson.build +++ b/src/examples/elementary/meson.build @@ -46,6 +46,8 @@ examples = [ 'hoversel_example_01', 'icon_example_01', 'image_example_01', + 'image_webp_example_01', + 'image_webp_example_02', 'index_example_01', 'index_example_02', 'inwin_example', diff --git a/src/lib/evas/meson.build b/src/lib/evas/meson.build index 3b49e2bea6..2196952d21 100644 --- a/src/lib/evas/meson.build +++ b/src/lib/evas/meson.build @@ -8,6 +8,7 @@ png = dependency('libpng') tiff = dependency('libtiff-4', required: get_option('evas-loaders-disabler').contains('tiff') == false) giflib = cc.find_library('gif') webp = dependency('libwebp', required: get_option('evas-loaders-disabler').contains('webp') == false) +webpdemux = dependency('libwebpdemux', required: get_option('evas-loaders-disabler').contains('webp') == false) libopenjp2 = dependency('libopenjp2', required: get_option('evas-loaders-disabler').contains('jp2k') == false) evas_image_loaders_file = [ @@ -25,7 +26,7 @@ evas_image_loaders_file = [ ['tgv', 'shared', [rg_etc, lz4]], ['tiff', 'shared', [tiff]], ['wbmp', 'shared', []], - ['webp', 'shared', [webp]], + ['webp', 'shared', [webp, webpdemux]], ['xpm', 'shared', []] ] diff --git a/src/modules/evas/image_loaders/webp/evas_image_load_webp.c b/src/modules/evas/image_loaders/webp/evas_image_load_webp.c index bd082455a2..8026e0c880 100644 --- a/src/modules/evas/image_loaders/webp/evas_image_load_webp.c +++ b/src/modules/evas/image_loaders/webp/evas_image_load_webp.c @@ -5,10 +5,30 @@ #include <stdio.h> #include <string.h> #include <webp/decode.h> +#include <webp/demux.h> #include "evas_common_private.h" #include "evas_private.h" +typedef struct _Loader_Info +{ + Eina_File *f; + Evas_Image_Load_Opts *opts; + Evas_Image_Animated *animated; + WebPAnimDecoder *dec; + void *map; + Eina_Array *frames; +}Loader_Info; + +// WebP Frame Information +typedef struct _Image_Frame +{ + int index; + int timestamp; + double delay; + uint8_t *data; +}Image_Frame; + static Eina_Bool evas_image_load_file_check(Eina_File *f, void *map, unsigned int *w, unsigned int *h, Eina_Bool *alpha, @@ -38,16 +58,95 @@ evas_image_load_file_check(Eina_File *f, void *map, static void * evas_image_load_file_open_webp(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) + Evas_Image_Load_Opts *opts, + Evas_Image_Animated *animated, + int *error) +{ + Loader_Info *loader = calloc(1, sizeof (Loader_Info)); + if (!loader) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return NULL; + } + loader->f = eina_file_dup(f); + loader->opts = opts; + loader->animated = animated; + return loader; +} + +static void +_free_all_frame(Loader_Info *loader) +{ + Image_Frame *frame; + + if (!loader->frames) return; + + for (unsigned int i = 0; i < eina_array_count(loader->frames); ++i) + { + frame = eina_array_data_get(loader->frames, i); + if (frame->data) + { + free(frame->data); + frame->data = NULL; + } + free(frame); + } +} + + +static void +evas_image_load_file_close_webp(void *loader_data) { - return f; + // Free Allocated Data + Loader_Info *loader = loader_data; + _free_all_frame(loader); + eina_array_free(loader->frames); + if (loader->dec) WebPAnimDecoderDelete(loader->dec); + if ((loader->map) && (loader->f)) + eina_file_map_free(loader->f, loader->map); + if (loader->f) eina_file_close(loader->f); + free(loader); } + static void -evas_image_load_file_close_webp(void *loader_data EINA_UNUSED) +_new_frame(Loader_Info *loader, uint8_t *data, int width, int height, int index, + int pre_timestamp, int cur_timestamp) +{ + // Allocate Frame Data + Image_Frame *frame; + + frame = calloc(1, sizeof(Image_Frame)); + if (!frame) return; + + frame->data = calloc(width * height * 4, sizeof(uint8_t)); + if (!frame->data) + { + free(frame); + return; + } + + frame->index = index; + frame->timestamp = cur_timestamp; + frame->delay = ((double)(cur_timestamp - pre_timestamp)/1000.0); + memcpy(frame->data, data, width * height * 4); + + eina_array_push(loader->frames, frame); +} + +static Image_Frame * +_find_frame(Loader_Info *loader, int index) { + // Find Frame + Image_Frame *frame; + + if (!loader->frames) return NULL; + + frame = eina_array_data_get(loader->frames, index - 1); + if (frame->index == index) + return frame; + + return NULL; } static Eina_Bool @@ -55,20 +154,96 @@ evas_image_load_file_head_webp(void *loader_data, Emile_Image_Property *prop, int *error) { - Eina_File *f = loader_data; - Eina_Bool r; + Loader_Info *loader = loader_data; + Evas_Image_Animated *animated = loader->animated; + Eina_File *f = loader->f; void *data; *error = EVAS_LOAD_ERROR_NONE; data = eina_file_map_all(f, EINA_FILE_RANDOM); + loader->map = data; - r = evas_image_load_file_check(f, data, + if (!evas_image_load_file_check(f, data, &prop->w, &prop->h, &prop->alpha, - error); + error)) + { + ERR("Image File is Invalid"); + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } - if (data) eina_file_map_free(f, data); - return r; + // Init WebP Data + WebPData webp_data; + WebPDataInit(&webp_data); + + // Assign Data + webp_data.bytes = data; + webp_data.size = eina_file_size_get(f); + + // Set Decode Option + WebPAnimDecoderOptions dec_options; + WebPAnimDecoderOptionsInit(&dec_options); + dec_options.color_mode = MODE_BGRA; + + // Create WebPAnimation Decoder + WebPAnimDecoder *dec = WebPAnimDecoderNew(&webp_data, &dec_options); + if (!dec) + { + ERR("WebP Decoder Creation is Failed"); + *error = EVAS_LOAD_ERROR_GENERIC; + return EINA_FALSE; + } + loader->dec = dec; + + // Get WebP Animation Info + WebPAnimInfo anim_info; + if (!WebPAnimDecoderGetInfo(dec, &anim_info)) + { + ERR("Getting WebP Information is Failed"); + *error = EVAS_LOAD_ERROR_GENERIC; + return EINA_FALSE; + } + + uint8_t* buf; + int pre_timestamp = 0; + int cur_timestamp = 0; + int index = 1; + + // Set Frame Array + loader->frames = eina_array_new(anim_info.frame_count); + if (!loader->frames) + { + ERR("Frame Array Allocation is Faild"); + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + + // Decode Frames + while (WebPAnimDecoderHasMoreFrames(dec)) + { + if (!WebPAnimDecoderGetNext(dec, &buf, &cur_timestamp)) + { + ERR("WebP Decoded Frame Get is Failed"); + *error = EVAS_LOAD_ERROR_GENERIC; + return EINA_FALSE; + } + _new_frame(loader, buf, anim_info.canvas_width, anim_info.canvas_height, index, + pre_timestamp, cur_timestamp); + pre_timestamp = cur_timestamp; + index++; + } + + // Set Animation Info + if (anim_info.frame_count > 1) + { + animated->animated = 1; + animated->loop_count = anim_info.loop_count; + animated->loop_hint = EVAS_IMAGE_ANIMATED_HINT_LOOP; + animated->frame_count = anim_info.frame_count; + } + + return EINA_TRUE; } static Eina_Bool @@ -77,37 +252,53 @@ evas_image_load_file_data_webp(void *loader_data, void *pixels, int *error) { - Eina_File *f = loader_data; - void *data = NULL; - void *decoded = NULL; + Loader_Info *loader = loader_data; + Evas_Image_Animated *animated = loader->animated; + + *error = EVAS_LOAD_ERROR_NONE; + void *surface = NULL; int width, height; + int index = 0; - data = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + index = animated->cur_frame; + // Find Cur Frame + if (index == 0) + index = 1; + Image_Frame *frame = _find_frame(loader, index); + if (frame == NULL) return EINA_FALSE; + + WebPAnimInfo anim_info; + WebPAnimDecoderGetInfo(loader->dec, &anim_info); + width = anim_info.canvas_width; + height = anim_info.canvas_height; + + // Render Frame surface = pixels; + memcpy(surface, frame->data, width * height * 4); + prop->premul = EINA_TRUE; - decoded = WebPDecodeBGRA(data, eina_file_size_get(f), &width, &height); - if (!decoded) - { - *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; - goto free_data; - } - *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; +} - if ((int) prop->w != width || - (int) prop->h != height) - goto free_data; +static double +evas_image_load_frame_duration_webp(void *loader_data, + int start_frame, + int frame_num) +{ + Loader_Info *loader = loader_data; + Evas_Image_Animated *animated = loader->animated; - // XXX: this copy of the surface is inefficient - memcpy(surface, decoded, width * height * 4); - prop->premul = EINA_TRUE; + if (!animated->animated) return -1.0; + if (frame_num < 0) return -1.0; + if (start_frame < 1) return -1.0; - free_data: - if (data) eina_file_map_free(f, data); - free(decoded); + // Calculate Duration of Current Frame + Image_Frame *frame = _find_frame(loader, start_frame); + if (frame == NULL) return -1.0; - return EINA_TRUE; + return frame->delay; } static Evas_Image_Load_Func evas_image_load_webp_func = @@ -118,7 +309,7 @@ static Evas_Image_Load_Func evas_image_load_webp_func = (void*) evas_image_load_file_head_webp, NULL, (void*) evas_image_load_file_data_webp, - NULL, + evas_image_load_frame_duration_webp, EINA_TRUE, EINA_FALSE }; |