forked from enlightenment/efl
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.
This commit is contained in:
parent
4eb983614c
commit
961ecab040
|
@ -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.
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(¤t, x, y,
|
||||
loader->block.width, loader->block.height);
|
||||
|
||||
if (!eina_rectangle_intersection(¤t, &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(¤t_etc, x + j, y + i, 4, 4);
|
||||
|
||||
if (!eina_rectangle_intersection(¤t_etc, ¤t))
|
||||
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
|
Loading…
Reference in New Issue