From: Taehyub Kim Date: Tue, 10 Nov 2020 06:18:40 +0000 (+0900) Subject: evas_image_load_png.c: Support Apng Image Format X-Git-Tag: accepted/tizen/unified/20201124.123027~2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F14%2F247414%2F13;p=platform%2Fupstream%2Fefl.git evas_image_load_png.c: Support Apng Image Format Summary: This patch implements the features for playing animated png image on efl application. Test Plan: 1. build 2. and run src/example/image_apng.c Reviewers: Hermet Subscribers: vtorri, cedric, #reviewers, #committers Tags: #efl Differential Revision: https://phab.enlightenment.org/D12175 Change-Id: I522ec8489a9e3fd84297edc045cd13173de38526 --- diff --git a/data/elementary/images/apng_image.png b/data/elementary/images/apng_image.png new file mode 100644 index 0000000..1b35084 Binary files /dev/null and b/data/elementary/images/apng_image.png differ diff --git a/src/examples/elementary/image_apng_example.c b/src/examples/elementary/image_apng_example.c new file mode 100644 index 0000000..0690904 --- /dev/null +++ b/src/examples/elementary/image_apng_example.c @@ -0,0 +1,41 @@ +//Compile with: +//gcc -g image_apng_example.c -o image_apng_example `pkg-config --cflags --libs elementary` + +#include + +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/apng_image.png"); + elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED); + + win = elm_win_util_standard_add("APNG Image", "APNG Image"); + elm_win_autodel_set(win, EINA_TRUE); + + snprintf(buf, sizeof(buf), "%s/images/apng_image.png", 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, 300, 300); + evas_object_show(win); + + elm_run(); + + return 0; +} +ELM_MAIN() diff --git a/src/modules/evas/image_loaders/png/evas_image_load_png.c b/src/modules/evas/image_loaders/png/evas_image_load_png.c index b6f3441..962af15 100644 --- a/src/modules/evas/image_loaders/png/evas_image_load_png.c +++ b/src/modules/evas/image_loaders/png/evas_image_load_png.c @@ -28,12 +28,27 @@ struct _Evas_PNG_Info volatile Eina_Bool hasa; }; -typedef struct _Evas_Loader_Internal Evas_Loader_Internal; -struct _Evas_Loader_Internal +typedef struct _Loader_Info { Eina_File *f; Evas_Image_Load_Opts *opts; -}; + //TIZEN_ONLY(20201111): Support APNG Image Format + Evas_Image_Animated *animated; + unsigned char *buffer; + unsigned char *buffer_pre; + // +} Loader_Info; + +//TIZEN_ONLY(20201111): Support APNG Image Format +typedef struct _Frame_Info +{ + int index; + int x, y, w, h; + double delay; + unsigned char dop; + unsigned char bop; +} Frame_Info; +// static const Evas_Colorspace cspace_grey[2] = { EVAS_COLORSPACE_GRY8, @@ -89,31 +104,98 @@ _evas_image_png_read(png_structp png_ptr, png_bytep out, png_size_t count) static void * evas_image_load_file_open_png(Eina_File *f, Eina_Stringshare *key EINA_UNUSED, Evas_Image_Load_Opts *opts, - Evas_Image_Animated *animated EINA_UNUSED, + Evas_Image_Animated *animated, int *error) { - Evas_Loader_Internal *loader; - - loader = calloc(1, sizeof (Evas_Loader_Internal)); + Loader_Info *loader = calloc(1, sizeof (Loader_Info)); if (!loader) { *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; return NULL; } - - loader->f = f; + loader->f = eina_file_dup(f); loader->opts = opts; + + //TIZEN_ONLY(20201111): Support APNG Image Format + loader->animated = animated; + // + return loader; } static void evas_image_load_file_close_png(void *loader_data) { - free(loader_data); + //TIZEN_ONLY(20201111): Support APNG Image Format + Loader_Info *loader = loader_data; + if (loader->f) eina_file_close(loader->f); + if (loader->buffer) free(loader->buffer); + if (loader->buffer_pre) free(loader->buffer_pre); + // + free(loader); } + +//TIZEN_ONLY(20201111): Support APNG Image Format +static void +_new_frame(Evas_Image_Animated *animated, unsigned char *data, int x, int y, int width, int height, int index, double delay, unsigned char dop, unsigned char bop) +{ + // Allocate Frame Data + Image_Entry_Frame *frame; + + frame = calloc(1, sizeof(Image_Entry_Frame)); + if (!frame) return; + + frame->info = calloc(1, sizeof(Frame_Info)); + if (!frame->info) + { + free(frame); + return; + } + + frame->data = calloc(width * height, sizeof(DATA32)); + if (!frame->data) + { + free(frame->info); + free(frame); + return; + } + + Frame_Info *finfo = frame->info; + + finfo->x = x; + finfo->y = y; + finfo->w = width; + finfo->h = height; + finfo->index = index; + finfo->delay = delay; + finfo->dop = dop; + finfo->bop = bop; + + memcpy(frame->data, data, width * height * sizeof(DATA32)); + + animated->frames = eina_list_append(animated->frames, frame); +} + +static Image_Entry_Frame * +_find_frame(Evas_Image_Animated *animated, int index) +{ + // Find Frame + Eina_List *l; + Image_Entry_Frame *frame; + + if (!animated->frames) return NULL; + EINA_LIST_FOREACH(animated->frames, l, frame) + { + if (((Frame_Info*)frame->info)->index == index) + return frame; + } + return NULL; +} +// + static Eina_Bool -_evas_image_load_file_internal_head_png(Evas_Loader_Internal *loader, +_evas_image_load_file_internal_head_png(Loader_Info *loader, Evas_Image_Property *prop, Evas_PNG_Info *epi, int *error, Eina_Bool is_for_head) @@ -223,6 +305,57 @@ _evas_image_load_file_internal_head_png(Evas_Loader_Internal *loader, prop->info.h = (int) epi->h32; } + //TIZEN_ONLY(20201111): Support APNG Image Format + if (png_get_valid(epi->png_ptr, epi->info_ptr, PNG_INFO_acTL)) + { + png_uint_32 frames = 1; + png_uint_32 plays = 0; + png_uint_32 w0 = epi->w32; + png_uint_32 h0 = epi->h32; + png_uint_32 x0 = 0; + png_uint_32 y0 = 0; + png_get_acTL(epi->png_ptr, epi->info_ptr, &frames, &plays); + int channel = png_get_channels(epi->png_ptr, epi->info_ptr); + + png_set_bgr(epi->png_ptr); + + // Define default delay + unsigned short delay_num = 1; + unsigned short delay_den = 10; + unsigned char dop = 0; + unsigned char bop = 0; + + if (!loader->buffer) + { + loader->buffer = calloc(prop->info.w * prop->info.h, sizeof(DATA32)); + if (!loader->buffer) goto close_file; + } + + Evas_Image_Animated *animated = loader->animated; + + // Read Apng Image Frames + int i, j; + for (i = 0; i < (int)frames; i++) + { + png_read_frame_head(epi->png_ptr, epi->info_ptr); + png_get_next_frame_fcTL(epi->png_ptr, epi->info_ptr, &w0, &h0, &x0, &y0, &delay_num, &delay_den, &dop, &bop); + unsigned char *cur_frame = calloc(w0 * h0 * channel, sizeof(unsigned char)); + if (!cur_frame) goto close_file; + double delay = (double)delay_num / delay_den; + for (j = 0; j < (int)h0; j++) + png_read_row(epi->png_ptr, cur_frame + (j * w0 * channel), NULL); + _new_frame(animated, cur_frame, x0, y0, w0, h0, i + 1, delay, dop, bop); + free(cur_frame); + } + png_read_end(epi->png_ptr, epi->info_ptr); + + animated->animated = 1; + animated->loop_count = plays; + animated->loop_hint = EVAS_IMAGE_ANIMATED_HINT_LOOP; + animated->frame_count = frames; + } + // + if (png_get_valid(epi->png_ptr, epi->info_ptr, PNG_INFO_tRNS)) { /* expand transparency entry -> alpha channel if present */ @@ -303,7 +436,7 @@ evas_image_load_file_head_png(void *loader_data, Evas_Image_Property *prop, int *error) { - Evas_Loader_Internal *loader = loader_data; + Loader_Info *loader = loader_data; Evas_PNG_Info epi; memset(&epi, 0, sizeof (Evas_PNG_Info)); @@ -327,7 +460,7 @@ evas_image_load_file_head_with_data_png(void *loader_data, void *pixels, int *error) { - Evas_Loader_Internal *loader = loader_data; + Loader_Info *loader = loader_data; Evas_Image_Load_Opts *opts; Eina_File *f; @@ -628,13 +761,50 @@ evas_image_load_file_head_with_data_png(void *loader_data, return r; } +//TIZEN_ONLY(20201111): Support APNG Image Format +static unsigned char* +_combine_frame(Loader_Info *loader, Evas_Image_Property *prop, Image_Entry_Frame *frame) +{ + Frame_Info *finfo = (Frame_Info*)frame->info; + uint32_t *buf = (uint32_t*)loader->buffer; + uint32_t *src = (uint32_t*)frame->data; + + if (finfo->dop == PNG_DISPOSE_OP_PREVIOUS) + { + if (!loader->buffer_pre) + { + loader->buffer_pre = calloc(prop->info.w * prop->info.h, sizeof(DATA32)); + if (!loader->buffer_pre) return NULL; + } + memcpy(loader->buffer_pre, buf, prop->info.w * prop->info.h * sizeof(DATA32)); + } + + int x, y, w, h; + x = finfo->x; + y = finfo->y; + w = finfo->w; + h = finfo->h; + + int i, j; + // Copy ROI image + for (i = 0; i < h; i++) + for (j = 0; j < w; j++) + buf[((y + i) * prop->info.w) + (x + j)] = src[(i * w) + j]; + + return (unsigned char*)buf; +} +// + static Eina_Bool evas_image_load_file_data_png(void *loader_data, Evas_Image_Property *prop, void *pixels, int *error) { - Evas_Loader_Internal *loader = loader_data; + Loader_Info *loader = loader_data; + //TIZEN_ONLY(20201111): Support APNG Image Format + Evas_Image_Animated *animated = loader->animated; + // Evas_Image_Load_Opts *opts; Eina_File *f; @@ -654,6 +824,35 @@ evas_image_load_file_data_png(void *loader_data, memset(&epi, 0, sizeof (Evas_PNG_Info)); + //TIZEN_ONLY(20201111): Support APNG Image Format + if (animated->animated) + { + surface = pixels; + int index = animated->cur_frame; + if (index == 0) index = 1; + // Find Current Apng Image Frame + Image_Entry_Frame *frame = _find_frame(animated, index); + if (!frame) goto close_file; + + // Combine Apng Image + unsigned char *data = _combine_frame(loader, prop, frame); + if (!data) goto close_file; + memcpy(surface, data, prop->info.w * prop->info.h * sizeof(DATA32)); + + // Apply Dispose Option + Frame_Info *finfo = (Frame_Info*)frame->info; + if (finfo->dop == PNG_DISPOSE_OP_BACKGROUND) + memset(loader->buffer, 0, prop->info.w * prop->info.h * sizeof(DATA32)); + else if (finfo->dop == PNG_DISPOSE_OP_PREVIOUS) + memcpy(loader->buffer, loader->buffer_pre, prop->info.w * prop->info.h * sizeof(DATA32)); + + prop->info.premul = EINA_TRUE; + r = EINA_TRUE; + + goto close_file; + } + // + if (!_evas_image_load_file_internal_head_png(loader, prop, &epi, error, EINA_FALSE)) return EINA_FALSE; @@ -915,6 +1114,28 @@ evas_image_load_file_data_png(void *loader_data, return r; } +//TIZEN_ONLY(20201111): Support APNG Image Format +static double +evas_image_load_frame_duration_png(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 ((start_frame + frame_num) > animated->frame_count) return -1.0; + if (frame_num < 0) return -1.0; + if (start_frame < 1) return -1.0; + + Image_Entry_Frame *frame = _find_frame(animated, start_frame); + if (frame == NULL) return -1.0; + Frame_Info *finfo = frame->info; + + return finfo->delay; +} +// + static Evas_Image_Load_Func evas_image_load_png_func = { EVAS_IMAGE_LOAD_VERSION, @@ -923,7 +1144,9 @@ static Evas_Image_Load_Func evas_image_load_png_func = evas_image_load_file_head_png, evas_image_load_file_head_with_data_png, evas_image_load_file_data_png, - NULL, + //TIZEN_ONLY(20201111): Support APNG Image Format + evas_image_load_frame_duration_png, + // EINA_TRUE, EINA_FALSE };