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
This commit is contained in:
Taehyub Kim 2020-05-29 11:40:37 +09:00 committed by Hermet Park
parent f88494aa2c
commit df06418b6f
7 changed files with 310 additions and 37 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -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()

View File

@ -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()

View File

@ -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',

View File

@ -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', []]
]

View File

@ -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)
{
return f;
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
evas_image_load_file_close_webp(void *loader_data EINA_UNUSED)
_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)
{
// 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
_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,39 +252,55 @@ evas_image_load_file_data_webp(void *loader_data,
void *pixels,
int *error)
{
Eina_File *f = loader_data;
void *data = NULL;
void *decoded = NULL;
void *surface = NULL;
int width, height;
Loader_Info *loader = loader_data;
Evas_Image_Animated *animated = loader->animated;
data = eina_file_map_all(f, EINA_FILE_SEQUENTIAL);
surface = pixels;
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;
if ((int) prop->w != width ||
(int) prop->h != height)
goto free_data;
void *surface = NULL;
int width, height;
int index = 0;
// XXX: this copy of the surface is inefficient
memcpy(surface, decoded, width * height * 4);
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;
free_data:
if (data) eina_file_map_free(f, data);
free(decoded);
return EINA_TRUE;
}
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;
if (!animated->animated) return -1.0;
if (frame_num < 0) return -1.0;
if (start_frame < 1) return -1.0;
// Calculate Duration of Current Frame
Image_Frame *frame = _find_frame(loader, start_frame);
if (frame == NULL) return -1.0;
return frame->delay;
}
static Evas_Image_Load_Func evas_image_load_webp_func =
{
EVAS_IMAGE_LOAD_VERSION,
@ -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
};