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:
parent
f88494aa2c
commit
df06418b6f
Binary file not shown.
After Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
|
@ -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()
|
|
@ -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()
|
|
@ -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',
|
||||
|
|
|
@ -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', []]
|
||||
]
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue