aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac2
-rw-r--r--m4/evas_check_loader.m416
-rw-r--r--src/Makefile_Evas.am52
-rw-r--r--src/lib/evas/common/evas_image_load.c3
-rw-r--r--src/lib/evas/file/evas_module.c4
-rw-r--r--src/modules/evas/loaders/tgv/evas_image_load_tgv.c359
6 files changed, 436 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac
index 3642b96a65..a848345bda 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 b34c28fc05..07d9cdd87e 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 6132592ceb..5f07710a67 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 92c6402f8c..52d3295ec4 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 28b4243cdd..6cb5dc66bd 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 0000000000..308d0b6f22
--- /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