diff --git a/legacy/evas/ChangeLog b/legacy/evas/ChangeLog index 884274731b..7ed0336bc4 100644 --- a/legacy/evas/ChangeLog +++ b/legacy/evas/ChangeLog @@ -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. + diff --git a/legacy/evas/configure.ac b/legacy/evas/configure.ac index 2d22dc9606..b6e87c0536 100644 --- a/legacy/evas/configure.ac +++ b/legacy/evas/configure.ac @@ -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 +#include +#include + ]], + [[ +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" diff --git a/legacy/evas/m4/evas_check_loader.m4 b/legacy/evas/m4/evas_check_loader.m4 index 2bff191559..52f953f36a 100644 --- a/legacy/evas/m4/evas_check_loader.m4 +++ b/legacy/evas/m4/evas_check_loader.m4 @@ -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) diff --git a/legacy/evas/src/lib/engines/common/evas_image_load.c b/legacy/evas/src/lib/engines/common/evas_image_load.c index 2ddbd1bcab..31b3d012ae 100644 --- a/legacy/evas/src/lib/engines/common/evas_image_load.c +++ b/legacy/evas/src/lib/engines/common/evas_image_load.c @@ -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) diff --git a/legacy/evas/src/modules/loaders/Makefile.am b/legacy/evas/src/modules/loaders/Makefile.am index f90cfd21a4..ddd51a5902 100644 --- a/legacy/evas/src/modules/loaders/Makefile.am +++ b/legacy/evas/src/modules/loaders/Makefile.am @@ -86,3 +86,9 @@ SUBDIRS += psd endif endif +if BUILD_LOADER_GENERIC +if !EVAS_STATIC_BUILD_GENERIC +SUBDIRS += generic +endif +endif + diff --git a/legacy/evas/src/modules/loaders/generic/.cvsignore b/legacy/evas/src/modules/loaders/generic/.cvsignore new file mode 100644 index 0000000000..a51c9665e0 --- /dev/null +++ b/legacy/evas/src/modules/loaders/generic/.cvsignore @@ -0,0 +1,6 @@ +.deps +.libs +Makefile +Makefile.in +*.lo +*.la \ No newline at end of file diff --git a/legacy/evas/src/modules/loaders/generic/Makefile.am b/legacy/evas/src/modules/loaders/generic/Makefile.am new file mode 100644 index 0000000000..a9e522ec7e --- /dev/null +++ b/legacy/evas/src/modules/loaders/generic/Makefile.am @@ -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 diff --git a/legacy/evas/src/modules/loaders/generic/evas_image_load_generic.c b/legacy/evas/src/modules/loaders/generic/evas_image_load_generic.c new file mode 100644 index 0000000000..e8bc039209 --- /dev/null +++ b/legacy/evas/src/modules/loaders/generic/evas_image_load_generic.c @@ -0,0 +1,395 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_EVIL +# include +#endif + +#include "evas_common.h" +#include "evas_private.h" + +#include +#include +#include +#include +#include +#include + +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