add generic image loader. allows for GPL loaders, unstable loaders and

more. making a loader is a matter of a binary of a specific name and
evas passes certain input on the cmd-line and your binary produces
output on stdout (and also optionally additionally in a shm or tmp
file).



SVN revision: 58914
This commit is contained in:
Carsten Haitzler 2011-04-26 01:03:06 +00:00
parent a613126be4
commit 0e57095d06
8 changed files with 560 additions and 43 deletions

View File

@ -267,3 +267,21 @@
* Add PSD file format support.
2011-04-26 Carsten Haitzler (The Rasterman)
* Added "generic" image loader that can just execute some command
to load any given file format and read the RGBA data as stdout
from the command, or from an intermediate temporary file or
shared memory segment the executable loader provides. Evas
doesn't care about the loader beyond it providing output via
stdout to define image parameters (size, alpha channel and data
information), and evas will call a specific loader binary
based on extension name of srouce file. This allows for evas to
load image data from files where the loader may be prone to
instability, or require use of libraries that are GPL or
otherwise undesireable in terms of the result to applications
using evas. So basically, if you want a GPL loader or have a
very unstable one, write a loader binary and call it
evas_image_loader.extension e.g. evas_image_loader.pdf or
evas_image_loader.xcf or evas_image_loader.xcf.gz etc. etc.

View File

@ -123,6 +123,7 @@ want_evas_image_loader_tga="yes"
want_evas_image_loader_wbmp="yes"
want_evas_image_loader_ico="yes"
want_evas_image_loader_psd="yes"
want_evas_image_loader_generic="yes"
want_evas_font_loader_eet="yes"
@ -476,6 +477,32 @@ case "$host_os" in
esac
AC_SUBST(dlopen_libs)
SHM_OPEN_LINK=""
AC_MSG_CHECKING([whether shm_open() is present])
LIBS_save=${LIBS}
LIBS="${LIBS} -lrt"
AC_LINK_IFELSE(
[AC_LANG_PROGRAM(
[[
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
]],
[[
int fd;
fd = shm_open("/", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
shm_unlink("/");
]])],
[
have_shm_open="yes"
AC_DEFINE(HAVE_SHM_OPEN, 1, [Have shm_open() call])
SHM_OPEN_LINK="-lrt"
],
[have_shm_open="no"])
LIBS=${LIBS_save}
AC_MSG_RESULT([${have_shm_open}])
AC_SUBST(SHM_OPEN_LINK)
# (shm_open (for cache server)
AC_ARG_ENABLE([evas-cserve],
AC_HELP_STRING([--disable-evas-cserve],
@ -838,6 +865,8 @@ EVAS_CHECK_IMAGE_LOADER([ICO], [${want_evas_image_loader_ico}])
EVAS_CHECK_IMAGE_LOADER([PSD], [${want_evas_image_loader_psd}])
EVAS_CHECK_IMAGE_LOADER([GENERIC], [${want_evas_image_loader_generic}])
#####################################################################
## Cpu based optimizations
@ -1611,6 +1640,7 @@ src/modules/loaders/svg/Makefile
src/modules/loaders/pmaps/Makefile
src/modules/loaders/wbmp/Makefile
src/modules/loaders/psd/Makefile
src/modules/loaders/generic/Makefile
src/modules/savers/Makefile
src/modules/savers/edb/Makefile
src/modules/savers/eet/Makefile
@ -1703,6 +1733,7 @@ echo " TIFF....................: $have_evas_image_loader_tiff"
echo " WBMP....................: $have_evas_image_loader_wbmp"
echo " XPM.....................: $have_evas_image_loader_xpm"
echo " PSD.....................: $have_evas_image_loader_psd"
echo " GENERIC.................: $have_evas_image_loader_generic"
echo
echo "Font Sourcing Systems:"
echo " EET.....................: $have_evas_font_loader_eet"

View File

@ -410,6 +410,26 @@ fi
])
dnl use: EVAS_CHECK_LOADER_DEP_GENERIC(loader, want_static[, ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
AC_DEFUN([EVAS_CHECK_LOADER_DEP_GENERIC],
[
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])
if test "x${have_dep}" = "xyes" ; then
m4_default([$3], [:])
else
m4_default([$4], [:])
fi
])
dnl use: EVAS_CHECK_IMAGE_LOADER(loader, want_loader, macro)

View File

@ -9,36 +9,40 @@ struct ext_loader_s
};
static const struct ext_loader_s loaders[] =
{
{ "png", "png" },
{ "jpg", "jpeg" },
{ "jpeg", "jpeg" },
{ "jfif", "jpeg" },
{ "eet", "eet" },
{ "edj", "eet" },
{ "eap", "eet" },
{ "edb", "edb" },
{ "xpm", "xpm" },
{ "tiff", "tiff" },
{ "tif", "tiff" },
{ "svg", "svg" },
{ "svgz", "svg" },
{ "gif", "gif" },
{ "pbm", "pmaps" },
{ "pgm", "pmaps" },
{ "ppm", "pmaps" },
{ "pnm", "pmaps" },
{ "bmp", "bmp" },
{ "tga", "tga" },
{ "wbmp", "wbmp" },
{ "ico", "ico" },
{ "cur", "ico" },
{ "psd", "psd" }
{ /* map extensions to loaders to use for good first-guess tries */
{ ".png", "png" },
{ ".jpg", "jpeg" },
{ ".jpeg", "jpeg" },
{ ".jfif", "jpeg" },
{ ".eet", "eet" },
{ ".edj", "eet" },
{ ".eap", "eet" },
{ ".edb", "edb" },
{ ".xpm", "xpm" },
{ ".tiff", "tiff" },
{ ".tif", "tiff" },
{ ".svg", "svg" },
{ ".svgz", "svg" },
{ ".svg.gz", "svg" },
{ ".gif", "gif" },
{ ".pbm", "pmaps" },
{ ".pgm", "pmaps" },
{ ".ppm", "pmaps" },
{ ".pnm", "pmaps" },
{ ".bmp", "bmp" },
{ ".tga", "tga" },
{ ".wbmp", "wbmp" },
{ ".ico", "ico" },
{ ".cur", "ico" },
{ ".psd", "psd" },
{ ".pdf", "generic" },
{ ".xcf", "generic" },
{ ".xcf.gz", "generic" }
};
static const char *loaders_name[] =
{
"png", "jpeg", "eet", "xpm", "tiff", "gif", "svg", "pmaps", "edb", "bmp", "tga", "wbmp", "ico", "psd"
{ /* in order of most likely needed */
"png", "jpeg", "eet", "xpm", "tiff", "gif", "svg", "pmaps", "bmp", "tga", "wbmp", "ico", "psd", "edb", "generic"
};
struct evas_image_foreach_loader_data
@ -76,11 +80,10 @@ EAPI int
evas_common_load_rgba_image_module_from_file(Image_Entry *ie)
{
Evas_Image_Load_Func *evas_image_load_func = NULL;
const char *loader = NULL;
const char *loader = NULL, *end;
Evas_Module *em;
char *dot;
unsigned int i;
int ret = EVAS_LOAD_ERROR_NONE;
int len, ret = EVAS_LOAD_ERROR_NONE;
struct evas_image_foreach_loader_data fdata;
@ -96,20 +99,20 @@ evas_common_load_rgba_image_module_from_file(Image_Entry *ie)
return EVAS_LOAD_ERROR_NONE;
}
}
#endif
dot = strrchr (ie->file, '.');
if (dot)
#endif
len = strlen(ie->file);
end = ie->file + len;
for (i = 0; i < (sizeof (loaders) / sizeof(struct ext_loader_s)); i++)
{
for (i = 0, ++dot; i < (sizeof (loaders) / sizeof (struct ext_loader_s)); ++i)
{
if (!strcasecmp(dot, loaders[i].extension))
{
loader = loaders[i].loader;
DBG("known loader '%s' handles extension '%s' of file '%s'",
loader, dot, ie->file);
break;
}
}
int len2 = strlen(loaders[i].extension);
if (len2 > len) continue;
if (!strcasecmp(end - len2, loaders[i].extension))
{
loader = loaders[i].loader;
DBG("known loader '%s' handles extension '%s' of file '%s'",
loader, end - len2, ie->file);
break;
}
}
if (loader)

View File

@ -86,3 +86,9 @@ SUBDIRS += psd
endif
endif
if BUILD_LOADER_GENERIC
if !EVAS_STATIC_BUILD_GENERIC
SUBDIRS += generic
endif
endif

View File

@ -0,0 +1,6 @@
.deps
.libs
Makefile
Makefile.in
*.lo
*.la

View File

@ -0,0 +1,38 @@
MAINTAINERCLEANFILES = Makefile.in
AM_CPPFLAGS = \
-I. \
-I$(top_srcdir)/src/lib \
-I$(top_srcdir)/src/lib/include \
@FREETYPE_CFLAGS@ \
@PIXMAN_CFLAGS@ \
@evas_image_loader_generic_cflags@ \
@EINA_CFLAGS@ \
@EVIL_CFLAGS@ \
@WIN32_CPPFLAGS@
AM_CFLAGS = @WIN32_CFLAGS@
if BUILD_LOADER_GENERIC
if !EVAS_STATIC_BUILD_GENERIC
pkgdir = $(libdir)/evas/modules/loaders/generic/$(MODULE_ARCH)
pkg_LTLIBRARIES = module.la
module_la_SOURCES = evas_image_load_generic.c
module_la_LIBADD = @EINA_LIBS@ @evas_image_loader_generic_libs@ @SHM_OPEN_LINK@ $(top_builddir)/src/lib/libevas.la
module_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -module -avoid-version
module_la_LIBTOOLFLAGS = --tag=disable-static
else
noinst_LTLIBRARIES = libevas_loader_generic.la
libevas_loader_generic_la_SOURCES = evas_image_load_generic.c
libevas_loader_generic_la_LIBADD = @evas_image_loader_generic_libs@ @SHM_OPEN_LINK@
endif
endif

View File

@ -0,0 +1,395 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_EVIL
# include <Evil.h>
#endif
#include "evas_common.h"
#include "evas_private.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
static Eina_Bool evas_image_load_file_head_generic(Image_Entry *ie, const char *file, const char *key, int *error) EINA_ARG_NONNULL(1, 2, 4);
static Eina_Bool evas_image_load_file_data_generic(Image_Entry *ie, const char *file, const char *key, int *error) EINA_ARG_NONNULL(1, 2, 4);
Evas_Image_Load_Func evas_image_load_generic_func =
{
EINA_TRUE,
evas_image_load_file_head_generic,
evas_image_load_file_data_generic
};
static Eina_Bool
illegal_char(const char *str)
{
const char *p;
for (p = str; *p; p++)
{
if (*p < '-') return EINA_TRUE;
if (*p == '/') return EINA_TRUE;
if (*p == ';') return EINA_TRUE;
if (*p == ':') return EINA_TRUE;
if (*p == '<') return EINA_TRUE;
if (*p == '>') return EINA_TRUE;
if (*p == '?') return EINA_TRUE;
if (*p == '[') return EINA_TRUE;
if (*p == '\\') return EINA_TRUE;
if (*p == ']') return EINA_TRUE;
if (*p == '`') return EINA_TRUE;
if (*p == '{') return EINA_TRUE;
if (*p == '|') return EINA_TRUE;
if (*p == '}') return EINA_TRUE;
if (*p == '~') return EINA_TRUE;
if (*p == 0x7f) return EINA_TRUE;
}
return EINA_FALSE;
}
static void
escape_copy(const char *src, char *dst)
{
const char *s;
char *d;
for (s = src, d = dst; *s; s++, d++)
{
if ((*s == ' ') ||
(*s == '!') ||
(*s == '"') ||
(*s == '#') ||
(*s == '$') ||
(*s == '%') ||
(*s == '&') ||
(*s == '\'') ||
(*s == '(') ||
(*s == ')') ||
(*s == '*') ||
(*s == '[') ||
(*s == '\\') ||
(*s == ']') ||
(*s == '`') ||
(*s == '{') ||
(*s == '|') ||
(*s == '}') ||
(*s == '~'))
{
*d = '\\';
d++;
}
*d = *s;
}
*d = 0;
}
static void
dotcat(char *dest, const char *src)
{
int len = strlen(dest);
const char *s;
char *d;
for (d = dest + len, s = src; *s; d++, s++) *d = tolower(*s);
*d = 0;
}
static Eina_Bool
evas_image_load_file_head_generic(Image_Entry *ie, const char *file, const char *key, int *error)
{
Eina_Bool res = EINA_FALSE;
int w = 0, h = 0, alpha = 0;
const char *dot1 = NULL, *dot2 = NULL, *end, *p;
char *cmd = NULL, decoders[3][128], buf[4096];
char *img_loader = "evas_image_loader"; // FIXME: specific path
// eg $libdir/evas/generic_loaders
int cmd_len, len, decoders_num = 0, try_count = 0;
int read_data = 0;
char *tmpfname = NULL, *shmfname = NULL;
DATA32 *body;
FILE *f;
// enough for command + params excluding filem key and loadopts
cmd_len = 1024 + strlen(img_loader);
cmd_len += strlen(file) * 2;
if (key) cmd_len += strlen(key) * 2;
cmd = alloca(cmd_len);
len = strlen(file);
if (len < 1)
{
*error = EVAS_LOAD_ERROR_DOES_NOT_EXIST;
return EINA_FALSE;
}
end = file + len;
for (p = end - 1; p >= file; p--)
{
if ((!dot1) && (*p == '.')) dot1 = p;
else if ((!dot2) && (*p == '.')) dot2 = p;
else if ((dot1) && (dot2)) break;
}
if (dot2)
{
// double extn not too long
if (((end - dot2) <= 10) && (!illegal_char(dot2)))
{
strcpy(&(decoders[decoders_num][0]), img_loader);
dotcat(&(decoders[decoders_num][0]), dot2);
decoders_num++;
}
// single extn not too long
if (((end - dot1) <= 5) && (!illegal_char(dot1)))
{
strcpy(&(decoders[decoders_num][0]), img_loader);
dotcat(&(decoders[decoders_num][0]), dot1);
decoders_num++;
}
strcpy(decoders[decoders_num], img_loader);
decoders_num++;
}
else if (dot1)
{
// single extn not too long
if (((end - dot1) <= 5) && (!illegal_char(dot1)))
{
strcpy(&(decoders[decoders_num][0]), img_loader);
dotcat(&(decoders[decoders_num][0]), dot1);
decoders_num++;
}
strcpy(decoders[decoders_num], img_loader);
decoders_num++;
}
else
{
strcpy(decoders[decoders_num], img_loader);
decoders_num++;
}
for (try_count = 0; try_count < decoders_num; try_count++)
{
// FIXME: strcats could be more efficient, not that it matters much
// here as we are about to build a cmd to exec via a shell that
// will interpret shell stuff and path hunt that will then exec the
// program itself that will dynamically link that will again
// parse the arguments and finally do something...
strcpy(cmd, decoders[try_count]);
strcat(cmd, " ");
// filename first arg
len = strlen(cmd);
escape_copy(file, cmd + len);
if (key)
{
strcat(cmd, " -key ");
len = strlen(cmd);
escape_copy(key, cmd + len);
}
if (ie->load_opts.scale_down_by > 1)
{
strcat(cmd, " -opt-scale-down-by ");
snprintf(buf, sizeof(buf), "%i", ie->load_opts.scale_down_by);
strcat(cmd, buf);
}
if (ie->load_opts.dpi > 0.0)
{
strcat(cmd, " -opt-dpi ");
snprintf(buf, sizeof(buf), "%i", (int)(ie->load_opts.dpi * 1000.0));
strcat(cmd, buf);
}
if ((ie->load_opts.w > 0) &&
(ie->load_opts.h > 0))
{
strcat(cmd, " -opt-size ");
snprintf(buf, sizeof(buf), "%i %i", ie->load_opts.w, ie->load_opts.h);
strcat(cmd, buf);
}
f = popen(cmd, "r");
if (f) break;
}
if (!f)
{
*error = EVAS_LOAD_ERROR_DOES_NOT_EXIST;
return EINA_FALSE;
}
while (fgets(buf, sizeof(buf), f))
{
len = strlen(buf);
if (len > 0)
{
if ((buf[len - 1] == '\n')) buf[len - 1] = 0;
if (!strncmp(buf, "size ", 5))
{
int tw = 0, th = 0;
len = sscanf(buf, "%*s %i %i", &tw, &th);
if (len == 2)
{
if ((tw > 0) && (th > 0))
{
w = tw;
h = th;
}
}
}
else if (!strncmp(buf, "alpha ", 6))
{
int ta;
len = sscanf(buf, "%*s %i", &ta);
if (len == 1)
{
alpha = ta;
}
}
else if (!strncmp(buf, "tmpfile ", 8))
{
tmpfname = buf + 8;
goto getdata;
}
#ifdef HAVE_SHM_OPEN
else if (!strncmp(buf, "shmfile ", 8))
{
shmfname = buf + 8;
goto getdata;
}
#endif
else if (!strncmp(buf, "data", 4))
{
read_data = 1;
goto getdata;
}
}
}
getdata:
if ((!read_data) && (!tmpfname) && (!shmfname))
{
*error = EVAS_LOAD_ERROR_CORRUPT_FILE;
goto on_error;
}
if ((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE) ||
IMG_TOO_BIG(w, h))
{
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
goto on_error;
}
if (alpha) ie->flags.alpha = 1;
ie->w = w;
ie->h = h;
evas_cache_image_surface_alloc(ie, ie->w, ie->h);
body = evas_cache_image_pixels(ie);
if (!body)
{
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
goto on_error;
}
if ((tmpfname) || (shmfname))
{
int fd = -1;
// open
if (tmpfname)
fd = open(tmpfname, O_RDONLY, S_IRUSR);
#ifdef HAVE_SHM_OPEN
else if (shmfname)
fd = shm_open(shmfname, O_RDONLY, S_IRUSR);
#endif
if (fd >= 0)
{
void *addr;
// mmap
addr = mmap(NULL, w * h * sizeof(DATA32),
PROT_READ, MAP_SHARED, fd, 0);
if (addr != MAP_FAILED)
{
memcpy(body, addr, w * h * sizeof(DATA32));
munmap(addr, w * h * sizeof(DATA32));
}
// close
if (tmpfname)
{
close(fd);
unlink(tmpfname);
}
#ifdef HAVE_SHM_OPEN
else if (shmfname)
{
close(fd);
shm_unlink(shmfname);
}
#endif
}
else
{
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
goto on_error;
}
}
else if (read_data)
{
if (fread(body, w * h * sizeof(DATA32), 1, f) != 1)
{
*error = EVAS_LOAD_ERROR_CORRUPT_FILE;
goto on_error;
}
}
res = EINA_TRUE;
*error = EVAS_LOAD_ERROR_NONE;
on_error:
if (f) pclose(f);
return res;
}
static Eina_Bool
evas_image_load_file_data_generic(Image_Entry *ie, const char *file, const char *key, int *error)
{
DATA32 *body;
body = evas_cache_image_pixels(ie);
if (!body)
{
return evas_image_load_file_head_generic(ie, file, key, error);
}
*error = EVAS_LOAD_ERROR_NONE;
return EINA_TRUE;
}
static int
module_open(Evas_Module *em)
{
if (!em) return 0;
em->functions = (void *)(&evas_image_load_generic_func);
return 1;
}
static void
module_close(Evas_Module *em __UNUSED__)
{
}
static Evas_Module_Api evas_modapi =
{
EVAS_MODULE_API_VERSION,
"generic",
"none",
{
module_open,
module_close
}
};
EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, generic);
#ifndef EVAS_STATIC_BUILD_GENERIC
EVAS_EINA_MODULE_DEFINE(image_loader, generic);
#endif