cedric pushed a commit to branch master.

http://git.enlightenment.org/core/efl.git/commit/?id=961ecab040669ad2b181ece230e467ae708be8f8

commit 961ecab040669ad2b181ece230e467ae708be8f8
Author: Cedric BAIL <cedric.b...@samsung.com>
Date:   Tue Mar 11 19:08:40 2014 +0900

    evas: add a tgv loader.
    
    The TGV file format is specifically created for Evas. It is designed to 
allow
    region decompression and parallele decompression with a fast path for GPU 
that
    do handle ETC1 compression. Plan for adding other compression method will 
come
    later.
---
 configure.ac                                       |   2 +
 m4/evas_check_loader.m4                            |  16 +
 src/Makefile_Evas.am                               |  52 +++
 src/lib/evas/common/evas_image_load.c              |   3 +
 src/lib/evas/file/evas_module.c                    |   4 +
 src/modules/evas/loaders/tgv/evas_image_load_tgv.c | 359 +++++++++++++++++++++
 6 files changed, 436 insertions(+)

diff --git a/configure.ac b/configure.ac
index 3642b96..a848345 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1419,6 +1419,7 @@ ARG_ENABLE_EVAS_IMAGE_LOADER(Tiff, yes)
 ARG_ENABLE_EVAS_IMAGE_LOADER(WBMP, static)
 ARG_ENABLE_EVAS_IMAGE_LOADER(WEBP, no)
 ARG_ENABLE_EVAS_IMAGE_LOADER(XPM, static)
+ARG_ENABLE_EVAS_IMAGE_LOADER(TGV, static)
 
 ### Default values
 
@@ -1700,6 +1701,7 @@ EVAS_CHECK_IMAGE_LOADER([Tiff],    
[${want_evas_image_loader_tiff}])
 EVAS_CHECK_IMAGE_LOADER([WBMP],    [${want_evas_image_loader_wbmp}])
 EVAS_CHECK_IMAGE_LOADER([WEBP],    [${want_evas_image_loader_webp}])
 EVAS_CHECK_IMAGE_LOADER([XPM],     [${want_evas_image_loader_xpm}])
+EVAS_CHECK_IMAGE_LOADER([TGV],     [${want_evas_image_loader_tgv}])
 
 dnl Windows has no sigsetjmp function, nor equivalent.
 dnl So we disable the jpeg saver.
diff --git a/m4/evas_check_loader.m4 b/m4/evas_check_loader.m4
index b34c28f..07d9cdd 100644
--- a/m4/evas_check_loader.m4
+++ b/m4/evas_check_loader.m4
@@ -341,6 +341,22 @@ AS_IF([test "x${have_dep}" = "xyes"], [$3], [$4])
 
 ])
 
+dnl use: EVAS_CHECK_LOADER_DEP_TGV(loader, want_static[, ACTION-IF-FOUND[, 
ACTION-IF-NOT-FOUND]])
+
+AC_DEFUN([EVAS_CHECK_LOADER_DEP_TGV],
+[
+
+have_dep="yes"
+evas_image_loader_[]$1[]_cflags=""
+evas_image_loader_[]$1[]_libs=""
+
+AC_SUBST([evas_image_loader_$1_cflags])
+AC_SUBST([evas_image_loader_$1_libs])
+
+AS_IF([test "x${have_dep}" = "xyes"], [$3], [$4])
+
+])
+
 dnl use: EVAS_CHECK_LOADER_DEP_SVG(loader, want_static[, ACTION-IF-FOUND[, 
ACTION-IF-NOT-FOUND]])
 
 AC_DEFUN([EVAS_CHECK_LOADER_DEP_SVG],
diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am
index 6132592..5f07710 100644
--- a/src/Makefile_Evas.am
+++ b/src/Makefile_Evas.am
@@ -1712,6 +1712,58 @@ modules_evas_loaders_xpm_module_la_LIBTOOLFLAGS = 
--tag=disable-static
 endif
 endif
 
+if BUILD_LOADER_TGV
+if EVAS_STATIC_BUILD_TGV
+lib_evas_libevas_la_SOURCES += \
+modules/evas/loaders/tgv/evas_image_load_tgv.c \
+static_libs/rg_etc/rg_etc1.c \
+static_libs/rg_etc/rg_etc1.h \
+static_libs/lz4/lz4.c \
+static_libs/lz4/lz4.h
+lib_evas_libevas_la_CPPFLAGS += \
+-I$(top_srcdir)/src/static_libs/lz4 \
+-I$(top_srcdir)/src/static_libs/rg_etc \
+@evas_image_loader_tgv_cflags@
+lib_evas_libevas_la_LIBADD += @evas_image_loader_tgv_libs@
+if EVAS_CSERVE2
+bin_evas_evas_cserve2_slave_SOURCES += \
+modules/evas/loaders/tgv/evas_image_load_tgv.c \
+static_libs/rg_etc/rg_etc1.c \
+static_libs/rg_etc/rg_etc1.h \
+static_libs/lz4/lz4.c \
+static_libs/lz4/lz4.h
+bin_evas_evas_cserve2_slave_CPPFLAGS +=  \
+-I$(top_builddir)/src/lib/efl \
+-I$(top_srcdir)/src/static_libs/lz4 \
+-I$(top_srcdir)/src/static_libs/rg_etc \
+-I$(top_srcdir)/src/lib/evas/ \
+@evas_image_loader_tgv_cflags@
+bin_evas_evas_cserve2_slave_LDADD += @evas_image_loader_tgv_libs@
+endif
+else
+loadertgvpkgdir = $(libdir)/evas/modules/loaders/tgv/$(MODULE_ARCH)
+loadertgvpkg_LTLIBRARIES = modules/evas/loaders/tgv/module.la
+modules_evas_loaders_tgv_module_la_SOURCES = \
+modules/evas/loaders/tgv/evas_image_load_tgv.c \
+static_libs/rg_etc/rg_etc1.c \
+static_libs/rg_etc/rg_etc1.h \
+static_libs/lz4/lz4.c \
+static_libs/lz4/lz4.h
+modules_evas_loaders_tgv_module_la_CPPFLAGS = \
+-I$(top_builddir)/src/lib/efl \
+-I$(top_srcdir)/src/static_libs/lz4 \
+-I$(top_srcdir)/src/static_libs/rg_etc \
+-I$(top_srcdir)/src/lib/evas/ \
+@EVAS_CFLAGS@ \
+@evas_image_loader_tgv_cflags@
+modules_evas_loaders_tgv_module_la_LIBADD = \
+@USE_EVAS_LIBS@ \
+@evas_image_loader_tgv_libs@
+modules_evas_loaders_tgv_module_la_DEPENDENCIES = @USE_EVAS_INTERNAL_LIBS@
+modules_evas_loaders_tgv_module_la_LDFLAGS = -module @EFL_LTMODULE_FLAGS@
+modules_evas_loaders_tgv_module_la_LIBTOOLFLAGS = --tag=disable-static
+endif
+endif
 
 ### Unit tests
 
diff --git a/src/lib/evas/common/evas_image_load.c 
b/src/lib/evas/common/evas_image_load.c
index 92c6402..52d3295 100644
--- a/src/lib/evas/common/evas_image_load.c
+++ b/src/lib/evas/common/evas_image_load.c
@@ -64,6 +64,9 @@ static const struct ext_loader_s loaders[] =
    MATCHING(".cur", "ico"),
    
    MATCHING(".psd", "psd"),
+
+   MATCHING(".tgv", "tgv"),
+
    /* xcf - gefenric */
    MATCHING(".xcf", "generic"),
    MATCHING(".xcf.gz", "generic"),
diff --git a/src/lib/evas/file/evas_module.c b/src/lib/evas/file/evas_module.c
index 28b4243..6cb5dc6 100644
--- a/src/lib/evas/file/evas_module.c
+++ b/src/lib/evas/file/evas_module.c
@@ -140,6 +140,7 @@ EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, tiff);
 EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, wbmp);
 EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, webp);
 EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, xpm);
+EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, tgv);
 #endif
 
 #if !EVAS_MODULE_NO_IMAGE_SAVERS
@@ -232,6 +233,9 @@ static const struct {
 #ifdef EVAS_STATIC_BUILD_XPM
   EVAS_EINA_STATIC_MODULE_USE(image_loader, xpm),
 #endif
+#ifdef EVAS_STATIC_BUILD_TGV
+  EVAS_EINA_STATIC_MODULE_USE(image_loader, tgv),
+#endif
 #endif
 #if !EVAS_MODULE_NO_IMAGE_SAVERS
 #ifdef EVAS_STATIC_BUILD_EET
diff --git a/src/modules/evas/loaders/tgv/evas_image_load_tgv.c 
b/src/modules/evas/loaders/tgv/evas_image_load_tgv.c
new file mode 100644
index 0000000..308d0b6
--- /dev/null
+++ b/src/modules/evas/loaders/tgv/evas_image_load_tgv.c
@@ -0,0 +1,359 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+
+#ifdef _WIN32
+# include <winsock2.h>
+#endif /* ifdef _WIN32 */
+
+#include "lz4.h"
+#include "rg_etc1.h"
+#include "Evas_Loader.h"
+
+/**************************************************************
+ * The TGV file format is oriented around compression mecanism
+ * that hardware are good at decompressing. We do still provide
+ * a fully software implementation in case your hardware doesn't
+ * handle it.
+ *
+ * This file format is designed to compress/decompress things
+ * in block area. Giving opportunity to store really huge file
+ * and only decompress/compress them as we need.
+ *
+ * The file format is as follow :
+ * - char     magic[4]: "TGV1"
+ * - uint8_t  block_size (real block size = (4 << bits[0-3], 4 << bits[4-7])
+ * - uint8_t  algorithm (0 -> ETC1)
+ * - uint8_t  options[2] (1 -> lz4)
+ * - uint32_t width
+ * - uint32_t height
+ * - blocks[]
+ *   - 0 length encoded compress size (if length == 64 * block_size => no 
compression)
+ *   - lzma encoded etc1 block
+ **************************************************************/
+
+// FIXME: wondering if we should support mipmap
+// FIXME: instead of the complete size, maybe just the usefull left over byte 
+ number of block.
+
+typedef struct _Evas_Loader_Internal Evas_Loader_Internal;
+struct _Evas_Loader_Internal
+{
+   Eina_File *f;
+
+   Eina_Rectangle region;
+
+   struct {
+      unsigned int width;
+      unsigned int height;
+   } block;
+   struct {
+      unsigned int width;
+      unsigned int height;
+   } size;
+
+   Eina_Bool compress;
+};
+
+
+static void *
+evas_image_load_file_open_tgv(Eina_File *f, Eina_Stringshare *key EINA_UNUSED,
+                              Evas_Image_Load_Opts *opts,
+                              Evas_Image_Animated *animated EINA_UNUSED,
+                              int *error)
+{
+   Evas_Loader_Internal *loader;
+
+   loader = calloc(1, sizeof (Evas_Loader_Internal));
+   if (!loader)
+     {
+        *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
+        return NULL;
+     }
+
+   if (eina_file_size_get(f) <= 16)
+     {
+        *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
+        return NULL;
+     }
+
+   loader->f = eina_file_dup(f);
+   if (!loader->f)
+     {
+        *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
+        return NULL;
+     }
+
+   if (opts && (opts->region.w > 0) && (opts->region.h > 0))
+     {
+        EINA_RECTANGLE_SET(&loader->region,
+                           opts->region.x,
+                           opts->region.y,
+                           opts->region.w,
+                           opts->region.h);
+     }
+   else
+     {
+        EINA_RECTANGLE_SET(&loader->region,
+                           0, 0,
+                           -1, -1);
+     }
+
+   return loader;
+}
+
+
+static void
+evas_image_load_file_close_tgv(void *loader_data)
+{
+   Evas_Loader_Internal *loader = loader_data;
+
+   eina_file_close(loader->f);
+   free(loader);
+}
+
+#define OFFSET_BLOCK_SIZE 4
+#define OFFSET_ALGORITHN 5
+#define OFFSET_OPTIONS 6
+#define OFFSET_WIDTH 8
+#define OFFSET_HEIGHT 12
+#define OFFSET_BLOCKS 16
+
+static Eina_Bool
+evas_image_load_file_head_tgv(void *loader_data,
+                              Evas_Image_Property *prop,
+                              int *error)
+{
+   Evas_Loader_Internal *loader = loader_data;
+   const char *m;
+
+   m = eina_file_map_all(loader->f, EINA_FILE_SEQUENTIAL);
+   if (!m)
+     {
+        *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
+        return EINA_FALSE;
+     }
+
+   if (strncmp(m, "TGV1", 4) != 0)
+     {
+        *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
+        return EINA_FALSE;
+     }
+
+   loader->block.width = 4 << (m[OFFSET_BLOCK_SIZE] & 0x0f);
+   loader->block.height = 4 << ((m[OFFSET_BLOCK_SIZE] & 0xf0) >> 4);
+
+   if (m[OFFSET_ALGORITHN] != 0)
+     {
+        *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
+        return EINA_FALSE;
+     }
+
+   loader->compress = m[OFFSET_OPTIONS] & 0x1;
+
+   loader->size.width = ntohl(*((unsigned int*) &(m[OFFSET_WIDTH])));
+   loader->size.height = ntohl(*((unsigned int*) &(m[OFFSET_HEIGHT])));
+
+   if (loader->region.w == -1 &&
+       loader->region.h == -1)
+     {
+        loader->region.w = loader->size.width;
+        loader->region.h = loader->size.height;
+     }
+   else
+     {
+        Eina_Rectangle r;
+
+        EINA_RECTANGLE_SET(&r, 0, 0, loader->size.width, loader->size.height);
+        if (!eina_rectangle_intersection(&loader->region, &r))
+          {
+             *error = EVAS_LOAD_ERROR_GENERIC;
+             return EINA_FALSE;
+          }
+     }
+
+   prop->w = loader->size.width;
+   prop->h = loader->size.height;
+
+   return EINA_TRUE;
+}
+
+static inline unsigned int
+_tgv_length_get(const char *m, unsigned int length, unsigned int *offset)
+{
+   unsigned int r = 0;
+   unsigned int shift = 0;
+
+   while (*offset < length && ((*m) & 0x80))
+     {
+        r = r | (((*m) & 0x7F) << shift);
+        shift += 7;
+        m++;
+        (*offset)++;
+     }
+   if (*offset < length)
+     {
+        r = r | (((*m) & 0x7F) << shift);
+        (*offset)++;
+     }
+
+   return r;
+}
+
+Eina_Bool
+evas_image_load_file_data_tgv(void *loader_data,
+                              Evas_Image_Property *prop,
+                              void *pixels,
+                              int *error)
+{
+   Evas_Loader_Internal *loader = loader_data;
+   const char *m;
+   unsigned int *p = pixels;
+   char *buffer;
+   Eina_Rectangle master;
+   unsigned int block_length;
+   unsigned int length, offset;
+   unsigned int x, y;
+   unsigned int block_count;
+   Eina_Bool r = EINA_FALSE;
+
+   length = eina_file_size_get(loader->f);
+   offset = OFFSET_BLOCKS;
+
+   *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
+
+   m = eina_file_map_all(loader->f, EINA_FILE_WILLNEED);
+   if (!m) return EINA_FALSE;
+
+   // By definition, prop{.w, .h} == region{.w, .h}
+   EINA_RECTANGLE_SET(&master,
+                      loader->region.x, loader->region.y,
+                      prop->w, prop->h);
+
+   // Allocate space for each ETC1 block (64bytes per 4 * 4 pixels group)
+   block_count = loader->block.width * loader->block.height / (4 * 4);
+   if (loader->compress)
+     buffer = alloca(8 * block_count);
+   else
+     buffer = NULL;
+
+   for (y = 0; y < loader->size.height; y += loader->block.height)
+     for (x = 0; x < loader->size.width; x += loader->block.width)
+       {
+          Eina_Rectangle current;
+          const char *data_start;
+          const char *it;
+          unsigned int expand_length;
+          unsigned int i, j;
+
+          block_length = _tgv_length_get(m + offset, length, &offset);
+
+          if (block_length == 0) goto on_error;
+
+          data_start = m + offset;
+          offset += block_length;
+
+          EINA_RECTANGLE_SET(&current, x, y,
+                             loader->block.width, loader->block.height);
+
+          if (!eina_rectangle_intersection(&current, &master))
+            continue ;
+
+          if (loader->compress)
+            {
+               expand_length = LZ4_uncompress(data_start,
+                                              buffer, block_count * 8);
+               // That's an overhead for now, need to be fixed
+               if (expand_length != block_length)
+                 goto on_error;
+            }
+          else
+            {
+               buffer = (void*) data_start;
+               if (block_count * 8 != block_length)
+                 goto on_error;
+            }
+          it = buffer;
+
+          for (i = 0; i < loader->block.height; i += 4)
+            for (j = 0; j < loader->block.width; j += 4, it += 8)
+              {
+                 Eina_Rectangle current_etc;
+                 unsigned int temporary[4 * 4] = { 0 };
+                 unsigned int offset_x, offset_y;
+                 int k;
+
+                 EINA_RECTANGLE_SET(&current_etc, x + j, y + i, 4, 4);
+
+                 if (!eina_rectangle_intersection(&current_etc, &current))
+                   continue ;
+
+                 if (!rg_etc1_unpack_block(it, temporary, 0))
+                   {
+                      fprintf(stderr, "HOUSTON WE HAVE A PROBLEM ! Block 
starting at {%i, %i} is corrupted !\n", x + j, y + i);
+                      continue ;
+                   }
+
+                 offset_x = current_etc.x - x - j;
+                 offset_y = current_etc.y - y - i;
+                 for (k = 0; k < current_etc.h; k++)
+                   {
+                      memcpy(&p[current_etc.x +
+                                (current_etc.y + k) * loader->region.w],
+                             &temporary[offset_x + (offset_y + k) * 4],
+                             current_etc.w * sizeof (unsigned int));
+                   }
+              }
+       }
+
+   r = EINA_TRUE;
+   *error = EVAS_LOAD_ERROR_NONE;
+
+ on_error:
+   eina_file_map_free(loader->f, (void*) m);
+   return r;
+}
+
+Evas_Image_Load_Func evas_image_load_tgv_func =
+{
+  evas_image_load_file_open_tgv,
+  evas_image_load_file_close_tgv,
+  evas_image_load_file_head_tgv,
+  evas_image_load_file_data_tgv,
+  NULL,
+  EINA_TRUE,
+  EINA_FALSE
+};
+
+static int
+module_open(Evas_Module *em)
+{
+   if (!em) return 0;
+   em->functions = (void *)(&evas_image_load_tgv_func);
+   return 1;
+}
+
+static void
+module_close(Evas_Module *em EINA_UNUSED)
+{
+}
+
+static Evas_Module_Api evas_modapi =
+{
+   EVAS_MODULE_API_VERSION,
+   "tgv",
+   "none",
+   {
+     module_open,
+     module_close
+   }
+};
+
+EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, tgv);
+
+#ifndef EVAS_STATIC_BUILD_TGV
+EVAS_EINA_MODULE_DEFINE(image_loader, tgv);
+#endif

-- 


Reply via email to