evas_image_load_png.c: Support Apng Image Format 14/247414/13
authorTaehyub Kim <taehyub.kim@samsung.com>
Tue, 10 Nov 2020 06:18:40 +0000 (15:18 +0900)
committerHermet Park <chuneon.park@samsung.com>
Thu, 19 Nov 2020 08:41:09 +0000 (08:41 +0000)
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

data/elementary/images/apng_image.png [new file with mode: 0644]
src/examples/elementary/image_apng_example.c [new file with mode: 0644]
src/modules/evas/image_loaders/png/evas_image_load_png.c

diff --git a/data/elementary/images/apng_image.png b/data/elementary/images/apng_image.png
new file mode 100644 (file)
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 (file)
index 0000000..0690904
--- /dev/null
@@ -0,0 +1,41 @@
+//Compile with:
+//gcc -g image_apng_example.c -o image_apng_example `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/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()
index b6f3441..962af15 100644 (file)
@@ -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
 };