From d96a7b2cc355408e812c6ff09f53459dee0f80bf Mon Sep 17 00:00:00 2001 From: sachiel Date: Thu, 3 May 2012 21:01:31 +0000 Subject: [PATCH] evas/cserve2: fix typo that kept cserve2 disabled now seriously... Introducing Cache Serve 2. This cache server will initially load images for clients connected to it. It starts slave processes to load these images, and share the loaded images through shm with the clients. All the connection done between clients and the server goes through sockets. The cserve2 build option is turned on by default, while the old cserve was disabled, but in order to make clients use it, the environment variable EVAS_CSERVE2 must be set, and a server must be running. Clients will try to find the socket on a specified location using the environment variable EVAS_CSERVE2_SOCKET. If it's not defined, then the XDG_RUNTIME_DIR path should be used, and finally HOME, TMPDIR and /tmp. git-svn-id: svn+ssh://svn.enlightenment.org/var/svn/e/trunk/evas@70699 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33 --- .gitignore | 4 + AUTHORS | 1 + ChangeLog | 6 + configure.ac | 48 +- src/bin/Makefile.am | 50 + src/bin/dummy_slave.c | 205 +++ src/bin/evas_cserve2.h | 180 +++ src/bin/evas_cserve2_cache.c | 1261 +++++++++++++++++ src/bin/evas_cserve2_client.c | 429 ++++++ src/bin/evas_cserve2_main.c | 456 ++++++ src/bin/evas_cserve2_main_loop_linux.c | 779 +++++++++++ src/bin/evas_cserve2_messages.c | 189 +++ src/bin/evas_cserve2_shm.c | 147 ++ src/bin/evas_cserve2_slave.c | 480 +++++++ src/bin/evas_cserve2_slave.h | 86 ++ src/bin/evas_cserve2_slaves.c | 395 ++++++ src/bin/evas_cserve2_utils.c | 58 + src/bin/loaders/Makefile.am | 47 + src/bin/loaders/bmp/Makefile.am | 32 + src/bin/loaders/bmp/evas_image_load_bmp.c | 1479 ++++++++++++++++++++ src/bin/loaders/eet/Makefile.am | 34 + src/bin/loaders/eet/evas_image_load_eet.c | 158 +++ src/bin/loaders/ico/Makefile.am | 32 + src/bin/loaders/ico/evas_image_load_ico.c | 789 +++++++++++ src/bin/loaders/jpeg/Makefile.am | 33 + src/bin/loaders/jpeg/evas_image_load_jpeg.c | 1118 +++++++++++++++ src/bin/loaders/pmaps/Makefile.am | 33 + src/bin/loaders/pmaps/evas_image_load_pmaps.c | 573 ++++++++ src/bin/loaders/png/Makefile.am | 33 + src/bin/loaders/png/evas_image_load_png.c | 310 ++++ src/bin/loaders/psd/Makefile.am | 32 + src/bin/loaders/psd/evas_image_load_psd.c | 981 +++++++++++++ src/bin/loaders/tga/Makefile.am | 32 + src/bin/loaders/tga/evas_image_load_tga.c | 574 ++++++++ src/bin/loaders/tiff/Makefile.am | 34 + src/bin/loaders/tiff/evas_image_load_tiff.c | 282 ++++ src/bin/loaders/wbmp/Makefile.am | 32 + src/bin/loaders/wbmp/evas_image_load_wbmp.c | 189 +++ src/bin/loaders/xpm/Makefile.am | 34 + src/bin/loaders/xpm/evas_image_load_xpm.c | 650 +++++++++ src/examples/Makefile.am | 6 + src/examples/evas-images3.c | 196 +++ src/lib/Makefile.am | 22 +- src/lib/cache/evas_cache_image.c | 4 +- src/lib/cache2/Makefile.am | 29 + src/lib/cache2/evas_cache2.c | 932 ++++++++++++ src/lib/cache2/evas_cache2.h | 87 ++ src/lib/canvas/Makefile.am | 1 + src/lib/canvas/evas_main.c | 6 + src/lib/canvas/evas_render.c | 4 + src/lib/cserve2/Makefile.am | 27 + src/lib/cserve2/evas_cs2.h | 133 ++ src/lib/cserve2/evas_cs2_client.c | 742 ++++++++++ src/lib/cserve2/evas_cs2_image_data.c | 23 + src/lib/cserve2/evas_cs2_private.h | 34 + src/lib/engines/common/Makefile.am | 1 + src/lib/engines/common/evas_image.h | 5 + src/lib/engines/common/evas_image_load.c | 39 + src/lib/engines/common/evas_image_main.c | 96 +- src/lib/engines/common/evas_image_private.h | 2 - src/lib/engines/common/evas_image_scalecache.c | 58 +- src/lib/include/evas_common.h | 9 + src/modules/engines/buffer/Makefile.am | 1 + src/modules/engines/buffer/evas_outbuf.c | 75 +- src/modules/engines/software_generic/Makefile.am | 1 + src/modules/engines/software_generic/evas_engine.c | 109 +- src/modules/engines/software_x11/Makefile.am | 1 + .../engines/software_x11/evas_xlib_outbuf.c | 155 +- 68 files changed, 15033 insertions(+), 50 deletions(-) create mode 100644 src/bin/dummy_slave.c create mode 100644 src/bin/evas_cserve2.h create mode 100644 src/bin/evas_cserve2_cache.c create mode 100644 src/bin/evas_cserve2_client.c create mode 100644 src/bin/evas_cserve2_main.c create mode 100644 src/bin/evas_cserve2_main_loop_linux.c create mode 100644 src/bin/evas_cserve2_messages.c create mode 100644 src/bin/evas_cserve2_shm.c create mode 100644 src/bin/evas_cserve2_slave.c create mode 100644 src/bin/evas_cserve2_slave.h create mode 100644 src/bin/evas_cserve2_slaves.c create mode 100644 src/bin/evas_cserve2_utils.c create mode 100644 src/bin/loaders/Makefile.am create mode 100644 src/bin/loaders/bmp/Makefile.am create mode 100644 src/bin/loaders/bmp/evas_image_load_bmp.c create mode 100644 src/bin/loaders/eet/Makefile.am create mode 100644 src/bin/loaders/eet/evas_image_load_eet.c create mode 100644 src/bin/loaders/ico/Makefile.am create mode 100644 src/bin/loaders/ico/evas_image_load_ico.c create mode 100644 src/bin/loaders/jpeg/Makefile.am create mode 100644 src/bin/loaders/jpeg/evas_image_load_jpeg.c create mode 100644 src/bin/loaders/pmaps/Makefile.am create mode 100644 src/bin/loaders/pmaps/evas_image_load_pmaps.c create mode 100644 src/bin/loaders/png/Makefile.am create mode 100644 src/bin/loaders/png/evas_image_load_png.c create mode 100644 src/bin/loaders/psd/Makefile.am create mode 100644 src/bin/loaders/psd/evas_image_load_psd.c create mode 100644 src/bin/loaders/tga/Makefile.am create mode 100644 src/bin/loaders/tga/evas_image_load_tga.c create mode 100644 src/bin/loaders/tiff/Makefile.am create mode 100644 src/bin/loaders/tiff/evas_image_load_tiff.c create mode 100644 src/bin/loaders/wbmp/Makefile.am create mode 100644 src/bin/loaders/wbmp/evas_image_load_wbmp.c create mode 100644 src/bin/loaders/xpm/Makefile.am create mode 100644 src/bin/loaders/xpm/evas_image_load_xpm.c create mode 100644 src/examples/evas-images3.c create mode 100644 src/lib/cache2/Makefile.am create mode 100644 src/lib/cache2/evas_cache2.c create mode 100644 src/lib/cache2/evas_cache2.h create mode 100644 src/lib/cserve2/Makefile.am create mode 100644 src/lib/cserve2/evas_cs2.h create mode 100644 src/lib/cserve2/evas_cs2_client.c create mode 100644 src/lib/cserve2/evas_cs2_image_data.c create mode 100644 src/lib/cserve2/evas_cs2_private.h diff --git a/.gitignore b/.gitignore index da31c37..836f7d1 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,10 @@ m4/ltversion.m4 m4/lt~obsolete.m4 src/bin/evas_cserve src/bin/evas_cserve_tool +src/bin/evas_cserve2 +src/bin/evas_cserve2_slave +src/bin/evas_cserve2_client +src/bin/dummy_slave *.gcno *.gcov src/tests/evas_suite diff --git a/AUTHORS b/AUTHORS index 2aa1185..9cad460 100644 --- a/AUTHORS +++ b/AUTHORS @@ -34,3 +34,4 @@ Nicolas Aguirre Rafal Krypa Hyoyoung Chang Jérôme Pinot +Rafael Antognolli diff --git a/ChangeLog b/ChangeLog index 4246cc0..f6ddb7a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -725,3 +725,9 @@ * Compute limited bounding box for Smart object. * Use bounding box to reduce the number of object explored during event propagation. + +2012-05-03 Iván Briano (Sachiel) + + * Add CServe2 and image cache2 that makes use of it. Several changes + in the buffer and software engines to use the right cache if CServe2 + is requested for the application. diff --git a/configure.ac b/configure.ac index eff95ce..911d31c 100644 --- a/configure.ac +++ b/configure.ac @@ -92,6 +92,7 @@ want_fribidi="auto" want_harfbuzz="auto" want_pixman="no" want_evas_cserve="yes" +want_evas_cserve2="yes" want_evas_engine_buffer="yes" want_evas_engine_software_xlib="no" @@ -605,14 +606,34 @@ AC_ARG_ENABLE([evas-cserve], AC_MSG_CHECKING(whether to build shared cache server and support) AC_MSG_RESULT(${want_evas_cserve}) +# (shm_open (for cache server) +AC_ARG_ENABLE([evas-cserve2], + AC_HELP_STRING([--disable-evas-cserve2], + [disable shared cache server 2 support. @<:@default=enabled@:>@]), + [ + if test "x${enableval}" = "xyes" ; then + want_evas_cserve2="yes" + else + want_evas_cserve2="no" + fi + ]) +AC_MSG_CHECKING(whether to build shared cache server 2 and support) +AC_MSG_RESULT(${want_evas_cserve2}) + +if test "x${want_evas_cserve2}" = "xyes"; then + want_evas_cserve="no" +fi + shm_open_libs="" -if test "x${want_evas_cserve}" = "xyes" ; then +if test "x${want_evas_cserve}" = "xyes" -o "x${want_evas_cserve2}" = "xyes" ; then EFL_CHECK_SHM_OPEN( [ - want_evas_cserve="yes" shm_open_libs=${EFL_SHM_OPEN_LIBS} ], - [want_evas_cserve="no"]) + [ + want_evas_cserve="no" + want_evas_cserve2="no" + ]) fi AC_SUBST([shm_open_libs]) @@ -622,6 +643,12 @@ fi AM_CONDITIONAL([EVAS_CSERVE], [test "x${want_evas_cserve}" = "xyes"]) +if test "x${want_evas_cserve2}" = "xyes" ; then + AC_DEFINE(EVAS_CSERVE2, 1, [Shared cache server.]) +fi + +AM_CONDITIONAL([EVAS_CSERVE2], [test "x${want_evas_cserve2}" = "xyes"]) + ####################################### @@ -1865,11 +1892,25 @@ doc/Makefile doc/Doxyfile src/Makefile src/bin/Makefile +src/bin/loaders/Makefile +src/bin/loaders/jpeg/Makefile +src/bin/loaders/png/Makefile +src/bin/loaders/eet/Makefile +src/bin/loaders/tiff/Makefile +src/bin/loaders/xpm/Makefile +src/bin/loaders/bmp/Makefile +src/bin/loaders/ico/Makefile +src/bin/loaders/tga/Makefile +src/bin/loaders/pmaps/Makefile +src/bin/loaders/wbmp/Makefile +src/bin/loaders/psd/Makefile src/lib/Makefile src/lib/canvas/Makefile src/lib/file/Makefile src/lib/cache/Makefile +src/lib/cache2/Makefile src/lib/cserve/Makefile +src/lib/cserve2/Makefile src/lib/engines/Makefile src/lib/engines/common/Makefile src/lib/engines/common/evas_op_add/Makefile @@ -2044,6 +2085,7 @@ echo echo "Features:" echo " MAGIC_DEBUG.............: $want_evas_magic_debug" echo " Cache Server............: $want_evas_cserve" +echo " Cache Server 2..........: $want_evas_cserve2" echo dnl dnl ... DISABLED! some testing has shown that this seems to have some diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am index 7159eaa..0abb3bb 100644 --- a/src/bin/Makefile.am +++ b/src/bin/Makefile.am @@ -39,3 +39,53 @@ $(top_builddir)/src/lib/libevas.la \ @EINA_LIBS@ endif + +if EVAS_CSERVE2 + +SUBDIRS = loaders + +libexec_PROGRAMS = evas_cserve2 evas_cserve2_slave dummy_slave +bin_PROGRAMS = evas_cserve2_client + +AM_CPPFLAGS = \ +-I. \ +-I$(top_srcdir)/src/lib \ +-I$(top_srcdir)/src/lib/include \ +-I$(top_srcdir)/src/lib/cserve2 \ +-DPACKAGE_BIN_DIR=\"$(bindir)\" \ +-DPACKAGE_LIB_DIR=\"$(libdir)\" \ +-DPACKAGE_LIBEXEC_DIR=\"$(libexecdir)\" \ +@EINA_CFLAGS@ + +evas_cserve2_SOURCES = \ +evas_cserve2.h \ +evas_cserve2_slave.h \ +evas_cserve2_main.c \ +evas_cserve2_slaves.c \ +evas_cserve2_messages.c \ +evas_cserve2_shm.c \ +evas_cserve2_cache.c \ +evas_cserve2_main_loop_linux.c + +evas_cserve2_LDADD = \ +@EINA_LIBS@ + +evas_cserve2_client_SOURCES = \ +evas_cserve2_client.c + +evas_cserve2_slave_SOURCES = \ +evas_cserve2_slave.c \ +evas_cserve2_utils.c + +evas_cserve2_slave_LDFLAGS = -export-dynamic + +evas_cserve2_slave_LDADD = \ +@EINA_LIBS@ + +dummy_slave_SOURCES = \ +dummy_slave.c + +dummy_slave_LDADD = \ +@EINA_LIBS@ + +endif diff --git a/src/bin/dummy_slave.c b/src/bin/dummy_slave.c new file mode 100644 index 0000000..a150d67 --- /dev/null +++ b/src/bin/dummy_slave.c @@ -0,0 +1,205 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "evas_cserve2.h" + +static Eina_Bool +command_read(int fd, Slave_Command *cmd, void **params) +{ + ssize_t ret; + int ints[2], size, got = 0; + char *buf; + + ret = read(fd, ints, sizeof(int) * 2); + if (ret < (int)sizeof(int) * 2) + return EINA_FALSE; + + size = ints[0]; + buf = malloc(size); + if (!buf) return EINA_FALSE; + + do { + ret = read(fd, buf + got, size - got); + if (ret < 0) + { + /* EINTR means we were interrupted by a signal before anything + * was sent, and if we are back here it means that signal was + * not meant for us to die. Any other error here is fatal and + * should result in the slave terminating. + */ + if (errno == EINTR) + continue; + free(buf); + return EINA_FALSE; + } + got += ret; + } while (got < size); + + *cmd = ints[1]; + *params = buf; + + return EINA_TRUE; +} + +static Eina_Bool +response_send(int fd, Slave_Command cmd, void *resp, int size) +{ + int sent = 0, ints[2]; + const char *data = resp; + ssize_t ret; + + ints[0] = size; + ints[1] = cmd; + ret = write(fd, ints, sizeof(int) * 2); + if (ret < 0) + return EINA_FALSE; + if (!size) + return EINA_TRUE; + do { + ret = write(fd, data + sent, size - sent); + if (ret < 0) + { + /* EINTR means we were interrupted by a signal before anything + * was sent, and if we are back here it means that signal was + * not meant for us to die. Any other error here is fatal and + * should result in the slave terminating. + */ + if (errno == EINTR) + continue; + return EINA_FALSE; + } + sent += ret; + } while (sent < size); + + return EINA_TRUE; +} + +static Eina_Bool +error_send(int fd, Error_Type err) +{ + return response_send(fd, ERROR, &err, sizeof(Error_Type)); +} + +void * +cserve2_shm_map(const char *name, size_t length, off_t offset) +{ + void *map; + int fd; + + fd = shm_open(name, O_RDWR, 0); + if (fd == -1) + return MAP_FAILED; + + map = mmap(NULL, length, PROT_WRITE, MAP_SHARED, fd, offset); + + close(fd); + + return map; +} + +void +cserve2_shm_unmap(void *map, size_t length) +{ + munmap(map, length); +} + +static Error_Type +image_open(const char *file __UNUSED__, const char *key __UNUSED__, Slave_Msg_Image_Opened *result) +{ + memset(result, 0, sizeof(*result)); + result->w = 32; + result->h = 32; + result->frame_count = 1; + result->loop_count = 0; + result->loop_hint = 0; + result->alpha = EINA_TRUE; + return CSERVE2_NONE; +} + +static Error_Type +image_load(const char *shmfile, Slave_Msg_Image_Load *params) +{ + char *map = cserve2_shm_map(shmfile, params->shm.mmap_size, + params->shm.mmap_offset); + if (map == MAP_FAILED) + return CSERVE2_RESOURCE_ALLOCATION_FAILED; + + memset(map + params->shm.image_offset, 'A', params->shm.image_size); + + return CSERVE2_NONE; +} + +int main(int c, char **v) +{ + int wfd, rfd; + Slave_Command cmd; + void *params = NULL;; + Eina_Bool quit = EINA_FALSE; + + if (c < 3) + return 1; + + wfd = atoi(v[1]); + rfd = atoi(v[2]); + + while (!quit) + { + if (!command_read(rfd, &cmd, ¶ms)) + { + error_send(wfd, CSERVE2_INVALID_COMMAND); + continue; + } + + switch (cmd) + { + case IMAGE_OPEN: + { + Slave_Msg_Image_Opened result; + Slave_Msg_Image_Open *p; + Error_Type err; + const char *file, *key; + p = params; + file = (const char *)(p + sizeof(*p)); + key = file + strlen(file) + 1; + if ((err = image_open(file, key, &result)) != CSERVE2_NONE) + error_send(wfd, err); + else + response_send(wfd, IMAGE_OPEN, &result, + sizeof(Slave_Msg_Image_Opened)); + break; + } + case IMAGE_LOAD: + { + Slave_Msg_Image_Load *load_args = params; + Error_Type err; + const char *shmfile = ((const char *)params) + + sizeof(Slave_Msg_Image_Load); + if ((err = image_load(shmfile, load_args)) != CSERVE2_NONE) + error_send(wfd, err); + else + response_send(wfd, IMAGE_LOAD, NULL, 0); + break; + } + case SLAVE_QUIT: + { + quit = EINA_TRUE; + break; + } + + default: + error_send(wfd, CSERVE2_INVALID_COMMAND); + } + free(params); + } + + return 0; +} diff --git a/src/bin/evas_cserve2.h b/src/bin/evas_cserve2.h new file mode 100644 index 0000000..92cfe62 --- /dev/null +++ b/src/bin/evas_cserve2.h @@ -0,0 +1,180 @@ +#ifndef _EVAS_CSERVE2_H +#define _EVAS_CSERVE2_H + +#include +#include "evas_cs2.h" + +#ifdef ERR +#undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_evas_cserve2_bin_log_dom, __VA_ARGS__) +#ifdef DBG +#undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(_evas_cserve2_bin_log_dom, __VA_ARGS__) +#ifdef WRN +#undef WRN +#endif +#define WRN(...) EINA_LOG_DOM_WARN(_evas_cserve2_bin_log_dom, __VA_ARGS__) +#ifdef INF +#undef INF +#endif +#define INF(...) EINA_LOG_DOM_INFO(_evas_cserve2_bin_log_dom, __VA_ARGS__) + +extern int _evas_cserve2_bin_log_dom; + +typedef struct _Slave_Proc Slave_Proc; +typedef struct _Shm_Handle Shm_Handle; + +typedef enum { + FD_READ = 1, + FD_WRITE = 2, + FD_ERROR = 4 +} Fd_Flags; + +struct _Client { + unsigned int id; + int socket; + struct { + Eina_Bool reading; + char *buf; // buffer of data being read + int done, size; + Eina_Binbuf *pending; // pending data to send + } msg; + struct { + Eina_Hash *referencing; // indexed by client file id + } files; + struct { + Eina_Hash *referencing; // indexed by client image id + } images; +}; + +typedef struct _Client Client; + +struct _Image_Load_Opts { + unsigned int w, h; + unsigned int rx, ry, rw, rh; + unsigned int scale_down_by; + double dpi; + Eina_Bool orientation; +}; + +typedef struct _Image_Load_Opts Image_Load_Opts; + +typedef enum { + IMAGE_OPEN, + IMAGE_LOAD, + SLAVE_QUIT, + ERROR +} Slave_Command; + +struct _Slave_Msg_Image_Open { + Eina_Bool has_opts : 1; + Eina_Bool has_loader_data : 1; +}; + +struct _Slave_Msg_Image_Opened { + int w, h; + int degree; + int scale; /* used by jpeg when loading in smaller sizes */ + int frame_count; + int loop_count; + int loop_hint; /* include Evas.h? Copy the enum around? */ + Eina_Bool alpha : 1; + Eina_Bool animated : 1; + Eina_Bool rotated : 1; + + Eina_Bool has_loader_data : 1; +}; + +struct _Slave_Msg_Image_Load { + int w, h; + Image_Load_Opts opts; + struct { + int mmap_offset; + int image_offset; + int mmap_size; + int image_size; + } shm; + Eina_Bool alpha : 1; + Eina_Bool has_loader_data : 1; +}; + +struct _Slave_Msg_Image_Loaded { + Eina_Bool alpha_sparse : 1; +}; + +typedef struct _Slave_Msg_Image_Open Slave_Msg_Image_Open; +typedef struct _Slave_Msg_Image_Opened Slave_Msg_Image_Opened; +typedef struct _Slave_Msg_Image_Load Slave_Msg_Image_Load; +typedef struct _Slave_Msg_Image_Loaded Slave_Msg_Image_Loaded; + +typedef void (*Fd_Watch_Cb)(int fd, Fd_Flags flags, void *data); +typedef void (*Timeout_Cb)(void); /* void* for compat? */ +typedef void (*Main_Loop_Child_Dead_Cb)(int pid, int status); /* void* for compat? */ +typedef void (*Slave_Dead_Cb)(Slave_Proc *slave, void *data); +typedef void (*Slave_Read_Cb)(Slave_Proc *slave, Slave_Command cmd, void *msg, void *data); +typedef void (*File_Change_Cb)(const char *path, Eina_Bool deleted, void *data); + +void cserve2_client_accept(int fd); +ssize_t cserve2_client_read(Client *client, void *buf, size_t len); +ssize_t cserve2_client_write(Client *client, const void *buf, size_t len); +void cserve2_client_del(Client *client); +void cserve2_client_deliver(Client *client); +void cserve2_client_error_send(Client *client, unsigned int rid, int error_code); +ssize_t cserve2_client_send(Client *client, const void *data, size_t size); + +Eina_Bool cserve2_fd_watch_add(int fd, Fd_Flags flags, Fd_Watch_Cb cb, const void *data); +Eina_Bool cserve2_fd_watch_del(int fd); +Eina_Bool cserve2_fd_watch_flags_set(int fd, Fd_Flags flags); +Eina_Bool cserve2_fd_watch_flags_get(int fd, Fd_Flags *flags); + +Eina_Bool cserve2_file_change_watch_add(const char *path, File_Change_Cb cb, const void *data); +Eina_Bool cserve2_file_change_watch_del(const char *path); + +void cserve2_on_child_dead_set(Main_Loop_Child_Dead_Cb cb); + +void cserve2_timeout_cb_set(int t, Timeout_Cb cb); + +Eina_Bool cserve2_main_loop_setup(void); +void cserve2_main_loop_run(void); +void cserve2_main_loop_finish(void); + +Eina_Bool cserve2_slaves_init(void); +void cserve2_slaves_shutdown(void); +int cserve2_slave_available_get(void); +Eina_Bool cserve2_slave_cmd_dispatch(void *data, Slave_Command cmd, const void *msg, int size); +Slave_Proc *cserve2_slave_run(const char *exe, Slave_Read_Cb read_cb, Slave_Dead_Cb dead_cb, const void *data); +void cserve2_slave_send(Slave_Proc *s, Slave_Command cmd, const char *data, size_t size); +void cserve2_slave_kill(Slave_Proc *s); + +void cserve2_message_handler(int fd, Fd_Flags flags, void *data); + +Shm_Handle *cserve2_shm_request(size_t size); +void cserve2_shm_unref(Shm_Handle *shm); +const char *cserve2_shm_name_get(const Shm_Handle *shm); +off_t cserve2_shm_map_offset_get(const Shm_Handle *shm); +off_t cserve2_shm_offset_get(const Shm_Handle *shm); +size_t cserve2_shm_map_size_get(const Shm_Handle *shm); +size_t cserve2_shm_size_get(const Shm_Handle *shm); + +void cserve2_command_run(Client *client, Message_Type type); + +void cserve2_cache_init(void); +void cserve2_cache_shutdown(void); +void cserve2_cache_client_new(Client *client); +void cserve2_cache_client_del(Client *client); +int cserve2_cache_file_open(Client *client, unsigned int client_file_id, const char *path, const char *key, unsigned int rid); +void cserve2_cache_file_close(Client *client, unsigned int client_file_id); +int cserve2_cache_image_opts_set(Client *client, Msg_Setopts *msg); +void cserve2_cache_image_load(Client *client, unsigned int client_image_id, unsigned int rid); +void cserve2_cache_image_preload(Client *client, unsigned int client_image_id, unsigned int rid); +void cserve2_cache_image_unload(Client *client, unsigned int client_image_id); + +void cserve2_cache_requests_process(void); + +void cserve2_cache_request_opened(Slave_Msg_Image_Opened *resp, void *data); +void cserve2_cache_request_loaded(Slave_Msg_Image_Loaded *resp, void *data); +void cserve2_cache_request_failed(void *data, Error_Type error); + +#endif /* _EVAS_CSERVE2_H */ diff --git a/src/bin/evas_cserve2_cache.c b/src/bin/evas_cserve2_cache.c new file mode 100644 index 0000000..3cd4547 --- /dev/null +++ b/src/bin/evas_cserve2_cache.c @@ -0,0 +1,1261 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "evas_cserve2.h" + +typedef struct _Request Request; +typedef struct _Entry Entry; +typedef struct _Reference Reference; +typedef struct _Waiter Waiter; +typedef struct _File_Data File_Data; +typedef struct _Image_Data Image_Data; +typedef struct _File_Watch File_Watch; + +typedef enum { + CSERVE2_IMAGE_FILE, + CSERVE2_IMAGE_DATA +} Entry_Type; + +struct _Request { + Entry *entry; + Eina_List *waiters; + Eina_Bool processing; +}; + +struct _File_Data { + char *path; + char *key; + int w, h; + int frame_count; + int loop_count; + int loop_hint; + const char *loader_data; + File_Watch *watcher; + Eina_List *images; + Eina_Bool alpha : 1; + Eina_Bool invalid : 1; +}; + +// Default values for load options commented below +struct _Image_Data { + unsigned int file_id; + Entry *file; + struct { + double dpi; // dpi < -1 + int w, h; // w and h < -1 + int scale_down; // scale_down < -1 + int rx, ry, rw, rh; // rx, ry, rw, rh < -1 + Eina_Bool orientation; // orientation == 0 + } opts; + Shm_Handle *shm; + Eina_Bool alpha_sparse : 1; + Eina_Bool unused : 1; + Eina_Bool doload : 1; +}; + +struct _Entry { + unsigned int id; + Eina_List *references; + Request *request; + Entry_Type type; + union { + File_Data file; + Image_Data image; + }; +}; + +struct _Reference { + Client *client; + Entry *entry; + unsigned int client_entry_id; // for reverse lookup + int count; +}; + +struct _Waiter { + Reference *ref; + unsigned int rid; + Message_Type type; +}; + +struct _File_Watch { + const char *path; + Eina_List *entries; +}; + +static unsigned int _file_id = 0; // id unique number +static unsigned int _image_id = 0; // id unique number +static Eina_Hash *file_ids = NULL; // maps path + key --> file_id +static Eina_Hash *file_entries = NULL; // maps file_id --> entry +static Eina_List *open_requests = NULL; + +static Eina_Hash *image_ids = NULL; // maps file id + load opts --> image id +static Eina_Hash *image_entries = NULL; // maps image_id --> entry +static Eina_List *load_requests = NULL; +static Eina_List *spload_requests = NULL; // speculative preload requests + +static Eina_Hash *file_watch = NULL; + +static Eina_List *image_entries_lru = NULL; + +static int max_unused_mem_usage = 5 * 1024; /* in kbytes */ +static int unused_mem_usage = 0; + +static unsigned int +_img_opts_id_get(Image_Data *im, char *buf, int size) +{ + uintptr_t image_id; + + snprintf(buf, size, "%u:%0.3f:%dx%d:%d:%d,%d+%dx%d:%d", + im->file_id, im->opts.dpi, im->opts.w, im->opts.h, + im->opts.scale_down, im->opts.rx, im->opts.ry, + im->opts.rw, im->opts.rh, im->opts.orientation); + + image_id = (uintptr_t)eina_hash_find(image_ids, buf); + + return image_id; +} + +static int +_image_entry_size_get(Entry *e) +{ + int size = sizeof(Entry); + /* XXX: get the overhead of the shm handler too */ + if (e->image.shm) + size += cserve2_shm_size_get(e->image.shm); + return size / 1024; +} + +static void +_file_id_free(Entry *entry) +{ + char buf[4096]; + + DBG("Removing entry file id: %d, file: \"%s:%s\"", + entry->id, entry->file.path, entry->file.key); + snprintf(buf, sizeof(buf), "%s:%s", entry->file.path, entry->file.key); + eina_hash_del_by_key(file_ids, buf); +} + +static void +_image_id_free(Entry *entry) +{ + char buf[4096]; + + DBG("Removing entry image id: %d", entry->id); + + _img_opts_id_get(&entry->image, buf, sizeof(buf)); + eina_hash_del_by_key(image_ids, buf); +} + +static void +_image_entry_free(Entry *entry) +{ + Entry *fentry = entry->image.file; + + if (entry->request) + { + if (entry->request->processing) + entry->request->entry = NULL; + else if (!entry->request->waiters) + { + if (entry->image.doload) + load_requests = eina_list_remove(load_requests, entry->request); + else + spload_requests = eina_list_remove(spload_requests, + entry->request); + } + } + + if (entry->image.unused) + { + image_entries_lru = eina_list_remove(image_entries_lru, entry); + unused_mem_usage -= _image_entry_size_get(entry); + } + + if (fentry) + fentry->file.images = eina_list_remove(fentry->file.images, entry); + if (entry->image.shm) + cserve2_shm_unref(entry->image.shm); + free(entry); +} + +static void +_hash_image_entry_free(void *data) +{ + Entry *entry = data; + + _image_id_free(entry); + _image_entry_free(entry); +} + +static void +_file_entry_free(Entry *entry) +{ + File_Watch *fw; + + // Should we call free for each of the images too? + // If everything goes fine, it's not necessary. + if (entry->file.images) + { + ERR("Freeing file %d (\"%s:%s\") image data still referenced.", + entry->id, entry->file.path, entry->file.key); + eina_list_free(entry->file.images); + } + + if (entry->request) + { + if (entry->request->processing) + entry->request->entry = NULL; + else if (!entry->request->waiters) + { + open_requests = eina_list_remove(open_requests, entry->request); + free(entry->request); + } + } + + if ((fw = entry->file.watcher)) + { + fw->entries = eina_list_remove(fw->entries, entry); + if (!fw->entries) + eina_hash_del_by_key(file_watch, fw->path); + } + + free(entry->file.key); + free(entry->file.path); + eina_stringshare_del(entry->file.loader_data); + free(entry); +} + +static void +_hash_file_entry_free(void *data) +{ + Entry *entry = data; + // TODO: Add some checks to make sure that we are freeing an + // unused entry. + + _file_id_free(entry); + _file_entry_free(entry); +} + +static void +_file_watch_free(void *data) +{ + File_Watch *fw = data; + cserve2_file_change_watch_del(fw->path); + eina_stringshare_del(fw->path); + eina_list_free(fw->entries); + free(fw); +} + +void +cserve2_cache_init(void) +{ + file_ids = eina_hash_string_superfast_new(NULL); + file_entries = eina_hash_int32_new(_hash_file_entry_free); + image_ids = eina_hash_string_superfast_new(NULL); + image_entries = eina_hash_string_superfast_new(_hash_image_entry_free); + file_watch = eina_hash_string_superfast_new(_file_watch_free); +} + +void +cserve2_cache_shutdown(void) +{ + eina_hash_free(image_entries); + eina_hash_free(image_ids); + eina_hash_free(file_entries); + eina_hash_free(file_ids); + eina_hash_free(file_watch); +} + +static void +_request_answer_del(Eina_List **requests, Request *req, Client *client, Error_Type err) +{ + Eina_List *l, *l_next; + Waiter *it; + + DBG("Removing answer requests from entry: %d, client: %d", + req->entry->id, client->id); + + EINA_LIST_FOREACH_SAFE(req->waiters, l, l_next, it) + { + if (it->ref->client->id == client->id) + { + cserve2_client_error_send(client, it->rid, err); + req->waiters = eina_list_remove_list(req->waiters, l); + free(it); + } + } + + // FIXME: Should this be really here? I guess that it should be in the + // entry_free_cb function, or entry_reference_del, when there are no more + // references + if (!req->waiters && !req->entry) + { + *requests = eina_list_remove(*requests, req); + free(req); + } +} + +static void +_request_answer_all_del(Eina_List **requests, Request *req, Error_Type err) +{ + Waiter *it; + + DBG("Removing all answer requests from entry: %d", req->entry->id); + + EINA_LIST_FREE(req->waiters, it) + { + cserve2_client_error_send(it->ref->client, it->rid, err); + free(it); + } + + *requests = eina_list_remove(*requests, req); + free(req); +} + +/* +static void +_open_request_del(Request *req) +{ + Waiter *it; + + EINA_LIST_FREE(req->waiters, it) + free(it); + + req->entry->request = NULL; + + open_requests = eina_list_remove(open_requests, req); + free(req); +} +*/ + +static void +_request_answer_add(Request *req, Reference *ref, unsigned int rid, Message_Type type) +{ + Waiter *w = malloc(sizeof(*w)); + + w->ref = ref; + w->rid = rid; + w->type = type; + + DBG("Add answer request for entry id: %d, client: %d, rid: %d", + req->entry->id, ref->client->id, rid); + req->waiters = eina_list_append(req->waiters, w); +} + +static void +_request_add(Eina_List **requests, Entry *entry, Reference *ref, unsigned int rid, Message_Type type) +{ + Request *req; + + // add the request if it doesn't exist yet + if (!entry->request) + { + req = malloc(sizeof(*req)); + req->entry = entry; + req->waiters = NULL; + req->processing = EINA_FALSE; + entry->request = req; + *requests = eina_list_append(*requests, req); + DBG("Add request for entry id: %d, client: %d, rid: %d", + req->entry->id, ref->client->id, rid); + } + else + req = entry->request; + + if (type != CSERVE2_SETOPTS) + _request_answer_add(req, ref, rid, type); + else + DBG("Adding entry for speculative preload: id=%d", req->entry->id); +} + +static Reference * +_entry_reference_add(Entry *entry, Client *client, unsigned int client_entry_id) +{ + Reference *ref; + + // increase reference for this file + ref = malloc(sizeof(*ref)); + ref->client = client; + ref->entry = entry; + ref->client_entry_id = client_entry_id; + ref->count = 1; + entry->references = eina_list_append(entry->references, ref); + + return ref; +} + +static int +_cserve2_cache_open_requests_process(int nloaders) +{ + Request *req; + Slave_Msg_Image_Open msg; + Entry *entry; + char slave_cmd_data[4096]; + int slave_cmd_size; + int path_len, key_len; + + while ((nloaders > 0) && (open_requests)) + { + + // remove the first element from the list and process this element + req = eina_list_data_get(open_requests); + open_requests = eina_list_remove_list(open_requests, open_requests); + + entry = req->entry; + DBG("Processing OPEN request for file entry: %d", entry->id); + + memset(&msg, 0, sizeof(msg)); + memcpy(slave_cmd_data, &msg, sizeof(msg)); + + path_len = strlen(entry->file.path) + 1; + key_len = strlen(entry->file.key) + 1; + slave_cmd_size = sizeof(msg) + path_len + key_len; + memcpy(slave_cmd_data + sizeof(msg), entry->file.path, path_len); + memcpy(slave_cmd_data + sizeof(msg) + path_len, entry->file.key, + key_len); + cserve2_slave_cmd_dispatch(req, IMAGE_OPEN, slave_cmd_data, + slave_cmd_size); + + req->processing = EINA_TRUE; + nloaders--; + } + + return nloaders; +} + +static void +_image_preloaded_send(Client *client, unsigned int rid) +{ + int size; + Msg_Preloaded msg; + + DBG("Sending PRELOADED reply for RID: %d.", rid); + memset(&msg, 0, sizeof(msg)); + msg.base.rid = rid; + msg.base.type = CSERVE2_PRELOADED; + + size = sizeof(msg); + cserve2_client_send(client, &size, sizeof(size)); + cserve2_client_send(client, &msg, size); +} + +static void +_image_loaded_send(Client *client, Entry *entry, unsigned int rid) +{ + int size; + const char *shmpath = cserve2_shm_name_get(entry->image.shm); + Msg_Loaded msg; + int path_len; + char *buf; + + DBG("Sending LOADED reply for entry %d and RID: %d.", entry->id, rid); + path_len = strlen(shmpath) + 1; + + memset(&msg, 0, sizeof(msg)); + msg.base.rid = rid; + msg.base.type = CSERVE2_LOADED; + + msg.shm.mmap_offset = cserve2_shm_map_offset_get(entry->image.shm); + msg.shm.use_offset = cserve2_shm_offset_get(entry->image.shm); + msg.shm.mmap_size = cserve2_shm_map_size_get(entry->image.shm); + msg.shm.image_size = cserve2_shm_size_get(entry->image.shm); + msg.alpha_sparse = entry->image.alpha_sparse; + + buf = malloc(sizeof(msg) + path_len); + + memcpy(buf, &msg, sizeof(msg)); + memcpy(buf + sizeof(msg), shmpath, path_len); + + size = sizeof(msg) + path_len; + + cserve2_client_send(client, &size, sizeof(size)); + cserve2_client_send(client, buf, size); + + free(buf); +} + +static void +_cserve2_cache_load_request_run(Request *req) +{ + Entry *fentry; + Shm_Handle *shm; + const char *shmpath, *file, *key, *loader; + int shmlen, filelen, keylen, loaderlen; + Slave_Msg_Image_Load msg; + char *buf, *cur; + Entry *ientry; + size_t size; + + ientry = req->entry; + + fentry = ientry->image.file; + // opening shm for this file + shm = cserve2_shm_request(fentry->file.w * fentry->file.h * 4); + shmpath = cserve2_shm_name_get(shm); + shmlen = strlen(shmpath) + 1; + + file = fentry->file.path; + filelen = strlen(file) + 1; + + key = fentry->file.key; + keylen = strlen(key) + 1; + + loader = fentry->file.loader_data; + if (!loader) + loaderlen = 0; + else + loaderlen = strlen(loader) + 1; + + memset(&msg, 0, sizeof(msg)); + msg.w = ientry->image.file->file.w; + msg.h = ientry->image.file->file.h; + msg.alpha = ientry->image.file->file.alpha; + msg.opts.w = ientry->image.opts.w; + msg.opts.h = ientry->image.opts.h; + msg.opts.rx = ientry->image.opts.rx; + msg.opts.ry = ientry->image.opts.ry; + msg.opts.rw = ientry->image.opts.rw; + msg.opts.rh = ientry->image.opts.rh; + msg.opts.scale_down_by = ientry->image.opts.scale_down; + msg.opts.dpi = ientry->image.opts.dpi; + msg.opts.orientation = ientry->image.opts.orientation; + + msg.shm.mmap_offset = cserve2_shm_map_offset_get(shm); + msg.shm.image_offset = cserve2_shm_offset_get(shm); + msg.shm.mmap_size = cserve2_shm_map_size_get(shm); + msg.shm.image_size = cserve2_shm_size_get(shm); + + msg.has_loader_data = !!loaderlen; + + size = sizeof(msg) + shmlen + filelen + keylen + loaderlen; + + buf = calloc(1, size); + + memcpy(buf, &msg, sizeof(msg)); + cur = buf + sizeof(msg); + memcpy(cur, shmpath, shmlen); + + cur += shmlen; + memcpy(cur, file, filelen); + + cur += filelen; + memcpy(cur, key, keylen); + + cur += keylen; + memcpy(cur, loader, loaderlen); + + ientry->image.shm = shm; + + cserve2_slave_cmd_dispatch(req, IMAGE_LOAD, buf, size); + + free(buf); +} + +static int +_cserve2_cache_load_requests_list_process(Eina_List **queue, int nloaders) +{ + Eina_List *skipped = NULL; + Request *req; + + while ((nloaders > 0) && (*queue)) + { + // remove the first element from the list and process this element + req = eina_list_data_get(*queue); + *queue = eina_list_remove_list(*queue, *queue); + + if (!req->entry->image.file) + { + ERR("File entry doesn't exist for entry id %d", req->entry->id); + cserve2_cache_request_failed(req, CSERVE2_INVALID_CACHE); + continue; + } + + if (req->entry->image.file->request) + { + /* OPEN still pending, skip this request */ + skipped = eina_list_append(skipped, req); + continue; + } + + DBG("Processing LOAD request for image entry: %d", req->entry->id); + + _cserve2_cache_load_request_run(req); + + req->processing = EINA_TRUE; + + nloaders--; + } + + EINA_LIST_FREE(skipped, req) + *queue = eina_list_append(*queue, req); + + return nloaders; +} + +static void +_cserve2_cache_load_requests_process(int nloaders) +{ + nloaders = _cserve2_cache_load_requests_list_process(&load_requests, + nloaders); + _cserve2_cache_load_requests_list_process(&spload_requests, nloaders - 1); +} + + +void +cserve2_cache_requests_process(void) +{ + int avail_loaders; + + avail_loaders = cserve2_slave_available_get(); + avail_loaders = _cserve2_cache_open_requests_process(avail_loaders); + _cserve2_cache_load_requests_process(avail_loaders); +} + +static void +_entry_unused_push(Entry *e) +{ + int size = _image_entry_size_get(e); + + if ((size > max_unused_mem_usage) || !(e->image.doload)) + { + eina_hash_del_by_key(image_entries, &e->id); + return; + } + while (size > (max_unused_mem_usage - unused_mem_usage)) + { + Entry *ie = eina_list_data_get(eina_list_last(image_entries_lru)); + eina_hash_del_by_key(image_entries, &ie->id); + } + image_entries_lru = eina_list_append(image_entries_lru, e); + e->image.unused = EINA_TRUE; + unused_mem_usage += size; +} + +static void +_entry_reference_del(Entry *entry, Reference *ref) +{ + entry->references = eina_list_remove(entry->references, ref); + + if (entry->references) + goto free_ref; + + if (entry->type == CSERVE2_IMAGE_FILE) + { + if (entry->file.invalid) + _file_entry_free(entry); + else + { + if (entry->file.images) + { + Entry *ie; + EINA_LIST_FREE(entry->file.images, ie) + ie->image.file = NULL; + entry->file.images = NULL; + } + eina_hash_del_by_key(file_entries, &entry->id); + } + } + else if (entry->type == CSERVE2_IMAGE_DATA) + { + if (!entry->image.file) + eina_hash_del_by_key(image_entries, &entry->id); + else if (entry->image.file->file.invalid) + _image_entry_free(entry); + else + _entry_unused_push(entry); + } + else + ERR("Wrong type of entry."); + +free_ref: + free(ref); +} + +static void +_entry_free_cb(void *data) +{ + Reference *ref = data; + Entry *entry; + + DBG("Removing client reference for entry id: %d, client: %d", + ref->entry->id, ref->client->id); + + entry = ref->entry; + + if (entry->request && !entry->request->processing) + { + if (entry->type == CSERVE2_IMAGE_FILE) + _request_answer_del(&open_requests, entry->request, ref->client, + CSERVE2_REQUEST_CANCEL); + else + { + if (entry->image.doload) + _request_answer_del(&load_requests, entry->request, + ref->client, CSERVE2_REQUEST_CANCEL); + else + _request_answer_del(&spload_requests, entry->request, + ref->client, CSERVE2_REQUEST_CANCEL); + } + } + + _entry_reference_del(entry, ref); +} + +void +cserve2_cache_client_new(Client *client) +{ + client->files.referencing = eina_hash_int32_new(_entry_free_cb); + client->images.referencing = eina_hash_int32_new(_entry_free_cb); +} + +void +cserve2_cache_client_del(Client *client) +{ + // will call _entry_free_cb() for every entry + eina_hash_free(client->images.referencing); + // will call _entry_free_cb() for every entry + eina_hash_free(client->files.referencing); +} + +void +_image_opened_send(Client *client, Entry *entry, unsigned int rid) +{ + int size; + Msg_Opened msg; + + DBG("Sending OPENED reply for entry: %d and RID: %d.", entry->id, rid); + // clear the struct with possible paddings, since it is not aligned. + memset(&msg, 0, sizeof(msg)); + msg.base.rid = rid; + msg.base.type = CSERVE2_OPENED; + msg.image.w = entry->file.w; + msg.image.h = entry->file.h; + msg.image.frame_count = entry->file.frame_count; + msg.image.loop_count = entry->file.loop_count; + msg.image.loop_hint = entry->file.loop_hint; + msg.image.alpha = entry->file.alpha; + + size = sizeof(msg); + cserve2_client_send(client, &size, sizeof(size)); + cserve2_client_send(client, &msg, sizeof(msg)); + // _cserve2_cache_load_requests_process(); +} + +static Entry * +_image_msg_new(Client *client, Msg_Setopts *msg) +{ + Reference *ref; + Entry *im_entry; + + ref = eina_hash_find(client->files.referencing, &msg->file_id); + if (!ref) + { + ERR("Couldn't find file id: %d, for image id: %d", + msg->file_id, msg->image_id); + cserve2_client_error_send(client, msg->base.rid, + CSERVE2_INVALID_CACHE); + return NULL; + } + if (ref->entry->file.invalid) + { + cserve2_client_error_send(client, msg->base.rid, + CSERVE2_FILE_CHANGED); + return NULL; + } + + im_entry = calloc(1, sizeof(*im_entry)); + im_entry->type = CSERVE2_IMAGE_DATA; + im_entry->image.file_id = ref->entry->id; + im_entry->image.file = ref->entry; + im_entry->image.opts.dpi = msg->opts.dpi; + im_entry->image.opts.w = msg->opts.w; + im_entry->image.opts.h = msg->opts.h; + im_entry->image.opts.scale_down = msg->opts.scale_down; + im_entry->image.opts.rx = msg->opts.rx; + im_entry->image.opts.ry = msg->opts.ry; + im_entry->image.opts.rw = msg->opts.rw; + im_entry->image.opts.rh = msg->opts.rh; + im_entry->image.opts.orientation = msg->opts.orientation; + + return im_entry; +} + +/* +static Entry * +_image_default_new(Entry *file_entry) +{ + Entry *im_entry = calloc(1, sizeof(*im_entry)); + im_entry->image.file_id = file_entry->id; + im_entry->image.opts.dpi = -1; + im_entry->image.opts.w = -1; + im_entry->image.opts.h = -1; + im_entry->image.opts.scale_down = -1; + im_entry->image.opts.rx = -1; + im_entry->image.opts.ry = -1; + im_entry->image.opts.rw = -1; + im_entry->image.opts.rh = -1; + im_entry->image.opts.orientation = 0; + + return im_entry; +} +*/ + +static void +_file_changed_cb(const char *path __UNUSED__, Eina_Bool deleted __UNUSED__, void *data) +{ + File_Watch *fw = data; + Entry *e; + Eina_List *l; + + EINA_LIST_FOREACH(fw->entries, l, e) + { + Eina_List *ll; + Entry *ie; + + e->file.invalid = EINA_TRUE; + e->file.watcher = NULL; + + EINA_LIST_FOREACH(e->file.images, ll, ie) + { + _image_id_free(ie); + eina_hash_set(image_entries, &ie->id, NULL); + if (ie->request && !ie->request->processing) + { + if (ie->image.doload) + _request_answer_all_del(&load_requests, ie->request, + CSERVE2_FILE_CHANGED); + else + _request_answer_all_del(&spload_requests, ie->request, + CSERVE2_FILE_CHANGED); + } + ie->request = NULL; + if (ie->image.unused) + _image_entry_free(ie); + } + + _file_id_free(e); + eina_hash_set(file_entries, &e->id, NULL); + if (e->request && !e->request->processing) + _request_answer_all_del(&open_requests, ie->request, + CSERVE2_FILE_CHANGED); + e->request = NULL; + if (!e->file.images && !e->references) + _file_entry_free(e); + } + + eina_hash_del_by_key(file_watch, fw->path); +} + +int +cserve2_cache_file_open(Client *client, unsigned int client_file_id, const char *path, const char *key, unsigned int rid) +{ + uintptr_t file_id; + Entry *entry; + Reference *ref; + File_Watch *fw; + char buf[4906]; + + // look for this file on client references + ref = eina_hash_find(client->files.referencing, &client_file_id); + if (ref) + { + entry = ref->entry; + + if (entry->file.invalid) + { + cserve2_client_error_send(client, rid, CSERVE2_FILE_CHANGED); + return -1; + } + + DBG("found client file id: %d", client_file_id); + ref->count++; + + // File already being loaded, just add the request to be replied + if (entry->request) + _request_answer_add(entry->request, ref, rid, CSERVE2_OPEN); + else + _image_opened_send(client, entry, rid); + return 0; + } + + // search whether the file is already opened by another client + snprintf(buf, sizeof(buf), "%s:%s", path, key); + file_id = (uintptr_t)eina_hash_find(file_ids, buf); + if (file_id) + { + DBG("found file_id %d for client file id %d", + file_id, client_file_id); + entry = eina_hash_find(file_entries, &file_id); + if (!entry) + { + ERR("file \"%s\" is in file_ids hash but not in entries hash.", + buf); + cserve2_client_error_send(client, rid, CSERVE2_INVALID_CACHE); + return -1; + } + ref = _entry_reference_add(entry, client, client_file_id); + eina_hash_add(client->files.referencing, &client_file_id, ref); + if (entry->request) + _request_answer_add(entry->request, ref, rid, CSERVE2_OPEN); + else // File already loaded, otherwise there would be a request + _image_opened_send(client, entry, rid); + return 0; + } + + file_id = _file_id++; + while ((file_id == 0) || (eina_hash_find(file_entries, &file_id))) + file_id = _file_id++; + + DBG("Creating new entry with file_id: %d for file \"%s:%s\"", + file_id, path, key); + entry = calloc(1, sizeof(*entry)); + entry->type = CSERVE2_IMAGE_FILE; + entry->file.path = strdup(path); + entry->file.key = strdup(key); + entry->id = file_id; + eina_hash_add(file_entries, &file_id, entry); + eina_hash_add(file_ids, buf, (void *)file_id); + ref = _entry_reference_add(entry, client, client_file_id); + eina_hash_add(client->files.referencing, &client_file_id, ref); + + fw = eina_hash_find(file_watch, entry->file.path); + if (!fw) + { + fw = calloc(1, sizeof(File_Watch)); + fw->path = eina_stringshare_add(entry->file.path); + cserve2_file_change_watch_add(fw->path, _file_changed_cb, fw); + eina_hash_direct_add(file_watch, fw->path, fw); + } + fw->entries = eina_list_append(fw->entries, entry); + entry->file.watcher = fw; + + _request_add(&open_requests, entry, ref, rid, CSERVE2_OPEN); + + // _open_image_default_set(entry); + + return 0; +} + +void +cserve2_cache_file_close(Client *client, unsigned int client_file_id) +{ + Reference *ref = eina_hash_find(client->files.referencing, + &client_file_id); + if (!ref) + { + ERR("Couldn't find file %d in client hash.", client_file_id); + return; + } + + ref->count--; + if (ref->count <= 0) + // will call _entry_free_cb() for this entry + eina_hash_del_by_key(client->files.referencing, &client_file_id); +} + +int +cserve2_cache_image_opts_set(Client *client, Msg_Setopts *msg) +{ + Entry *entry; + Entry *fentry = NULL; + Reference *ref, *oldref; + unsigned int image_id; + char buf[4096]; + + oldref = eina_hash_find(client->images.referencing, &msg->image_id); + + // search whether the image is already loaded by another client + entry = _image_msg_new(client, msg); + if (!entry) + return -1; + image_id = _img_opts_id_get(&entry->image, buf, sizeof(buf)); + if (image_id) + { // if so, just update the references + free(entry); + DBG("found image_id %d for client image id %d", + image_id, msg->image_id); + entry = eina_hash_find(image_entries, &image_id); + if (!entry) + { + ERR("image id %d is in file_ids hash, but not in entries hash" + "with entry id %d.", msg->image_id, image_id); + cserve2_client_error_send(client, msg->base.rid, + CSERVE2_INVALID_CACHE); + return -1; + } + + if (entry->image.unused) + { + DBG("Re-using old image entry (id: %d) from the LRU list.", + entry->id); + entry->image.unused = EINA_FALSE; + image_entries_lru = eina_list_remove(image_entries_lru, entry); + unused_mem_usage -= _image_entry_size_get(entry); + } + + if (oldref && (oldref->entry->id == image_id)) + return 0; + + ref = _entry_reference_add(entry, client, msg->image_id); + + if (oldref) + eina_hash_del_by_key(client->images.referencing, &msg->image_id); + + eina_hash_add(client->images.referencing, &msg->image_id, ref); + + return 0; + } + + image_id = _image_id++; + while ((image_id == 0) || (eina_hash_find(image_entries, &image_id))) + image_id = _image_id++; + + entry->id = image_id; + eina_hash_add(image_entries, &image_id, entry); + eina_hash_add(image_ids, buf, (void *)image_id); + ref = _entry_reference_add(entry, client, msg->image_id); + + if (oldref) + eina_hash_del_by_key(client->images.referencing, &msg->image_id); + eina_hash_add(client->images.referencing, &msg->image_id, ref); + + fentry = entry->image.file; + fentry->file.images = eina_list_append(fentry->file.images, entry); + + _request_add(&spload_requests, entry, ref, msg->base.rid, CSERVE2_SETOPTS); + return 0; +} + +void +cserve2_cache_image_load(Client *client, unsigned int client_image_id, unsigned int rid) +{ + Entry *entry; + Reference *ref; + + ref = eina_hash_find(client->images.referencing, &client_image_id); + if (!ref) + { + ERR("Can't load: client %d has no image id %d", + client->id, client_image_id); + return; + } + if (ref->entry->image.file->file.invalid) + { + cserve2_client_error_send(client, rid, CSERVE2_FILE_CHANGED); + return; + } + + DBG("Loading image id: %d", ref->entry->id); + + entry = ref->entry; + + // File already being loaded, just add the request to be replied + if (entry->request) + { + _request_answer_add(entry->request, ref, rid, CSERVE2_LOAD); + if ((!entry->request->processing) && (!entry->image.doload)) + { + DBG("Removing entry %d from speculative preload and adding " + "to normal load queue.", entry->id); + spload_requests = eina_list_remove(spload_requests, + entry->request); + load_requests = eina_list_append(load_requests, entry->request); + } + } + else if (entry->image.shm) + _image_loaded_send(client, entry, rid); + else + _request_add(&load_requests, entry, ref, rid, CSERVE2_LOAD); + + entry->image.doload = EINA_TRUE; +} + +void +cserve2_cache_image_preload(Client *client, unsigned int client_image_id, unsigned int rid) +{ + Entry *entry; + Reference *ref; + + ref = eina_hash_find(client->images.referencing, &client_image_id); + if (!ref) + { + ERR("Can't load: client %d has no image id %d", + client->id, client_image_id); + return; + } + if (ref->entry->image.file->file.invalid) + { + cserve2_client_error_send(client, rid, CSERVE2_FILE_CHANGED); + return; + } + + DBG("Loading image id: %d", ref->entry->id); + + entry = ref->entry; + + // File already being loaded, just add the request to be replied + if (entry->request) + { + _request_answer_add(entry->request, ref, rid, CSERVE2_PRELOAD); + if ((!entry->request->processing) && (!entry->image.doload)) + { + DBG("Removing entry %d from speculative preload and adding " + "to normal (pre)load queue.", entry->id); + spload_requests = eina_list_remove(spload_requests, + entry->request); + load_requests = eina_list_append(load_requests, entry->request); + } + } + else if (entry->image.shm) + _image_preloaded_send(client, rid); + else + _request_add(&load_requests, entry, ref, rid, CSERVE2_PRELOAD); + + entry->image.doload = EINA_TRUE; +} + +void +cserve2_cache_image_unload(Client *client, unsigned int client_image_id) +{ + Reference *ref = eina_hash_find(client->images.referencing, + &client_image_id); + if (!ref) + { + ERR("Couldn't find file %d in client hash.", client_image_id); + return; + } + + ref->count--; + if (ref->count <= 0) + // will call _entry_free_cb() for this entry + eina_hash_del_by_key(client->images.referencing, &client_image_id); +} + +void +cserve2_cache_request_opened(Slave_Msg_Image_Opened *resp, void *data) +{ + Waiter *w; + Request *req = data; + Entry *entry; + + entry = req->entry; + if (!entry) + { + Error_Type err = entry->file.invalid + ? CSERVE2_FILE_CHANGED + : CSERVE2_REQUEST_CANCEL; + DBG("File entry for request doesn't exist anymore."); + cserve2_cache_request_failed(req, err); + entry->request = NULL; + return; + } + + entry->request = NULL; + + entry->file.w = resp->w; + entry->file.h = resp->h; + entry->file.frame_count = resp->frame_count; + entry->file.loop_count = resp->loop_count; + entry->file.loop_hint = resp->loop_hint; + entry->file.alpha = resp->alpha; + if (resp->has_loader_data) + { + const char *ldata = (const char *)resp + + sizeof(Slave_Msg_Image_Opened); + entry->file.loader_data = eina_stringshare_add(ldata); + } + + DBG("Finished opening file %d. Notifying %d waiters.", entry->id, + req->waiters ? eina_list_count(req->waiters) : 0); + EINA_LIST_FREE(req->waiters, w) + { + _image_opened_send(w->ref->client, entry, w->rid); + free(w); + } + + free(req); +} + +void +cserve2_cache_request_loaded(Slave_Msg_Image_Loaded *resp, void *data) +{ + Waiter *w; + Request *req = data; + Entry *entry; + + entry = req->entry; + if (!entry) + { + // FIXME: Wouldn't we keep the entry alive when the file changed + // until we send the errors needed and just then delete it? Right now + // the check below just makes no sense. + // Error_Type err = entry->image.file->file.invalid + // ? CSERVE2_FILE_CHANGED + // : CSERVE2_REQUEST_CANCEL; + DBG("Image entry for request doesn't exist anymore."); + cserve2_cache_request_failed(req, CSERVE2_REQUEST_CANCEL); + entry->request = NULL; + return; + } + + entry->request = NULL; + entry->image.alpha_sparse = resp->alpha_sparse; + if (!entry->image.doload) + DBG("Entry %d loaded by speculative preload.", entry->id); + + DBG("Finished loading image %d. Notifying %d waiters.", entry->id, + req->waiters ? eina_list_count(req->waiters) : 0); + EINA_LIST_FREE(req->waiters, w) + { + if (w->type == CSERVE2_LOAD) + _image_loaded_send(w->ref->client, entry, w->rid); + else if (w->type == CSERVE2_PRELOAD) + _image_preloaded_send(w->ref->client, w->rid); + // else w->type == CSERVE2_SETOPTS --> do nothing + + free(w); + } + + free(req); +} + +void +cserve2_cache_request_failed(void *data, Error_Type type) +{ + Waiter *w; + Request *req = data; + Entry *entry; + Eina_List *l; + Reference *ref; + + DBG("Request for entry %p failed with error %d", req->entry, type); + EINA_LIST_FREE(req->waiters, w) + { + cserve2_client_error_send(w->ref->client, w->rid, type); + + w->ref->count--; + free(w); + } + + entry = req->entry; + if (!entry) + goto free_req; + + EINA_LIST_FOREACH(entry->references, l, ref) + { + Eina_Hash *hash; + if (entry->type == CSERVE2_IMAGE_FILE) + hash = ref->client->files.referencing; + else if (entry->type == CSERVE2_IMAGE_DATA) + hash = ref->client->images.referencing; + + eina_hash_del_by_key(hash, &(ref->client_entry_id)); + } + +free_req: + free(req); +} diff --git a/src/bin/evas_cserve2_client.c b/src/bin/evas_cserve2_client.c new file mode 100644 index 0000000..9863dc8 --- /dev/null +++ b/src/bin/evas_cserve2_client.c @@ -0,0 +1,429 @@ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "evas_cserve2.h" + +static const char *SOCK_PATH = "/tmp/cserve2.socket"; +static unsigned int _rid_count = 0; + +static void +debug_msg(const void *buf, int size) +{ + const char *str = buf; + int i; + + printf("message: "); + for (i = 0; i < size; i++) + printf("%x ", str[i]); + + printf("\n"); +} + +static int +_read_line(char *buf, int size) +{ + int len; + char *c; + + fgets(buf, size, stdin); + c = strchr(buf, '#'); + if (c) + *c = '\0'; + else + { + c = strchr(buf, '\n'); + if (c) + *c = '\0'; + } + len = strlen(buf); + + return len + 1; +} + +static void * +parse_input_open(int *size) +{ + char path[4096]; + char key[4096]; + char line[4096]; + int path_len, key_len; + Msg_Open msg; + char *buf; + int file_id; + + _read_line(line, sizeof(line)); + path_len = _read_line(path, sizeof(path)); + key_len = _read_line(key, sizeof(key)); + + sscanf(line, "%d", &file_id); + + buf = malloc(sizeof(msg) + path_len + key_len); + + msg.base.rid = _rid_count++; + msg.base.type = CSERVE2_OPEN; + msg.file_id = file_id; + msg.path_offset = 0; + msg.key_offset = path_len; + + memcpy(buf, &msg, sizeof(msg)); + memcpy(buf + sizeof(msg), path, path_len); + memcpy(buf + sizeof(msg) + path_len, key, key_len); + + *size = sizeof(msg) + path_len + key_len; + + return buf; +} + +static void * +parse_input_setopts(int *size) +{ + Msg_Setopts *msg; + char line[4096]; + int file_id, image_id; + double dpi; + int w, h; + int scale; + int rx, ry, rw, rh; + int orientation; + + // reading file_id, image_id + _read_line(line, sizeof(line)); + sscanf(line, "%d %d", &file_id, &image_id); + + // reading load dpi + _read_line(line, sizeof(line)); + dpi = atof(line); + + // reading load size + _read_line(line, sizeof(line)); + sscanf(line, "%d %d", &w, &h); + + // reading load scale down + _read_line(line, sizeof(line)); + sscanf(line, "%d", &scale); + + // reading load region + _read_line(line, sizeof(line)); + sscanf(line, "%d %d %d %d", &rx, &ry, &rw, &rh); + + // reading orientation + _read_line(line, sizeof(line)); + sscanf(line, "%d", &orientation); + + + msg = calloc(1, sizeof(*msg)); + + msg->base.rid = _rid_count++; + msg->base.type = CSERVE2_SETOPTS; + msg->file_id = file_id; + msg->image_id = image_id; + msg->opts.dpi = dpi; + msg->opts.w = w; + msg->opts.h = h; + msg->opts.scale_down = scale; + msg->opts.rx = rx; + msg->opts.ry = ry; + msg->opts.rw = rw; + msg->opts.rh = rh; + msg->opts.orientation = !!orientation; + + *size = sizeof(*msg); + + return msg; +} + +static void * +parse_input_load(int *size) +{ + Msg_Load *msg; + char line[4096]; + int image_id; + + // read image_id + _read_line(line, sizeof(line)); + sscanf(line, "%d", &image_id); + + msg = calloc(1, sizeof(*msg)); + + msg->base.rid = _rid_count++; + msg->base.type = CSERVE2_LOAD; + msg->image_id = image_id; + + *size = sizeof(*msg); + + return msg; +} + +static void * +parse_input_preload(int *size) +{ + Msg_Preload *msg; + char line[4096]; + int image_id; + + // read image_id + _read_line(line, sizeof(line)); + sscanf(line, "%d", &image_id); + + msg = calloc(1, sizeof(*msg)); + + msg->base.rid = _rid_count++; + msg->base.type = CSERVE2_PRELOAD; + msg->image_id = image_id; + + *size = sizeof(*msg); + + return msg; +} + +static void * +parse_input_unload(int *size) +{ + Msg_Unload *msg; + char line[4096]; + int image_id; + + // read image_id + _read_line(line, sizeof(line)); + sscanf(line, "%d", &image_id); + + msg = calloc(1, sizeof(*msg)); + + msg->base.rid = _rid_count++; + msg->base.type = CSERVE2_UNLOAD; + msg->image_id = image_id; + + *size = sizeof(*msg); + + return msg; +} + +static void * +parse_input_close(int *size) +{ + Msg_Close *msg; + char line[4096]; + int file_id; + + // read file_id + _read_line(line, sizeof(line)); + sscanf(line, "%d", &file_id); + + msg = calloc(1, sizeof(*msg)); + + msg->base.rid = _rid_count++; + msg->base.type = CSERVE2_CLOSE; + msg->file_id = file_id; + + *size = sizeof(*msg); + + return msg; +} + +static void +parse_answer_opened(const void *buf) +{ + const Msg_Opened *msg = buf; + printf("OPENED rid = %d\n", msg->base.rid); + printf("size: %dx%d, alpha: %d\n\n", + msg->image.w, msg->image.h, msg->image.alpha); +} + +static void +parse_answer_setoptsed(const void *buf) +{ + const Msg_Setoptsed *msg = buf; + printf("SETOPTSED rid = %d\n", msg->base.rid); +} + +static void +parse_answer_loaded(const void *buf) +{ + const Msg_Loaded *msg = buf; + const char *path; + + path = ((const char *)msg) + sizeof(*msg); + + printf("LOADED rid = %d\n", msg->base.rid); + printf("shm mmap_offset = 0x%x, use_offset = 0x%x, mmap size = %d bytes\n", + msg->shm.mmap_offset, msg->shm.use_offset, msg->shm.mmap_size); + printf("shm path: \"%s\"\n\n", path); +} + +static void +parse_answer_preloaded(const void *buf) +{ + const Msg_Preloaded *msg = buf; + + printf("PRELOADED rid = %d\n", msg->base.rid); +} + +static void +parse_answer_error(const void *buf) +{ + const Msg_Error *msg = buf; + + printf("ERROR rid = %d, error = %d\n", msg->base.rid, msg->error); +} + +static void +parse_answer(const void *buf) +{ + const Msg_Base *msg = buf; + + switch (msg->type) + { + case CSERVE2_OPENED: + parse_answer_opened(buf); + break; + case CSERVE2_SETOPTSED: + parse_answer_setoptsed(buf); + break; + case CSERVE2_LOADED: + parse_answer_loaded(buf); + break; + case CSERVE2_PRELOADED: + parse_answer_preloaded(buf); + break; + case CSERVE2_ERROR: + parse_answer_error(buf); + break; + default: + printf("unhandled answer: %d\n", msg->type); + } +} + +static struct { + const char *name; + Message_Type type; + void *(*parse_func)(int *size); +} _msg_types[] = { + { "OPEN", CSERVE2_OPEN, parse_input_open }, + { "OPENED", CSERVE2_OPENED, NULL }, + { "SETOPTS", CSERVE2_SETOPTS, parse_input_setopts }, + { "SETOPTSED", CSERVE2_SETOPTSED, NULL }, + { "LOAD", CSERVE2_LOAD, parse_input_load }, + { "LOADED", CSERVE2_LOADED, NULL }, + { "PRELOAD", CSERVE2_PRELOAD, parse_input_preload }, + { "PRELOADED", CSERVE2_PRELOADED, NULL }, + { "UNLOAD", CSERVE2_UNLOAD, parse_input_unload }, + { "CLOSE", CSERVE2_CLOSE, parse_input_close }, + { NULL, 0, NULL } +}; + +int main(void) +{ + int s, t, len, skip_cmd = 0; + struct sockaddr_un remote; + char msgbuf[4096]; + + if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + { + perror("socket"); + exit(1); + } + + printf("Trying to connect...\n"); + + remote.sun_family = AF_UNIX; + strcpy(remote.sun_path, SOCK_PATH); + len = strlen(remote.sun_path) + sizeof(remote.sun_family); + if (connect(s, (struct sockaddr *)&remote, len) == -1) + { + perror("connect"); + exit(1); + } + + printf("Connected.\n"); + + while(!feof(stdin)) + { + char cmd[1024]; + int i; + int size; + void *msg; + + if (skip_cmd) + skip_cmd = 0; + else + printf("\n> "); + fgets(cmd, sizeof(cmd), stdin); + len = strlen(cmd) - 1; + cmd[len] = '\0'; + + if (!len) + { + skip_cmd = 1; + continue; + } + + for (i = 0; _msg_types[i].name; i++) + { + if (!strcmp(cmd, _msg_types[i].name)) + break; + } + + // discards the end of the message if we can't parse it + if (!_msg_types[i].name) + { + printf("Invalid command.\n"); + continue; + } + + if (!_msg_types[i].parse_func) + { + printf("Command %s still unhandled.\n", _msg_types[i].name); + continue; + } + + msg = _msg_types[i].parse_func(&size); + + if (send(s, &size, sizeof(size), MSG_NOSIGNAL) == -1) + { + perror("send size"); + exit(1); + } + if (send(s, msg, size, MSG_NOSIGNAL) == -1) + { + perror("send"); + exit(1); + } + + free(msg); + + usleep(100000); + + if ((t=recv(s, &size, sizeof(size), MSG_DONTWAIT)) > 0) + { + int len = recv(s, msgbuf, size, 0); + printf("size of received message: %d\n", len); + if (len != size) + { + printf("couldn't read entire message.\n"); + continue; + } + debug_msg(&size, sizeof(size)); + debug_msg(msgbuf, size); + parse_answer(msgbuf); + } + else + { + if (t < 0 && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) + continue; + else fprintf(stderr, "Server closed connection\n"); + exit(1); + } + } + + close(s); + + return 0; +} diff --git a/src/bin/evas_cserve2_main.c b/src/bin/evas_cserve2_main.c new file mode 100644 index 0000000..47f120e --- /dev/null +++ b/src/bin/evas_cserve2_main.c @@ -0,0 +1,456 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "evas_cserve2.h" + +#ifdef CSERVE2_BIN_DEFAULT_COLOR +#undef CSERVE2_BIN_DEFAULT_COLOR +#endif +#define CSERVE2_BIN_DEFAULT_COLOR EINA_COLOR_BLUE + +#define MAX_SLAVES 3 + +struct _Slave_Worker { + EINA_INLIST; + void *data; + Slave_Proc *slave; + Eina_Binbuf *ret; + int ret_size; + Eina_Bool done; + Eina_Bool delete_me; +}; + +typedef struct _Slave_Worker Slave_Worker; + +int _evas_cserve2_bin_log_dom = -1; +static unsigned int _client_id = 0; +static Eina_Hash *client_list = NULL; +static Eina_Inlist *slaves_idle = NULL; +static Eina_Inlist *slaves_working = NULL; + +void +cserve2_client_error_send(Client *client, unsigned int rid, int error_code) +{ + int size; + Msg_Error msg; + + // clear the struct with possible paddings, since it is not aligned. + memset(&msg, 0, sizeof(msg)); + msg.base.rid = rid; + msg.base.type = CSERVE2_ERROR; + msg.error = error_code; + + size = sizeof(msg); + cserve2_client_send(client, &size, sizeof(size)); + cserve2_client_send(client, &msg, sizeof(msg)); +} + +static void +_cserve2_client_image_setoptsed(Client *client, unsigned int rid) +{ + int size; + Msg_Setoptsed msg; + + memset(&msg, 0, sizeof(msg)); + msg.base.rid = rid; + msg.base.type = CSERVE2_SETOPTSED; + + size = sizeof(msg); + cserve2_client_send(client, &size, sizeof(size)); + cserve2_client_send(client, &msg, size); +} + +static void +_slave_dead_cb(Slave_Proc *s __UNUSED__, void *data) +{ + Slave_Worker *sw = data; + + if (sw->delete_me) + { + DBG("Slave killed by cserve2. Restart routine."); + free(sw); + return; + } + + if (!sw->data) + { + WRN("Slave died with no pending job, but not requested."); + slaves_idle = eina_inlist_remove(slaves_idle, EINA_INLIST_GET(sw)); + free(sw); + return; + } + + slaves_working = eina_inlist_remove(slaves_working, EINA_INLIST_GET(sw)); + if (!sw->done) + cserve2_cache_request_failed(sw->data, CSERVE2_LOADER_DIED); + if (sw->ret) + eina_binbuf_free(sw->ret); + free(sw); +} + +static void +_slave_read_error(Slave_Worker *sw, void *msg) +{ + Error_Type *error = msg; + + cserve2_cache_request_failed(sw->data, *error); +} + +static void +_slave_read_cb(Slave_Proc *s __UNUSED__, Slave_Command cmd, void *msg, void *data) +{ + Slave_Worker *sw = data; + + DBG("Received reply command '%d' from slave '%p'", cmd, sw->slave); + switch (cmd) + { + case IMAGE_OPEN: + cserve2_cache_request_opened(msg, sw->data); + sw->done = EINA_TRUE; + break; + case IMAGE_LOAD: + cserve2_cache_request_loaded(msg, sw->data); + sw->done = EINA_TRUE; + break; + case ERROR: + _slave_read_error(sw, msg); + break; + default: + ERR("Unrecognized command received from slave: %d", cmd); + } + free(msg); + + // slave finishes its work, put it back to idle list + sw->data = NULL; + slaves_working = eina_inlist_remove(slaves_working, EINA_INLIST_GET(sw)); + + if (!sw->delete_me) // if it is being deleted, it shouldn't be in any list + slaves_idle = eina_inlist_append(slaves_idle, EINA_INLIST_GET(sw)); + + cserve2_cache_requests_process(); +} + +int +cserve2_slave_available_get(void) +{ + return MAX_SLAVES - eina_inlist_count(slaves_working); +} + +Eina_Bool +cserve2_slave_cmd_dispatch(void *data, Slave_Command cmd, const void *msg, int size) +{ + Slave_Worker *sw; + char *exe; + + DBG("Dispatching command to slave. %d idle slaves, %d working slaves.", + eina_inlist_count(slaves_idle), eina_inlist_count(slaves_working)); + + // first check if there's an available slave + if (slaves_idle) + { + sw = EINA_INLIST_CONTAINER_GET(slaves_idle, Slave_Worker); + slaves_idle = eina_inlist_remove(slaves_idle, slaves_idle); + slaves_working = eina_inlist_append(slaves_working, + EINA_INLIST_GET(sw)); + + sw->data = data; + DBG("Dispatching command '%d' to slave '%p'", cmd, sw->slave); + cserve2_slave_send(sw->slave, cmd, msg, size); + return EINA_TRUE; + } + + // no available slave, start a new one + sw = calloc(1, sizeof(Slave_Worker)); + if (!sw) return EINA_FALSE; + + sw->data = data; + exe = getenv("EVAS_CSERVE2_SLAVE"); + if (!exe) exe = "evas_cserve2_slave"; + sw->slave = cserve2_slave_run(exe, _slave_read_cb, _slave_dead_cb, sw); + if (!sw->slave) + { + ERR("Could not launch slave process"); + cserve2_cache_request_failed(data, CSERVE2_LOADER_EXEC_ERR); + free(sw); + return EINA_FALSE; + } + DBG("Dispatching command '%d' to slave '%p'", cmd, sw->slave); + cserve2_slave_send(sw->slave, cmd, msg, size); + + slaves_working = eina_inlist_append(slaves_working, EINA_INLIST_GET(sw)); + + return EINA_TRUE; +} + +static void +_cserve2_client_close(Client *client) +{ + Msg_Close *msg = (Msg_Close *)client->msg.buf; + + INF("Received CLOSE command: RID=%d", msg->base.rid); + INF("File_ID: %d\n", msg->file_id); + + cserve2_cache_file_close(client, msg->file_id); +} + +static void +_cserve2_client_unload(Client *client) +{ + Msg_Unload *msg = (Msg_Unload *)client->msg.buf; + + INF("Received UNLOAD command: RID=%d", msg->base.rid); + INF("Image_ID: %d\n", msg->image_id); + + cserve2_cache_image_unload(client, msg->image_id); +} + +static void +_cserve2_client_preload(Client *client) +{ + Msg_Preload *msg = (Msg_Preload *)client->msg.buf; + + INF("Received PRELOAD command: RID=%d", msg->base.rid); + INF("Image_ID: %d\n", msg->image_id); + + cserve2_cache_image_preload(client, msg->image_id, msg->base.rid); + cserve2_cache_requests_process(); +} + +static void +_cserve2_client_load(Client *client) +{ + Msg_Load *msg = (Msg_Load *)client->msg.buf; + + INF("Received LOAD command: RID=%d", msg->base.rid); + INF("Image_ID: %d\n", msg->image_id); + + cserve2_cache_image_load(client, msg->image_id, msg->base.rid); + cserve2_cache_requests_process(); +} + +static void +_cserve2_client_setopts(Client *client) +{ + Msg_Setopts *msg = (Msg_Setopts *)client->msg.buf; + + INF("Received SETOPTS command: RID=%d", msg->base.rid); + INF("File_ID: %d, Image_ID: %d", msg->file_id, msg->image_id); + INF("Load Options:"); + INF("\tdpi: %03.1f", msg->opts.dpi); + INF("\tsize: %dx%d", msg->opts.w, msg->opts.h); + INF("\tscale down: %d", msg->opts.scale_down); + INF("\tregion: %d,%d + %dx%d", + msg->opts.rx, msg->opts.ry, msg->opts.rw, msg->opts.rh); + INF("\torientation: %d\n", msg->opts.orientation); + + if (cserve2_cache_image_opts_set(client, msg) != 0) + return; + + _cserve2_client_image_setoptsed(client, msg->base.rid); +} + +static void +_cserve2_client_open(Client *client) +{ + Msg_Open *msg = (Msg_Open *)client->msg.buf; + const char *path, *key; + + path = ((const char *)msg) + sizeof(*msg) + msg->path_offset; + key = ((const char *)msg) + sizeof(*msg) + msg->key_offset; + + INF("Received OPEN command: RID=%d", msg->base.rid); + INF("File_ID: %d, path=\"%s\", key=\"%s\"\n", + msg->file_id, path, key); + + cserve2_cache_file_open(client, msg->file_id, path, key, msg->base.rid); + cserve2_cache_requests_process(); +} + +void +cserve2_command_run(Client *client, Message_Type type) +{ + switch (type) + { + case CSERVE2_OPEN: + _cserve2_client_open(client); + break; + case CSERVE2_SETOPTS: + _cserve2_client_setopts(client); + break; + case CSERVE2_LOAD: + _cserve2_client_load(client); + break; + case CSERVE2_PRELOAD: + _cserve2_client_preload(client); + break; + case CSERVE2_UNLOAD: + _cserve2_client_unload(client); + break; + case CSERVE2_CLOSE: + _cserve2_client_close(client); + break; + default: + WRN("Unhandled message"); + } +} + +static void +_slave_quit_send(Slave_Worker *sw) +{ + cserve2_slave_send(sw->slave, SLAVE_QUIT, NULL, 0); +} + +static void +_slaves_restart(void) +{ + Slave_Worker *list, *sw; + + while (slaves_idle) // remove idle workers from idle list + { + sw = EINA_INLIST_CONTAINER_GET(slaves_idle, Slave_Worker); + slaves_idle = eina_inlist_remove(slaves_idle, slaves_idle); + sw->delete_me = EINA_TRUE; + _slave_quit_send(sw); + } + + // working workers will be removed from the working list when they + // finish processing their jobs + list = EINA_INLIST_CONTAINER_GET(slaves_working, Slave_Worker); + EINA_INLIST_FOREACH(list, sw) + { + sw->delete_me = EINA_TRUE; + _slave_quit_send(sw); + } +} + +static void +_timeout_cb(void) +{ + static unsigned int slaves_restart = 0; + + slaves_restart++; + + if (slaves_restart == 10) + { + DBG("kill slaves"); + _slaves_restart(); + slaves_restart = 0; + } + + cserve2_timeout_cb_set(3000, _timeout_cb); +} + +void +cserve2_client_accept(int fd) +{ + Client *client = calloc(1, sizeof(*client)); + + client->socket = fd; + client->id = _client_id++; + + while (eina_hash_find(client_list, &client->id)) + client->id = _client_id++; + + if (!eina_hash_add(client_list, &client->id, client)) + { + Eina_Error err = eina_error_get(); + ERR("Could not add client to the list: \"%s\"", + eina_error_msg_get(err)); + free(client); + close(fd); + } + + cserve2_fd_watch_add(fd, FD_READ | FD_ERROR, cserve2_message_handler, + client); + INF("Client %d connection accepted.", client->id); + + cserve2_cache_client_new(client); +} + +void +cserve2_client_del(Client *client) +{ + eina_hash_del_by_key(client_list, &client->id); +} + +static void +_client_free(void *data) +{ + Client *client = data; + cserve2_cache_client_del(client); + if (client->msg.pending) + eina_binbuf_free(client->msg.pending); + cserve2_fd_watch_del(client->socket); + close(client->socket); + free(data); +} + +static void +_clients_setup(void) +{ + client_list = eina_hash_int32_new(_client_free); +} + +static void +_clients_finish(void) +{ + eina_hash_free(client_list); +} + +int +main(int argc __UNUSED__, const char *argv[] __UNUSED__) +{ + eina_init(); + + _evas_cserve2_bin_log_dom = eina_log_domain_register + ("evas_cserve2_bin", CSERVE2_BIN_DEFAULT_COLOR); + if (_evas_cserve2_bin_log_dom < 0) + { + EINA_LOG_ERR("impossible to create a log domain."); + eina_shutdown(); + exit(1); + } + + if (!cserve2_main_loop_setup()) + { + ERR("could not setup main loop."); + goto error; + } + + if (!cserve2_slaves_init()) + { + ERR("Could not init slaves subsystem."); + goto error; + } + + cserve2_cache_init(); + + _clients_setup(); + + cserve2_timeout_cb_set(3000, _timeout_cb); + + cserve2_main_loop_run(); + + _clients_finish(); + + cserve2_cache_shutdown(); + + _slaves_restart(); + cserve2_slaves_shutdown(); + + cserve2_main_loop_finish(); + + eina_log_domain_unregister(_evas_cserve2_bin_log_dom); + eina_shutdown(); + return 0; + +error: + eina_log_domain_unregister(_evas_cserve2_bin_log_dom); + eina_shutdown(); + exit(1); +} diff --git a/src/bin/evas_cserve2_main_loop_linux.c b/src/bin/evas_cserve2_main_loop_linux.c new file mode 100644 index 0000000..b092cc2 --- /dev/null +++ b/src/bin/evas_cserve2_main_loop_linux.c @@ -0,0 +1,779 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "evas_cserve2.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_EPOLL_EVENTS 10 +#define MAX_INCOMING_CONN 10 + +struct _Watch_Data +{ + int fd; + Fd_Flags flags; + Fd_Watch_Cb callback; + const void *user_data; +}; + +typedef struct _Watch_Data Watch_Data; + +struct _Inotify_Data +{ + EINA_INLIST; + const char *path; + int watchid; + File_Change_Cb cb; + const void *data; +}; + +typedef struct _Inotify_Data Inotify_Data; + +static int epoll_fd = -1; +static int signal_fd = -1; +static int socket_fd = -1; +static int inotify_fd = -1; +static struct sockaddr_un socket_local; +static Eina_Hash *watch_list; +static Eina_Hash *inotify_path_hash; +static Eina_Hash *inotify_id_hash; +static Eina_Bool running; +static Eina_Bool terminate; +static int timeout = -1; // in miliseconds +static long timeout_time = 0; // in miliseconds + +static Timeout_Cb timeout_func = NULL; +static Main_Loop_Child_Dead_Cb reap_children_func = NULL; + +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX sizeof(socket_local.sun_path) +#endif + +static void +_signal_handle_child(struct signalfd_siginfo *sinfo __UNUSED__) +{ + int status; + pid_t pid; + + while ((pid = waitpid(0, &status, WNOHANG)) > 0) + { + if (reap_children_func) + { + reap_children_func(pid, status); + continue; + } + + DBG("Received SIGCHLD and no handler is set."); + + if (WIFEXITED(status)) + DBG("Child '%d' exited with status '%d'.", pid, + WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + DBG("Child '%d' exited with signal '%d'.", pid, + WTERMSIG(status)); + } +} + +static void +_signal_handle_int(struct signalfd_siginfo *sinfo __UNUSED__) +{ + DBG("Received SIGINT. Honoring request."); + terminate = EINA_TRUE; +} + +static void +_signal_handle_term(struct signalfd_siginfo *sinfo __UNUSED__) +{ + DBG("Received SIGTERM. Honoring request."); + terminate = EINA_TRUE; +} + +static void +_signalfd_handler(int fd, Fd_Flags flags __UNUSED__, void *data __UNUSED__) +{ + struct signalfd_siginfo sinfo; + ssize_t ret; + + for (;;) + { + ret = read(fd, &sinfo, sizeof(struct signalfd_siginfo)); + if (ret == -1) + { + if (errno == EAGAIN) + break; + ERR("Error reading from signal fd: %m."); + return; + } + + switch(sinfo.ssi_signo) + { + case SIGCHLD: + _signal_handle_child(&sinfo); + break; + case SIGINT: + _signal_handle_int(&sinfo); + break; + case SIGTERM: + _signal_handle_term(&sinfo); + break; + default: + ERR("Caught unexpected signal '%d'.", sinfo.ssi_signo); + } + } +} + +static int +_signalfd_setup(void) +{ + sigset_t mask; + + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + + if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) + { + ERR("Could not set mask of handled signals."); + return -1; + } + + signal_fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC); + if (signal_fd == -1) + ERR("Could not create file descriptor from signalfd."); + + /* ignore SIGPIPE so it's handled by write() and send() as needed */ + signal(SIGPIPE, SIG_IGN); + + return signal_fd; +} + +static void +_signalfd_finish(void) +{ + sigset_t mask; + + cserve2_fd_watch_del(signal_fd); + + sigemptyset(&mask); + sigprocmask(SIG_BLOCK, NULL, &mask); + + close(signal_fd); + sigprocmask(SIG_UNBLOCK, &mask, NULL); + + signal(SIGPIPE, SIG_DFL); +} + +static void +_socketfd_handler(int fd __UNUSED__, Fd_Flags flags __UNUSED__, void *data __UNUSED__) +{ + struct sockaddr_un remote; + unsigned int len; + int s; + + len = sizeof(struct sockaddr_un); + s = accept4(socket_fd, &remote, &len, SOCK_CLOEXEC); + if (s == -1) + { + ERR("Could not accept socket: \"%s\"", strerror(errno)); + return; + } + + cserve2_client_accept(s); +} + +static void +_socket_path_set(char *path) +{ + char *env; + char buf[UNIX_PATH_MAX]; + + env = getenv("EVAS_CSERVE2_SOCKET"); + if (env && env[0]) + { + strncpy(path, env, UNIX_PATH_MAX - 1); + return; + } + + env = getenv("XDG_RUNTIME_DIR"); + if (!env || !env[0]) + { + env = getenv("HOME"); + if (!env || !env[0]) + { + env = getenv("TMPDIR"); + if (!env || !env[0]) + env = "/tmp"; + } + } + + snprintf(buf, sizeof(buf), "%s/evas-cserve2-%x.socket", env, getuid()); + /* FIXME: check we can actually create this socket */ + strcpy(path, buf); +} + +static int +_socketfd_setup(void) +{ + int s; + int len; + + s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (s == -1) + { + ERR("Could not create socketfd: \"%s\"", strerror(errno)); + return -1; + } + + socket_local.sun_family = AF_UNIX; + _socket_path_set(socket_local.sun_path); + DBG("Using '%s' as server socket.", socket_local.sun_path); + unlink(socket_local.sun_path); + len = strlen(socket_local.sun_path) + sizeof(socket_local.sun_family); + if (bind(s, (struct sockaddr *)&socket_local, len) == -1) + { + ERR("Could not bind socketfd: \"%s\"", strerror(errno)); + close(s); + return -1; + } + + if (listen(s, MAX_INCOMING_CONN) == -1) + { + ERR("Could not listen on socketfd: \"%s\"", strerror(errno)); + close(s); + unlink(socket_local.sun_path); + return -1; + } + + socket_fd = s; + + return s; +} + +static void +_socketfd_finish(void) +{ + close(socket_fd); + unlink(socket_local.sun_path); +} + +static void +_inotifyfd_handler(int fd, Fd_Flags flags, void *data __UNUSED__) +{ + char buffer[16384]; + int i = 0; + ssize_t size; + + if (flags & FD_ERROR) + { + ERR("Error on inotify file handler, what to do?"); + return; + } + + size = read(fd, buffer, sizeof(buffer)); + while (i < size) + { + struct inotify_event *event; + int event_size; + Eina_Inlist *ids, *itr; + Inotify_Data *id; + Eina_Bool deleted; + + event = (struct inotify_event *)&buffer[i]; + event_size = sizeof(struct inotify_event) + event->len; + i += event_size; + + ids = eina_hash_find(inotify_id_hash, &event->wd); + if (!ids) continue; + + deleted = !!(event->mask + & (IN_DELETE_SELF | IN_MOVE_SELF + | IN_IGNORED | IN_UNMOUNT)); + EINA_INLIST_FOREACH_SAFE(ids, itr, id) + id->cb(id->path, deleted, (void *)id->data); + } +} + +static void +_inotify_id_hash_free_cb(void *data) +{ + Eina_Inlist *list = data; + + while (list) + { + Inotify_Data *id; + id = EINA_INLIST_CONTAINER_GET(list, Inotify_Data); + list = eina_inlist_remove(list, list); + eina_stringshare_del(id->path); + free(id); + } +} + +static int +_inotifyfd_setup(void) +{ + inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (inotify_fd == -1) + { + ERR("Could not create inotifyfd: \"%s\"", strerror(errno)); + return -1; + } + inotify_path_hash = eina_hash_string_superfast_new(NULL); + inotify_id_hash = eina_hash_int32_new(_inotify_id_hash_free_cb); + + return inotify_fd; +} + +static void +_inotifyfd_finish(void) +{ + close(inotify_fd); + + eina_hash_free(inotify_path_hash); + eina_hash_free(inotify_id_hash); +} + +static void +_watch_data_free_cb(void *data) +{ + free(data); +} + +Eina_Bool +cserve2_main_loop_setup(void) +{ + int sfd; + int socket; + int ifd; + + epoll_fd = epoll_create1(EPOLL_CLOEXEC); + + if (epoll_fd < 0) + { + ERR("Could not create epoll fd."); + return EINA_FALSE; + } + + watch_list = eina_hash_int32_new(_watch_data_free_cb); + if (!watch_list) + { + ERR("Could not create watch list hash struct."); + close(epoll_fd); + return EINA_FALSE; + } + + sfd = _signalfd_setup(); + if (sfd == -1) + { + ERR("Could not setup signalfd."); + close(epoll_fd); + eina_hash_free(watch_list); + return EINA_FALSE; + } + + DBG("Add watch for signal fd: %d", sfd); + if (!cserve2_fd_watch_add(sfd, FD_READ, _signalfd_handler, NULL)) + { + ERR("Could not add watch for signalfd."); + close(sfd); + close(epoll_fd); + eina_hash_free(watch_list); + return EINA_FALSE; + } + + socket = _socketfd_setup(); + if (socket == -1) + { + ERR("Could not setup socketfd."); + goto error_socket; + } + + DBG("Add watch for socket fd: %d", socket); + if (!cserve2_fd_watch_add(socket, FD_READ, _socketfd_handler, NULL)) + { + ERR("Could not add watch for socketf."); + close(socket); + goto error_socket; + } + + ifd = _inotifyfd_setup(); + if (ifd == -1) + { + ERR("Could not setup inotify."); + goto error_inotify; + } + + DBG("Add watch for inotify fd: %d", ifd); + if (!cserve2_fd_watch_add(ifd, FD_READ, _inotifyfd_handler, NULL)) + { + ERR("Could not add watch for inotifyfd."); + close(ifd); + goto error_inotify; + } + + return EINA_TRUE; + +error_inotify: + _socketfd_finish(); + +error_socket: + close(sfd); + close(epoll_fd); + eina_hash_free(watch_list); + return EINA_FALSE; +} + +void +cserve2_main_loop_finish(void) +{ + _socketfd_finish(); + + _signalfd_finish(); + + _inotifyfd_finish(); + + eina_hash_free(watch_list); + + close(epoll_fd); +} + +Eina_Bool +cserve2_fd_watch_add(int fd, Fd_Flags flags, Fd_Watch_Cb cb, const void *data) +{ + Watch_Data *w_data; + struct epoll_event ev; + int err; + + DBG("Add watch for fd %d, flags = 0x%x", fd, flags); + + if ((fd < 0) || (!cb)) + { + ERR("Can't add watch: fd = %d, callback = %p", fd, cb); + return EINA_FALSE; + } + + w_data = calloc(1, sizeof(*w_data)); + w_data->fd = fd; + w_data->flags = flags; + w_data->callback = cb; + w_data->user_data = data; + + if (!eina_hash_add(watch_list, &fd, w_data)) + { + ERR("Could not add watch for fd %d to the hash.", fd); + free(w_data); + return EINA_FALSE; + } + + memset(&ev, 0, sizeof(ev)); + if (flags & FD_READ) + ev.events |= EPOLLIN; + if (flags & FD_WRITE) + ev.events |= EPOLLOUT; + ev.data.ptr = w_data; + + err = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev); + if (!err < 0) + { + ERR("Could not create epoll watch for fd %d.", fd); + eina_hash_del(watch_list, &fd, NULL); + return EINA_FALSE; + } + + return EINA_TRUE; +} + +Eina_Bool +cserve2_fd_watch_flags_set(int fd, Fd_Flags flags) +{ + Watch_Data *w_data; + struct epoll_event ev; + int err; + + DBG("Set watch flags for fd %d, flags = 0x%x", fd, flags); + + if (fd < 0) + { + ERR("Can't modify watch: fd = %d", fd); + return EINA_FALSE; + } + + w_data = eina_hash_find(watch_list, &fd); + if (!w_data) + { + ERR("Couldn't find data for fd %d: not being watched.", fd); + return EINA_FALSE; + } + + if (flags == w_data->flags) + return EINA_TRUE; + + memset(&ev, 0, sizeof(ev)); + if (flags & FD_READ) + ev.events |= EPOLLIN; + if (flags & FD_WRITE) + ev.events |= EPOLLOUT; + ev.data.ptr = w_data; + + err = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ev); + if (err < 0) + { + ERR("Could not modify epoll watch for fd: %d", fd); + return EINA_FALSE; + } + + return EINA_TRUE; +} + +Eina_Bool +cserve2_fd_watch_flags_get(int fd, Fd_Flags *flags) +{ + Watch_Data *w_data; + if (fd < 0) + { + ERR("Can't get flags for watch: fd = %d", fd); + return EINA_FALSE; + } + + w_data = eina_hash_find(watch_list, &fd); + if (!w_data) + { + ERR("Couldn't find data for fd: %d. Is it really being watched?", fd); + return EINA_FALSE; + } + + *flags = w_data->flags; + + return EINA_TRUE; +} + +Eina_Bool +cserve2_fd_watch_del(int fd) +{ + int err; + + DBG("Remove watch for fd %d", fd); + + if (fd < 0) + return EINA_FALSE; + + if (!eina_hash_del(watch_list, &fd, NULL)) + ERR("Could not remove watch for fd %d from watch list hash.", fd); + + err = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL); + if (err < 0) + { + ERR("Could not remove epoll watch for fd %d.", fd); + return EINA_FALSE; + } + + return EINA_TRUE; +} + +Eina_Bool +cserve2_file_change_watch_add(const char *path, File_Change_Cb cb, const void *data) +{ + Inotify_Data *id; + Eina_Inlist *ids; + unsigned int mask = IN_ATTRIB | IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF; + + id = eina_hash_find(inotify_path_hash, path); + if (id) + { + ERR("Attempt to watch changes for path '%s', which is already " + "being watched.", path); + return EINA_FALSE; + } + + id = calloc(1, sizeof(Inotify_Data)); + if (!id) + { + ERR("Could not alloc Inotify_Data instance."); + return EINA_FALSE; + } + + id->watchid = inotify_add_watch(inotify_fd, path, mask); + if (id->watchid == -1) + { + ERR("Could not add inotify watch for %s: %m.", path); + free(id); + return EINA_FALSE; + } + + id->path = eina_stringshare_add(path); + id->cb = cb; + id->data = data; + + eina_hash_direct_add(inotify_path_hash, id->path, id); + + ids = eina_hash_find(inotify_id_hash, &id->watchid); + ids = eina_inlist_append(ids, EINA_INLIST_GET(id)); + eina_hash_set(inotify_id_hash, &id->watchid, ids); + + return EINA_TRUE; +} + +Eina_Bool +cserve2_file_change_watch_del(const char *path) +{ + Inotify_Data *id; + Eina_Inlist *ids; + int wd; + + id = eina_hash_set(inotify_path_hash, path, NULL); + if (!id) + { + ERR("Requested to remove change watch for %s, but it's not being " + "watched.", path); + return EINA_FALSE; + } + + ids = eina_hash_find(inotify_id_hash, &id->watchid); + ids = eina_inlist_remove(ids, EINA_INLIST_GET(id)); + eina_hash_set(inotify_id_hash, &id->watchid, ids); + + wd = id->watchid; + eina_stringshare_del(id->path); + free(id); + + if (!ids) + { + if (inotify_rm_watch(inotify_fd, wd) == -1) + { + ERR("Could not remove change watch for %s: %m", path); + return EINA_FALSE; + } + } + + return EINA_TRUE; +} + +static void +_update_timeout(void) +{ + struct timeval timev; + long cur_time; + + if (timeout <= 0) + return; + + gettimeofday(&timev, NULL); + cur_time = timev.tv_sec * 1000 + timev.tv_usec / 1000; + timeout -= cur_time - timeout_time; + timeout_time = cur_time; + + if (timeout < 0) + timeout = 0; +} + +void +cserve2_timeout_cb_set(int t, Timeout_Cb cb) +{ + struct timeval timev; + if (cb && t <= 0) + { + ERR("timeout must be a value greater than 0 to set a callback." + " given timeout: %d miliseconds", t); + return; + } + + if (!cb) + { + DBG("Removing timeout callback."); + timeout = -1; + cb = NULL; + return; + } + + //DBG("Setting timeout to: %d miliseconds", t); + gettimeofday(&timev, NULL); + timeout_time = timev.tv_sec * 1000 + timev.tv_usec / 1000; + timeout = t; + timeout_func = cb; +} + +void +cserve2_on_child_dead_set(Main_Loop_Child_Dead_Cb cb) +{ + reap_children_func = cb; +} + +void +cserve2_main_loop_run(void) +{ + running = EINA_TRUE; + terminate = EINA_FALSE; + + for (;;) + { + struct epoll_event events[MAX_EPOLL_EVENTS]; + int n, nfds; + + if (terminate) + break; + + nfds = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, timeout); + if (nfds < 0) + { + ERR("An error occurred when reading the epoll fd."); + ERR("%s", strerror(errno)); + break; + } + if (nfds == 0) // timeout occurred + { + timeout = -1; + if (!timeout_func) + ERR("Timeout expired, but no timeout function set."); + else + timeout_func(); + } + + for (n = 0; n < nfds; n++) + { + Watch_Data *data = events[n].data.ptr; + Fd_Flags flags = 0; + + if (!data) + continue; + + if (!data->callback) + continue; + + if (events[n].events & EPOLLIN) + flags |= FD_READ; + if (events[n].events & EPOLLOUT) + flags |= FD_WRITE; + if (events[n].events & EPOLLERR) + flags |= FD_ERROR; + data->callback(data->fd, flags, (void *)data->user_data); + } + + _update_timeout(); + } + + running = EINA_FALSE; +} + +ssize_t +cserve2_client_read(Client *client, void *buf, size_t len) +{ + return recv(client->socket, buf, len, MSG_DONTWAIT); +} + +ssize_t +cserve2_client_write(Client *client, const void *data, size_t size) +{ + return send(client->socket, data, size, MSG_NOSIGNAL | MSG_DONTWAIT); +} diff --git a/src/bin/evas_cserve2_messages.c b/src/bin/evas_cserve2_messages.c new file mode 100644 index 0000000..5186896 --- /dev/null +++ b/src/bin/evas_cserve2_messages.c @@ -0,0 +1,189 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "evas_cserve2.h" + +// #define DEBUG_MSG 1 + +static void +debug_msg(const char *typestr, const void *buf, int size) +{ +#ifdef DEBUG_MSG + const char *str = buf; + int i; + + printf("message %s: ", typestr); + for (i = 0; i < size; i++) + printf("%x ", str[i]); + + printf("\n"); +#endif +} + +static void +_client_msg_allocate_buf(Client *client, int msgsize) +{ + client->msg.reading = EINA_TRUE; + client->msg.buf = malloc(msgsize + 1); + client->msg.size = msgsize; + client->msg.done = 0; +} + +static void +_client_msg_free(Client *client) +{ + client->msg.reading = EINA_FALSE; + free(client->msg.buf); +} + +static void +_client_msg_parse(Client *client) +{ + Msg_Base *msg = (Msg_Base *)client->msg.buf; + DBG("Message received. Size: %d; type = %d", + client->msg.size, msg->type); + + cserve2_command_run(client, msg->type); +} + +static void +_client_msg_read(Client *client, int done) +{ + client->msg.done += done; + if (client->msg.done == client->msg.size) + { + debug_msg("received", client->msg.buf, client->msg.size); + _client_msg_parse(client); + _client_msg_free(client); + } +} + +void +cserve2_message_handler(int fd __UNUSED__, Fd_Flags flags, void *data) +{ + Client *client = data; + int len; + int msgsize; + + if (flags & FD_ERROR) + { + ERR("Error on socket for client: %d", client->id); + goto client_close; + } + + if (flags & FD_WRITE) + cserve2_client_deliver(client); + + if (!(flags & FD_READ)) + return; + + if (!client->msg.reading) + len = cserve2_client_read(client, &msgsize, sizeof(msgsize)); + else + len = cserve2_client_read(client, &client->msg.buf[client->msg.done], + client->msg.size - client->msg.done); + + if (!len) + { + INF("Client %d connection closed.", client->id); + goto client_close; + } + + if (len < 0) + { + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + WRN("No data to read but the message handler was called."); + return; + } + WRN("Error when reading message from client: \"%s\"", + strerror(errno)); + // FIXME: Should we close the connection, or just send an ERROR + // message? + goto client_close; + } + + if (!client->msg.reading) + _client_msg_allocate_buf(client, msgsize); + else + _client_msg_read(client, len); + + return; + +client_close: + if (client->msg.reading) + _client_msg_free(client); + cserve2_client_del(client); +} + +void +cserve2_client_deliver(Client *client) +{ + size_t sent, size; + const char *str; + + if (!client->msg.pending) + { + Fd_Flags cur_flags; + cserve2_fd_watch_flags_get(client->socket, &cur_flags); + cur_flags ^= FD_WRITE; + cserve2_fd_watch_flags_set(client->socket, cur_flags); + return; + } + + size = eina_binbuf_length_get(client->msg.pending); + str = (const char *)eina_binbuf_string_get(client->msg.pending); + sent = cserve2_client_write(client, str, size); + if (sent == size) + { + eina_binbuf_free(client->msg.pending); + client->msg.pending = NULL; + return; + } + + eina_binbuf_remove(client->msg.pending, 0, sent); +} + +ssize_t +cserve2_client_send(Client *client, const void *data, size_t size) +{ + ssize_t sent; + + debug_msg("sent", data, size); + if (client->msg.pending) + { + eina_binbuf_append_length + (client->msg.pending, (unsigned char *)data, size); + return size; + } + + sent = cserve2_client_write(client, data, size); + if ((sent < 0) && ((errno != EAGAIN) && (errno != EWOULDBLOCK))) + { + // FIXME: Big error when writing on the socket to the client, + // so we must close the connection to the client and remove + // its references inside our cache. + WRN("Error on socket with client %d: %s", client->id, strerror(errno)); + if (client->msg.reading) + _client_msg_free(client); + cserve2_client_del(client); + return sent; + } + if (sent < 0) + sent = 0; + if (sent < (int)size) + { + Fd_Flags cur_flags; + client->msg.pending = eina_binbuf_new(); + eina_binbuf_append_length + (client->msg.pending, (unsigned char *)data + sent, size - sent); + cserve2_fd_watch_flags_get(client->socket, &cur_flags); + cur_flags |= FD_WRITE; + cserve2_fd_watch_flags_set(client->socket, cur_flags); + } + return size; +} diff --git a/src/bin/evas_cserve2_shm.c b/src/bin/evas_cserve2_shm.c new file mode 100644 index 0000000..36291f9 --- /dev/null +++ b/src/bin/evas_cserve2_shm.c @@ -0,0 +1,147 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "evas_cserve2.h" + +#include +#include +#include +#include +#include + +struct _Shm_Mapping +{ + const char *name; + size_t length; + Eina_Inlist *segments; +}; + +typedef struct _Shm_Mapping Shm_Mapping; + +struct _Shm_Handle +{ + EINA_INLIST; + Shm_Mapping *mapping; + off_t map_offset; + off_t image_offset; + size_t map_size; + size_t image_size; +}; + +static int id = 0; + +Shm_Handle * +cserve2_shm_request(size_t size) +{ + Shm_Mapping *map; + Shm_Handle *shm; + char shmname[NAME_MAX]; + size_t map_size; + long pagesize; + int fd; + + map = calloc(1, sizeof(Shm_Mapping)); + if (!map) + { + ERR("Failed to allocate mapping handler."); + return NULL; + } + + do { + snprintf(shmname, sizeof(shmname), "/evas-shm-img-%d", id++); + fd = shm_open(shmname, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + if (fd == -1 && errno != EEXIST) + { + ERR("Failed to create shared memory object '%s': %m", shmname); + free(map); + return NULL; + } + } while (fd == -1); + + pagesize = sysconf(_SC_PAGESIZE); + if (pagesize < 1) + { + ERR("sysconf() reported weird value for PAGESIZE, assuming 4096."); + pagesize = 4096; + } + + map_size = ((size + pagesize - 1) / pagesize) * pagesize; + + if (ftruncate(fd, map_size) == -1) + { + ERR("Failed to set size of shared file: %m"); + close(fd); + free(map); + return NULL; + } + close(fd); + + map->name = eina_stringshare_add(shmname); + map->length = map_size; + + shm = calloc(1, sizeof(Shm_Handle)); + if (!shm) + { + ERR("Failed to allocate shared memory handler."); + eina_stringshare_del(map->name); + free(map); + return NULL; + } + + map->segments = eina_inlist_append(map->segments, EINA_INLIST_GET(shm)); + shm->mapping = map; + shm->map_offset = 0; + shm->image_offset = 0; + + shm->image_size = size; + shm->map_size = map_size; + + return shm; +} + +void +cserve2_shm_unref(Shm_Handle *shm) +{ + Shm_Mapping *map = shm->mapping; + + map->segments = eina_inlist_remove(map->segments, EINA_INLIST_GET(shm)); + free(shm); + + if (map->segments) + return; + + shm_unlink(map->name); + eina_stringshare_del(map->name); + free(map); +} + +const char * +cserve2_shm_name_get(const Shm_Handle *shm) +{ + return shm->mapping->name; +} + +off_t +cserve2_shm_map_offset_get(const Shm_Handle *shm) +{ + return shm->map_offset; +} + +off_t +cserve2_shm_offset_get(const Shm_Handle *shm) +{ + return shm->image_offset; +} + +size_t +cserve2_shm_map_size_get(const Shm_Handle *shm) +{ + return shm->map_size; +} + +size_t +cserve2_shm_size_get(const Shm_Handle *shm) +{ + return shm->image_size; +} diff --git a/src/bin/evas_cserve2_slave.c b/src/bin/evas_cserve2_slave.c new file mode 100644 index 0000000..b6e4610 --- /dev/null +++ b/src/bin/evas_cserve2_slave.c @@ -0,0 +1,480 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "evas_cserve2.h" +#include "evas_cserve2_slave.h" + +static Eina_Hash *loaders = NULL; +static Eina_List *modules = NULL; + +struct ext_loader_s +{ + unsigned int length; + const char *extension; + const char *loader; +}; + +#define MATCHING(Ext, Module) \ + { sizeof (Ext), Ext, Module } + +static const struct ext_loader_s map_loaders[] = +{ /* map extensions to loaders to use for good first-guess tries */ + MATCHING(".png", "png"), + MATCHING(".jpg", "jpeg"), + MATCHING(".jpeg", "jpeg"), + MATCHING(".jfif", "jpeg"), + MATCHING(".eet", "eet"), + MATCHING(".edj", "eet"), + MATCHING(".eap", "eet"), + MATCHING(".edb", "edb"), + MATCHING(".xpm", "xpm"), + MATCHING(".tiff", "tiff"), + MATCHING(".tif", "tiff"), + MATCHING(".svg", "svg"), + MATCHING(".svgz", "svg"), + MATCHING(".svg.gz", "svg"), + MATCHING(".gif", "gif"), + MATCHING(".pbm", "pmaps"), + MATCHING(".pgm", "pmaps"), + MATCHING(".ppm", "pmaps"), + MATCHING(".pnm", "pmaps"), + MATCHING(".bmp", "bmp"), + MATCHING(".tga", "tga"), + MATCHING(".wbmp", "wbmp"), + MATCHING(".ico", "ico"), + MATCHING(".cur", "ico"), + MATCHING(".psd", "psd") +}; + +static const char *loaders_name[] = +{ /* in order of most likely needed */ + "png", "jpeg", "eet", "xpm", "tiff", "gif", "svg", "pmaps", "bmp", "tga", "wbmp", "ico", "psd", "edb" +}; + +Eina_Bool +evas_cserve2_loader_register(Evas_Loader_Module_Api *api) +{ + eina_hash_direct_add(loaders, api->type, api); + return EINA_TRUE; +} + +#if defined(__CEGCC__) || defined(__MINGW32CE__) +# define EVAS_MODULE_NAME_IMAGE_LOADER "loader_%s.dll" +#elif _WIN32 +# define EVAS_MODULE_NAME_IMAGE_LOADER "module.dll" +#else +# define EVAS_MODULE_NAME_IMAGE_LOADER "module.so" +#endif + +static Evas_Loader_Module_Api * +loader_module_find(const char *type) +{ + Evas_Loader_Module_Api *l; + Eina_Module *em; + char buf[PATH_MAX]; + + l = eina_hash_find(loaders, type); + if (l) return l; + + /* FIXME: Look in every possible path, but what will those be? */ + snprintf(buf, sizeof(buf), PACKAGE_LIB_DIR "/evas/cserve2/loaders/%s/%s/%s", + type, MODULE_ARCH, EVAS_MODULE_NAME_IMAGE_LOADER); + + em = eina_module_new(buf); + if (!em) return NULL; + + if (!eina_module_load(em)) + { + eina_module_free(em); + return NULL; + } + + l = eina_hash_find(loaders, type); + if (l) + { + modules = eina_list_append(modules, em); + return l; + } + + eina_module_free(em); + + return NULL; +} + +static Eina_Bool +command_read(int fd, Slave_Command *cmd, void **params) +{ + ssize_t ret; + int ints[2], size, got = 0; + char *buf; + + ret = read(fd, ints, sizeof(int) * 2); + if (ret < (int)sizeof(int) * 2) + return EINA_FALSE; + + size = ints[0]; + buf = malloc(size); + if (!buf) return EINA_FALSE; + + do { + ret = read(fd, buf + got, size - got); + if (ret < 0) + { + /* EINTR means we were interrupted by a signal before anything + * was sent, and if we are back here it means that signal was + * not meant for us to die. Any other error here is fatal and + * should result in the slave terminating. + */ + if (errno == EINTR) + continue; + free(buf); + return EINA_FALSE; + } + got += ret; + } while (got < size); + + *cmd = ints[1]; + *params = buf; + + return EINA_TRUE; +} + +static Eina_Bool +response_send(int fd, Slave_Command cmd, void *resp, int size) +{ + int sent = 0, ints[2]; + const char *data = resp; + ssize_t ret; + + ints[0] = size; + ints[1] = cmd; + ret = write(fd, ints, sizeof(int) * 2); + if (ret < 0) + return EINA_FALSE; + if (!size) + return EINA_TRUE; + do { + ret = write(fd, data + sent, size - sent); + if (ret < 0) + { + /* EINTR means we were interrupted by a signal before anything + * was sent, and if we are back here it means that signal was + * not meant for us to die. Any other error here is fatal and + * should result in the slave terminating. + */ + if (errno == EINTR) + continue; + return EINA_FALSE; + } + sent += ret; + } while (sent < size); + + return EINA_TRUE; +} + +static Eina_Bool +error_send(int fd, Error_Type err) +{ + return response_send(fd, ERROR, &err, sizeof(Error_Type)); +} + +void * +cserve2_shm_map(const char *name, size_t length, off_t offset) +{ + void *map; + int fd; + + fd = shm_open(name, O_RDWR, S_IWUSR); + if (fd == -1) + return MAP_FAILED; + + map = mmap(NULL, length, PROT_WRITE, MAP_SHARED, fd, offset); + + close(fd); + + return map; +} + +void +cserve2_shm_unmap(void *map, size_t length) +{ + munmap(map, length); +} + +static Error_Type +image_open(const char *file, const char *key, Image_Load_Opts *opts, Slave_Msg_Image_Opened *result, const char **use_loader) +{ + Evas_Img_Load_Params ilp; + Evas_Loader_Module_Api *api; + const char *loader = NULL, *end; + unsigned int i; + int len; + int err; + + memset(&ilp, 0, sizeof(ilp)); + + if (opts) + { +#define SETOPT(v) ilp.opts.v = opts->v + SETOPT(w); + SETOPT(h); + SETOPT(rx); + SETOPT(ry); + SETOPT(rw); + SETOPT(rh); + SETOPT(scale_down_by); + SETOPT(dpi); + SETOPT(orientation); +#undef SETOPT + ilp.has_opts = EINA_TRUE; + } + + if (!*use_loader) + goto try_extension; + + loader = *use_loader; + api = loader_module_find(loader); + if (!api) + goto try_extension; + + if (api->head_load(&ilp, file, key, &err)) + goto done; + +try_extension: + len = strlen(file); + end = file + len; + for (i = 0; i < (sizeof (map_loaders) / sizeof(struct ext_loader_s)); i++) + { + int len2 = strlen(map_loaders[i].extension); + if (len2 > len) continue; + if (!strcasecmp(end - len2, map_loaders[i].extension)) + { + loader = map_loaders[i].loader; + break; + } + } + + if (!loader) + goto try_all_known; + + api = loader_module_find(loader); + if (!api) + goto try_all_known; + + if (api->head_load(&ilp, file, key, &err)) + goto done; + +try_all_known: + for (i = 0; i < (sizeof(loaders_name) / sizeof(loaders_name[0])); i++) + { + loader = loaders_name[i]; + api = loader_module_find(loader); + if (!api) + continue; + if (api->head_load(&ilp, file, key, &err)) + goto done; + } + + /* find every module available and try them, even if we don't know they + * exist. That will be our generic loader */ + + return err; + +done: + *use_loader = loader; + + result->w = ilp.w; + result->h = ilp.h; + if ((result->rotated = ilp.rotated)) + { + result->degree = ilp.degree; + } + if ((result->animated = ilp.animated)) + { + result->frame_count = ilp.frame_count; + result->loop_count = ilp.loop_count; + result->loop_hint = ilp.loop_hint; + } + result->scale = ilp.scale; + result->alpha = ilp.alpha; + return CSERVE2_NONE; +} + +static Error_Type +image_load(const char *file, const char *key, const char *shmfile, Slave_Msg_Image_Load *params, Slave_Msg_Image_Loaded *result, const char *loader) +{ + Evas_Img_Load_Params ilp; + Evas_Loader_Module_Api *api; + int err; + Error_Type ret = CSERVE2_NONE; + char *map = cserve2_shm_map(shmfile, params->shm.mmap_size, + params->shm.mmap_offset); + if (map == MAP_FAILED) + return CSERVE2_RESOURCE_ALLOCATION_FAILED; + + memset(&ilp, 0, sizeof(ilp)); + + api = loader_module_find(loader); + if (!api) + { + ret = CSERVE2_GENERIC; + goto done; + } + + ilp.w = params->w; + ilp.h = params->h; + ilp.alpha = params->alpha; +#define SETOPT(v) ilp.opts.v = params->opts.v + SETOPT(w); + SETOPT(h); + SETOPT(rx); + SETOPT(ry); + SETOPT(rw); + SETOPT(rh); + SETOPT(scale_down_by); + SETOPT(dpi); + SETOPT(orientation); +#undef SETOPT + + ilp.buffer = map + params->shm.image_offset; + if (!api->data_load(&ilp, file, key, &err)) + ret = err; + + result->alpha_sparse = ilp.alpha_sparse; + +done: + cserve2_shm_unmap(map, params->shm.mmap_size); + + return ret; +} + +static void +handle_image_open(int wfd, void *params) +{ + Slave_Msg_Image_Open *p; + Slave_Msg_Image_Opened result; + Image_Load_Opts *load_opts = NULL; + Error_Type err; + const char *loader = NULL, *file, *key, *ptr; + char *resp; + size_t resp_size; + + p = params; + file = (const char *)(p + sizeof(Slave_Msg_Image_Open)); + key = file + strlen(file) + 1; + ptr = key + strlen(key) + 1; + if (p->has_opts) + { + load_opts = (Image_Load_Opts *)ptr; + ptr += sizeof(Image_Load_Opts); + } + if (p->has_loader_data) + loader = ptr; + + memset(&result, 0, sizeof(result)); + if ((err = image_open(file, key, load_opts, &result, &loader)) + != CSERVE2_NONE) + { + error_send(wfd, err); + return; + } + + result.has_loader_data = EINA_TRUE; + + resp_size = sizeof(Slave_Msg_Image_Opened) + sizeof(int) + strlen(loader) + 1; + resp = alloca(resp_size); + memcpy(resp, &result, sizeof(Slave_Msg_Image_Opened)); + memcpy(resp + sizeof(Slave_Msg_Image_Opened), loader, strlen(loader) + 1); + response_send(wfd, IMAGE_OPEN, resp, resp_size); +} + +static void +handle_image_load(int wfd, void *params) +{ + Slave_Msg_Image_Load *load_args = params; + Slave_Msg_Image_Loaded resp; + Error_Type err; + const char *shmfile; + const char *file, *key, *loader; + + if (!load_args->has_loader_data) + { + error_send(wfd, CSERVE2_UNKNOWN_FORMAT); + return; + } + + memset(&resp, 0, sizeof(resp)); + + shmfile = ((const char *)params) + sizeof(Slave_Msg_Image_Load); + file = shmfile + strlen(shmfile) + 1; + key = file + strlen(file) + 1; + loader = key + strlen(key) + 1; + if ((err = image_load(file, key, shmfile, load_args, &resp, loader)) + != CSERVE2_NONE) + { + error_send(wfd, err); + return; + } + + response_send(wfd, IMAGE_LOAD, &resp, sizeof(resp)); +} + +int main(int c, char **v) +{ + int wfd, rfd; + Slave_Command cmd; + void *params = NULL; + Eina_Module *m; + Eina_Bool quit = EINA_FALSE; + + if (c < 3) + return 1; + + eina_init(); + + loaders = eina_hash_string_superfast_new(NULL); + + wfd = atoi(v[1]); + rfd = atoi(v[2]); + + while (!quit) + { + if (!command_read(rfd, &cmd, ¶ms)) + { + error_send(wfd, CSERVE2_INVALID_COMMAND); + return 1; + } + switch (cmd) + { + case IMAGE_OPEN: + handle_image_open(wfd, params); + break; + case IMAGE_LOAD: + handle_image_load(wfd, params); + break; + case SLAVE_QUIT: + quit = EINA_TRUE; + break; + default: + error_send(wfd, CSERVE2_INVALID_COMMAND); + } + } + + eina_hash_free(loaders); + + EINA_LIST_FREE(modules, m) + eina_module_free(m); + + eina_shutdown(); + + return 0; +} diff --git a/src/bin/evas_cserve2_slave.h b/src/bin/evas_cserve2_slave.h new file mode 100644 index 0000000..a491b45 --- /dev/null +++ b/src/bin/evas_cserve2_slave.h @@ -0,0 +1,86 @@ +#ifndef _EVAS_CSERVE2_SLAVE_H +#define _EVAS_CSERVE2_SLAVE_H + +#include + +/* begin bunch of stuff from evas_common.h so that we don't need to drag + * a lot of useless @SOMETHING_CFLAGS@ around */ +typedef unsigned long long DATA64; +typedef unsigned int DATA32; +typedef unsigned short DATA16; +typedef unsigned char DATA8; + +#ifndef WORDS_BIGENDIAN +/* x86 */ +#define A_VAL(p) (((DATA8 *)(p))[3]) +#define R_VAL(p) (((DATA8 *)(p))[2]) +#define G_VAL(p) (((DATA8 *)(p))[1]) +#define B_VAL(p) (((DATA8 *)(p))[0]) +#define AR_VAL(p) ((DATA16 *)(p)[1]) +#define GB_VAL(p) ((DATA16 *)(p)[0]) +#else +/* ppc */ +#define A_VAL(p) (((DATA8 *)(p))[0]) +#define R_VAL(p) (((DATA8 *)(p))[1]) +#define G_VAL(p) (((DATA8 *)(p))[2]) +#define B_VAL(p) (((DATA8 *)(p))[3]) +#define AR_VAL(p) ((DATA16 *)(p)[0]) +#define GB_VAL(p) ((DATA16 *)(p)[1]) +#endif + +/* if more than 1/ALPHA_SPARSE_INV_FRACTION is "alpha" (1-254) then sparse + * alpha flag gets set */ +#define ALPHA_SPARSE_INV_FRACTION 3 + +#define IMG_MAX_SIZE 65000 + +#define IMG_TOO_BIG(w, h) \ + ((((unsigned long long)w) * ((unsigned long long)h)) >= \ + ((1ULL << (29 * (sizeof(void *) / 4))) - 2048)) + +#define RGB_JOIN(r,g,b) \ + (((r) << 16) + ((g) << 8) + (b)) + +#define ARGB_JOIN(a,r,g,b) \ + (((a) << 24) + ((r) << 16) + ((g) << 8) + (b)) +/* end bunchf of stuff from evas_common.h */ + +typedef struct _Evas_Loader_Module_Api Evas_Loader_Module_Api; +typedef struct _Evas_Img_Load_Params Evas_Img_Load_Params; + +#define EVAS_CSERVE2_MODULE_API_VERSION 1 +struct _Evas_Loader_Module_Api { + int version; + const char *type; + Eina_Bool (*head_load)(Evas_Img_Load_Params *p, const char *file, const char *key, int *error); + Eina_Bool (*data_load)(Evas_Img_Load_Params *p, const char *file, const char *key, int *error); +}; + +struct _Evas_Img_Load_Params { + unsigned int w, h; + unsigned int degree; + unsigned int scale; + int frame_count; + int loop_count; + int loop_hint; + struct { + unsigned int w, h; + unsigned int rx, ry, rw, rh; + int scale_down_by; + double dpi; + Eina_Bool orientation; + } opts; + void *buffer; + Eina_Bool has_opts : 1; + Eina_Bool rotated : 1; + Eina_Bool alpha : 1; + Eina_Bool alpha_sparse : 1; + Eina_Bool animated : 1; +}; + +EAPI Eina_Bool evas_cserve2_loader_register(Evas_Loader_Module_Api *api); + +EAPI void evas_cserve2_image_premul(Evas_Img_Load_Params *ilp); +EAPI void evas_cserve2_image_alpha_sparse_set(Evas_Img_Load_Params *ilp); + +#endif /* _EVAS_CSERVE2_SLAVE_H */ diff --git a/src/bin/evas_cserve2_slaves.c b/src/bin/evas_cserve2_slaves.c new file mode 100644 index 0000000..452df0d --- /dev/null +++ b/src/bin/evas_cserve2_slaves.c @@ -0,0 +1,395 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "evas_cserve2.h" + +#include +#include +#include +#include +#include + +struct _Slave_Proc +{ + pid_t pid; + const char *name; + int write_fd; + int read_fd; + Slave_Read_Cb read_cb; + Slave_Dead_Cb dead_cb; + const void *data; + Eina_Binbuf *pending; + + struct { + int size; + int read_size; + Slave_Command cmd; + char *buf; + } read; + + Eina_Bool killed : 1; +}; + +static Eina_List *slaves; + +static Slave_Proc * +_child_find(pid_t pid) +{ + Eina_List *l; + Slave_Proc *s; + + EINA_LIST_FOREACH(slaves, l, s) + if (s->pid == pid) + return s; + return NULL; +} + +static void +_slave_free(Slave_Proc *s) +{ + if (s->write_fd) + close(s->write_fd); + if (s->read_fd) + { + cserve2_fd_watch_del(s->read_fd); + close(s->read_fd); + } + + free(s->read.buf); + + if (s->pending) + eina_binbuf_free(s->pending); + + if (s->dead_cb) + s->dead_cb(s, (void *)s->data); + + eina_stringshare_del(s->name); + + free(s); +} + +static void +_slave_dead_cb(int pid, int status __UNUSED__) +{ + Slave_Proc *s; + + DBG("Child dead with pid '%d'.", pid); + s = _child_find(pid); + if (!s) + { + ERR("Unknown child dead '%d'.", pid); + return; + } + + slaves = eina_list_remove(slaves, s); + _slave_free(s); +} + +static size_t +_slave_write(Slave_Proc *s, const char *data, size_t size) +{ + size_t sent = 0; + + do + { + ssize_t ret; + ret = write(s->write_fd, data + sent, size - sent); + if (ret == -1) + { + if (errno == EAGAIN) + break; + if (errno == EPIPE) + { + WRN("Slave unexpectedly gone."); + /* handle dead? */ + break; + } + } + sent += ret; + } while(sent < size); + + return sent; +} + +static void +_slave_write_cb(int fd __UNUSED__, Fd_Flags flags __UNUSED__, void *data) +{ + Slave_Proc *s = data; + size_t sent; + size_t size; + const char *str; + + size = eina_binbuf_length_get(s->pending); + str = (const char *)eina_binbuf_string_get(s->pending); + sent = _slave_write(s, str, size); + if (sent == size) + { + eina_binbuf_free(s->pending); + s->pending = NULL; + cserve2_fd_watch_del(s->write_fd); + return; + } + + eina_binbuf_remove(s->pending, 0, sent); +} + +static void +_slave_read_clear(Slave_Proc *s) +{ + s->read.buf = NULL; + s->read.cmd = 0; + s->read.read_size = s->read.size = 0; +} + +static void +_slave_read_cb(int fd, Fd_Flags flags, void *data) +{ + Slave_Proc *s = data; + Eina_Bool done = EINA_FALSE; + + /* handle error */ + if (!(flags & FD_READ)) + return; + + if (!s->read.size) + { + int ints[2]; + ssize_t ret; + + ret = read(fd, ints, sizeof(int) * 2); + if (ret < (int)sizeof(int) * 2) + { + return; + } + s->read.size = ints[0]; + s->read.cmd = ints[1]; + if (s->read.size) + s->read.buf = malloc(s->read.size); + else + done = EINA_TRUE; + } + + if (s->read.buf) + { + ssize_t ret; + do { + char *p = s->read.buf + s->read.read_size; + int sz = s->read.size - s->read.read_size; + ret = read(fd, p, sz); + if (ret < 0) + { + if (errno == EAGAIN) + break; + } + s->read.read_size += ret; + } while(s->read.read_size < s->read.size); + + if (s->read.read_size == s->read.size) + done = EINA_TRUE; + } + + if (done) + { + s->read_cb(s, s->read.cmd, s->read.buf, (void *)s->data); + _slave_read_clear(s); + } +} + +Eina_Bool +cserve2_slaves_init(void) +{ + cserve2_on_child_dead_set(_slave_dead_cb); + return EINA_TRUE; +} + +void +cserve2_slaves_shutdown(void) +{ + Slave_Proc *s; + + cserve2_on_child_dead_set(NULL); + + if (!slaves) + return; + + DBG("Shutting down slaves subsystem with %d slaves alive!", + eina_list_count(slaves)); + + EINA_LIST_FREE(slaves, s) + { + kill(s->pid, SIGKILL); + _slave_free(s); + } +} + +static const char * +_slave_path_get(const char *name) +{ + char buf[PATH_MAX], cwd[PATH_MAX]; + struct stat st; + + if (name[0] == '/') + { + if (access(name, X_OK)) + return NULL; + return eina_stringshare_add(name); + } + + getcwd(cwd, sizeof(cwd)); + snprintf(buf, sizeof(buf), "%s/%s", cwd, name); + if (!access(buf, X_OK)) + return eina_stringshare_add(buf); + + snprintf(buf, sizeof(buf), PACKAGE_LIBEXEC_DIR"/%s", name); + if (!access(buf, X_OK)) + return eina_stringshare_add(buf); + + return NULL; +} + +Slave_Proc * +cserve2_slave_run(const char *exe, Slave_Read_Cb read_cb, Slave_Dead_Cb dead_cb, const void *data) +{ + Slave_Proc *s; + pid_t pid; + int child[2], parent[2]; + int flags; + const char *name; + + name = _slave_path_get(exe); + if (!name) + { + ERR("Cannot execute slave '%s'. Not found or not executable.", exe); + return NULL; + } + DBG("Running slave '%s', resolved to path: %s", exe, name); + + s = calloc(1, sizeof(Slave_Proc)); + if (!s) + { + ERR("Could not create Slave_Proc handler."); + eina_stringshare_del(name); + return NULL; + } + + if (pipe(child)) + { + ERR("Could not create pipes for child."); + eina_stringshare_del(name); + free(s); + return NULL; + } + + if (pipe(parent)) + { + ERR("Could not create pipes for parent."); + eina_stringshare_del(name); + free(s); + close(child[0]); + close(child[1]); + return NULL; + } + + pid = fork(); + if (pid < 0) + { + ERR("Could not create sub process."); + eina_stringshare_del(name); + close(child[0]); + close(child[1]); + close(parent[0]); + close(parent[1]); + free(s); + return NULL; + } + + if (!pid) + { + char *args[4], readfd[12], writefd[12]; + + close(child[1]); + close(parent[0]); + + sprintf(readfd, "%d", child[0]); + sprintf(writefd, "%d", parent[1]); + args[0] = (char *)name; + args[1] = writefd; + args[2] = readfd; + args[3] = NULL; + execvp(name, args); + /* we only get here if execvp fails, which should not + * happen and if it does, it's baaaaaaaaad */ + ERR("execvp() for slave at: '%s' failed! '%m'", name); + exit(1); + } + + s->pid = pid; + s->name = name; + s->write_fd = child[1]; + flags = fcntl(s->write_fd, F_GETFL); + flags |= O_NONBLOCK; + fcntl(s->write_fd, F_SETFL, flags); + s->read_fd = parent[0]; + flags = fcntl(s->read_fd, F_GETFL); + flags |= O_NONBLOCK; + fcntl(s->read_fd, F_SETFL, flags); + s->read_cb = read_cb; + s->dead_cb = dead_cb; + s->data = data; + cserve2_fd_watch_add(s->read_fd, FD_READ, _slave_read_cb, s); + + close(child[0]); + close(parent[1]); + + slaves = eina_list_append(slaves, s); + + return s; +} + +static void +_slave_send_aux(Slave_Proc *s, const char *data, size_t size) +{ + size_t sent; + + if (s->pending) + { + eina_binbuf_append_length(s->pending, (unsigned char *)data, size); + return; + } + + sent = _slave_write(s, data, size); + if (sent < size) + { + s->pending = eina_binbuf_new(); + eina_binbuf_append_length(s->pending, (unsigned char *)data + sent, + size - sent); + cserve2_fd_watch_add(s->write_fd, FD_WRITE, _slave_write_cb, s); + } +} + +void +cserve2_slave_send(Slave_Proc *s, Slave_Command cmd, const char *data, size_t size) +{ + int ints[2]; + + ints[0] = size; + ints[1] = cmd; + _slave_send_aux(s, (char *)ints, sizeof(int) * 2); + if (size) + _slave_send_aux(s, (char *)data, size); +} + +void +cserve2_slave_kill(Slave_Proc *s) +{ + if (s->killed) + { + if (!kill(s->pid, 0)) + DBG("Slave %p(%d) requested to kill, but it's still alive.", + s, s->pid); + } + + s->killed = EINA_TRUE; + kill(s->pid, SIGTERM); +} diff --git a/src/bin/evas_cserve2_utils.c b/src/bin/evas_cserve2_utils.c new file mode 100644 index 0000000..68fa216 --- /dev/null +++ b/src/bin/evas_cserve2_utils.c @@ -0,0 +1,58 @@ +#include "evas_cserve2_slave.h" + +static unsigned int +evas_cserve2_convert_argb_premul(unsigned int *data, unsigned int len) +{ + unsigned int *de = data + len; + unsigned int nas = 0; + + while (data < de) + { + unsigned int a = 1 + (*data >> 24); + + *data = (*data & 0xff000000) + + (((((*data) >> 8) & 0xff) * a) & 0xff00) + + (((((*data) & 0x00ff00ff) * a) >> 8) & 0x00ff00ff); + data++; + + if ((a == 1) || (a == 256)) + nas++; + } + + return nas; +} + +EAPI void +evas_cserve2_image_premul(Evas_Img_Load_Params *ilp) +{ + unsigned int nas; + + if (!ilp->alpha) return; + + nas = evas_cserve2_convert_argb_premul(ilp->buffer, ilp->w * ilp->h); + if ((ALPHA_SPARSE_INV_FRACTION * nas) >= (ilp->w * ilp->h)) + ilp->alpha_sparse = EINA_TRUE; +} + +EAPI void +evas_cserve2_imave_alpha_sparse_set(Evas_Img_Load_Params *ilp) +{ + unsigned int *s, *se; + unsigned int nas = 0; + unsigned int len = ilp->w * ilp->h; + + if (!ilp->alpha) return; + + s = ilp->buffer; + se = s + len; + while (s < se) + { + unsigned int p = *s & 0xff000000; + + if (!p || (p == 0xff000000)) + nas++; + s++; + } + if ((ALPHA_SPARSE_INV_FRACTION * nas) >= len) + ilp->alpha_sparse = EINA_TRUE; +} diff --git a/src/bin/loaders/Makefile.am b/src/bin/loaders/Makefile.am new file mode 100644 index 0000000..46222c8 --- /dev/null +++ b/src/bin/loaders/Makefile.am @@ -0,0 +1,47 @@ +MAINTAINERCLEANFILES = Makefile.in + +SUBDIRS = + +if BUILD_LOADER_BMP +SUBDIRS += bmp +endif + +if BUILD_LOADER_EET +SUBDIRS += eet +endif + +if BUILD_LOADER_ICO +SUBDIRS += ico +endif + +if BUILD_LOADER_JPEG +SUBDIRS += jpeg +endif + +if BUILD_LOADER_PMAPS +SUBDIRS += pmaps +endif + +if BUILD_LOADER_PNG +SUBDIRS += png +endif + +if BUILD_LOADER_PSD +SUBDIRS += psd +endif + +if BUILD_LOADER_TGA +SUBDIRS += tga +endif + +if BUILD_LOADER_TIFF +SUBDIRS += tiff +endif + +if BUILD_LOADER_WBMP +SUBDIRS += wbmp +endif + +if BUILD_LOADER_XPM +SUBDIRS += xpm +endif diff --git a/src/bin/loaders/bmp/Makefile.am b/src/bin/loaders/bmp/Makefile.am new file mode 100644 index 0000000..113c19b --- /dev/null +++ b/src/bin/loaders/bmp/Makefile.am @@ -0,0 +1,32 @@ + +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +-I. \ +-I$(top_srcdir)/src/lib \ +-I$(top_srcdir)/src/lib/include \ +-I$(top_srcdir)/src/lib/cserve2 \ +-I$(top_srcdir)/src/bin \ +@EINA_CFLAGS@ \ +@EVIL_CFLAGS@ + +if BUILD_LOADER_BMP +#if !EVAS_STATIC_BUILD_BMP + +pkgdir = $(libdir)/evas/cserve2/loaders/bmp/$(MODULE_ARCH) +pkg_LTLIBRARIES = module.la + +module_la_SOURCES = evas_image_load_bmp.c + +module_la_LIBADD = @EINA_LIBS@ @EVIL_LIBS@ -lm +module_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -module -avoid-version +module_la_LIBTOOLFLAGS = --tag=disable-static + +#else + +#noinst_LTLIBRARIES = libevas_loader_bmp.la +#libevas_loader_bmp_la_SOURCES = evas_image_load_bmp.c +#libevas_loader_bmp_la_LIBADD = + +#endif +endif diff --git a/src/bin/loaders/bmp/evas_image_load_bmp.c b/src/bin/loaders/bmp/evas_image_load_bmp.c new file mode 100644 index 0000000..4a00547 --- /dev/null +++ b/src/bin/loaders/bmp/evas_image_load_bmp.c @@ -0,0 +1,1479 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#ifdef HAVE_EVIL +# include +#endif + +#include + +#include "evas_macros.h" + +#include "evas_cserve2.h" +#include "evas_cserve2_slave.h" + +static Eina_Bool +read_short(unsigned char *map, size_t length, size_t *position, short *ret) +{ + unsigned char b[2]; + + if (*position + 2 > length) return EINA_FALSE; + b[0] = map[(*position)++]; + b[1] = map[(*position)++]; + *ret = (b[1] << 8) | b[0]; + return EINA_TRUE; +} + +static Eina_Bool +read_ushort(unsigned char *map, size_t length, size_t *position, unsigned short *ret) +{ + unsigned char b[2]; + + if (*position + 2 > length) return EINA_FALSE; + b[0] = map[(*position)++]; + b[1] = map[(*position)++]; + *ret = (b[1] << 8) | b[0]; + return EINA_TRUE; +} + +static Eina_Bool +read_int(unsigned char *map, size_t length, size_t *position, int *ret) +{ + unsigned char b[4]; + int i; + + if (*position + 4 > length) return EINA_FALSE; + for (i = 0; i < 4; i++) + b[i] = map[(*position)++]; + *ret = ARGB_JOIN(b[3], b[2], b[1], b[0]); + return EINA_TRUE; +} + +static Eina_Bool +read_uint(unsigned char *map, size_t length, size_t *position, unsigned int *ret) +{ + unsigned char b[4]; + int i; + + if (*position + 4 > length) return EINA_FALSE; + for (i = 0; i < 4; i++) + b[i] = map[(*position)++]; + *ret = ARGB_JOIN(b[3], b[2], b[1], b[0]); + return EINA_TRUE; +} + +static Eina_Bool +read_uchar(unsigned char *map, size_t length, size_t *position, unsigned char *ret) +{ + if (*position + 1 > length) return EINA_FALSE; + *ret = map[(*position)++]; + return EINA_TRUE; +} + +static Eina_Bool +read_skip(size_t length, size_t *position, int skip) +{ + if (*position + skip > length) return EINA_FALSE; + *position += skip; + return EINA_TRUE; +} + +static Eina_Bool +read_mem(unsigned char *map, size_t length, size_t *position, void *buffer, int size) +{ + if (*position + size > length) return EINA_FALSE; + memcpy(buffer, map + *position, size); + *position += size; + return EINA_TRUE; +} + +static Eina_Bool +evas_image_load_file_head_bmp(Evas_Img_Load_Params *ilp, const char *file, const char *key __UNUSED__, int *error) +{ + Eina_File *f; + void *map = NULL; + size_t position = 0; + char hasa = 0; + int w = 0, h = 0, bit_count = 0, image_size = 0, comp = 0; + unsigned int offset, head_size, amask = 0; + int fsize = 0; + unsigned int bmpsize; + unsigned short res1, res2; + + f = eina_file_open(file, 0); + if (!f) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return EINA_FALSE; + } + + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + fsize = eina_file_size_get(f); + if (fsize < 2) goto close_file; + + map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + if (!map) goto close_file; + + if (strncmp(map, "BM", 2)) goto close_file; // magic number + position += 2; + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + if (!read_uint(map, fsize, &position, &bmpsize)) goto close_file; + if (!read_ushort(map, fsize, &position, &res1)) goto close_file; + if (!read_ushort(map, fsize, &position, &res2)) goto close_file; + if (!read_uint(map, fsize, &position, &offset)) goto close_file; + if (!read_uint(map, fsize, &position, &head_size)) goto close_file; + if (head_size == 12) // OS/2 V1 + Windows 3.0 + { + short tmp; + + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + w = tmp; // width + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + h = tmp; // height + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + //planes = tmp; // must be 1 + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + bit_count = tmp; // bits per pixel: 1, 4, 8 & 24 + } + else if (head_size == 64) // OS/2 V2 + { + short tmp; + int tmp2; + + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + w = tmp2; // width + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + h = tmp2; // height + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + //planes = tmp; // must be 1 + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + bit_count = tmp; // bits per pixel: 1, 4, 8, 16, 24 & 32 + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + comp = tmp2; // compression method + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + image_size = tmp2; // bitmap data size + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //hdpi = (tmp2 * 254) / 10000; // horizontal pixels/meter + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //vdpi = (tmp2 * 254) / 10000; // vertical pixles/meter + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //palette_size = tmp2; // number of palette colors power (2^n - so 0 - 8) + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //important_colors = tmp2; // number of important colors - 0 if all + if (!read_skip(fsize, &position, 24)) goto close_file; // skip unused header + if (image_size == 0) image_size = fsize - offset; + } + else if (head_size == 40) // Windows 3.0 + (v3) + { + short tmp; + int tmp2; + + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + w = tmp2; // width + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + h = tmp2; // height + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + //planes = tmp; // must be 1 + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + bit_count = tmp; // bits per pixel: 1, 4, 8, 16, 24 & 32 + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + comp = tmp2; // compression method + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + image_size = tmp2; // bitmap data size + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //hdpi = (tmp2 * 254) / 10000; // horizontal pixels/meter + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //vdpi = (tmp2 * 254) / 10000; // vertical pixles/meter + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //palette_size = tmp2; // number of palette colors power (2^n - so 0 - 8) + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //important_colors = tmp2; // number of important colors - 0 if all + if (image_size == 0) image_size = fsize - offset; + if ((comp == 0) && (bit_count == 32)) hasa = 1; // GIMP seems to store it this way + } + else if (head_size == 108) // Windows 95/NT4 + (v4) + { + short tmp; + int tmp2; + + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + w = tmp2; // width + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + h = tmp2; // height + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + //planes = tmp; // must be 1 + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + bit_count = tmp; // bits per pixel: 1, 4, 8, 16, 24 & 32 + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + comp = tmp2; // compression method + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + image_size = tmp2; // bitmap data size + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //hdpi = (tmp2 * 254) / 10000; // horizontal pixels/meter + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //vdpi = (tmp2 * 254) / 10000; // vertical pixles/meter + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //palette_size = tmp2; // number of palette colors power (2^n - so 0 - 8) + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //important_colors = tmp2; // number of important colors - 0 if all + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //rmask = tmp2; // red mask + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //gmask = tmp2; // green mask + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //bmask = tmp2; // blue mask + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + amask = tmp2; // alpha mask + if (!read_skip(fsize, &position, 36)) goto close_file; // skip unused cie + if (!read_skip(fsize, &position, 12)) goto close_file; // skip unused gamma + if (image_size == 0) image_size = fsize - offset; + if ((amask) && (bit_count == 32)) hasa = 1; + } + else if (head_size == 124) // Windows 98/2000 + (v5) + { + short tmp; + int tmp2; + + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + w = tmp2; // width + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + h = tmp2; // height + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + //planes = tmp; // must be 1 + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + bit_count = tmp; // bits per pixel: 1, 4, 8, 16, 24 & 32 + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + comp = tmp2; // compression method + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //image_size = tmp2; // bitmap data size + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //hdpi = (tmp2 * 254) / 10000; // horizontal pixels/meter + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //vdpi = (tmp2 * 254) / 10000; // vertical pixles/meter + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //palette_size = tmp2; // number of palette colors power (2^n - so 0 - 8) + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //important_colors = tmp2; // number of important colors - 0 if all + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //rmask = tmp2; // red mask + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //gmask = tmp2; // green mask + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //bmask = tmp2; // blue mask + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + amask = tmp2; // alpha mask + if (!read_skip(fsize, &position, 36)) goto close_file; // skip unused cie + if (!read_skip(fsize, &position, 12)) goto close_file; // skip unused gamma + if (!read_skip(fsize, &position, 16)) goto close_file; // skip others + if (image_size == 0) image_size = fsize - offset; + if ((amask) && (bit_count == 32)) hasa = 1; + } + else + goto close_file; + + if (h < 0) + { + h = -h; + //right_way_up = 1; + } + + if ((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE) || + IMG_TOO_BIG(w, h)) + { + if (IMG_TOO_BIG(w, h)) + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + else + *error = EVAS_LOAD_ERROR_GENERIC; + goto close_file; + } + /* It is not bad idea that bmp loader support scale down decoding + * because of memory issue in mobile world.*/ + if (ilp->opts.scale_down_by > 1) + { + w /= ilp->opts.scale_down_by; + h /= ilp->opts.scale_down_by; + } + + if (bit_count < 16) + { + //if ((palette_size < 0) || (palette_size > 256)) pal_num = 256; + //else pal_num = palette_size; + if (bit_count == 1) + { + if (comp == 0) // no compression + { + } + else + goto close_file; + } + else if (bit_count == 4) + { + if (comp == 0) // no compression + { + } + else if (comp == 2) // rle 4bit/pixel + { + } + else + goto close_file; + } + else if (bit_count == 8) + { + if (comp == 0) // no compression + { + } + else if (comp == 1) // rle 8bit/pixel + { + } + else + goto close_file; + } + } + else if ((bit_count == 16) || (bit_count == 24) || (bit_count == 32)) + { + if (comp == 0) // no compression + { + // handled + } + else if (comp == 3) // bit field + { + // handled + } + else if (comp == 4) // jpeg - only printer drivers + goto close_file; + else if (comp == 3) // png - only printer drivers + goto close_file; + else + goto close_file; + } + else + goto close_file; + + ilp->w = w; + ilp->h = h; + if (hasa) ilp->alpha = 1; + + eina_file_map_free(f, map); + eina_file_close(f); + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; + + close_file: + if (map) eina_file_map_free(f, map); + eina_file_close(f); + return EINA_FALSE; +} + +static Eina_Bool +evas_image_load_file_data_bmp(Evas_Img_Load_Params *ilp, const char *file, const char *key __UNUSED__, int *error) +{ + Eina_File *f; + void *map = NULL; + size_t position = 0; + unsigned char *buffer = NULL, *buffer_end = NULL, *p; + char hasa = 0; + int x = 0, y = 0, w = 0, h = 0, bit_count = 0, image_size = 0, + comp = 0, palette_size = -1; + unsigned int offset = 0, head_size = 0; + unsigned int *pal = NULL, pal_num = 0, *pix = NULL, *surface = NULL, fix, + rmask = 0, gmask = 0, bmask = 0, amask = 0; + int right_way_up = 0; + unsigned char r, g, b, a; + int fsize = 0; + unsigned int bmpsize; + unsigned short res1, res2; + + /* for scale decoding */ + unsigned int *scale_surface = NULL, *scale_pix = NULL; + int scale_ratio = 1, image_w = 0, image_h = 0; + int row_size = 0; /* Row size is rounded up to a multiple of 4bytes */ + int read_line = 0; /* total read line */ + + f = eina_file_open(file, 0); + if (!f) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return EINA_FALSE; + } + + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + fsize = eina_file_size_get(f); + if (fsize < 2) goto close_file; + + map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + if (!map) goto close_file; + + if (strncmp(map, "BM", 2)) goto close_file; // magic number + position += 2; + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + if (!read_uint(map, fsize, &position, &bmpsize)) goto close_file; + if (!read_ushort(map, fsize, &position, &res1)) goto close_file; + if (!read_ushort(map, fsize, &position, &res2)) goto close_file; + if (!read_uint(map, fsize, &position, &offset)) goto close_file; + if (!read_uint(map, fsize, &position, &head_size)) goto close_file; + image_size = fsize - offset; + if (image_size < 1) goto close_file; + + if (head_size == 12) // OS/2 V1 + Windows 3.0 + { + short tmp; + + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + w = tmp; // width + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + h = tmp; // height + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + //planes = tmp; // must be 1 + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + bit_count = tmp; // bits per pixel: 1, 4, 8 & 24 + } + else if (head_size == 64) // OS/2 V2 + { + short tmp; + int tmp2; + + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + w = tmp2; // width + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + h = tmp2; // height + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + //planes = tmp; // must be 1 + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + bit_count = tmp; // bits per pixel: 1, 4, 8, 16, 24 & 32 + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + comp = tmp2; // compression method + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + image_size = tmp2; // bitmap data size + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //hdpi = (tmp2 * 254) / 10000; // horizontal pixels/meter + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //vdpi = (tmp2 * 254) / 10000; // vertical pixles/meter + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + palette_size = tmp2; // number of palette colors power (2^n - so 0 - 8) + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //important_colors = tmp2; // number of important colors - 0 if all + if (!read_skip(fsize, &position, 24)) goto close_file; // skip unused header + if (image_size == 0) image_size = fsize - offset; + } + else if (head_size == 40) // Windows 3.0 + (v3) + { + short tmp; + int tmp2; + + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + w = tmp2; // width + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + h = tmp2; // height + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + //planes = tmp; // must be 1 + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + bit_count = tmp; // bits per pixel: 1, 4, 8, 16, 24 & 32 + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + comp = tmp2; // compression method + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + image_size = tmp2; // bitmap data size + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //hdpi = (tmp2 * 254) / 10000; // horizontal pixels/meter + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //vdpi = (tmp2 * 254) / 10000; // vertical pixles/meter + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + palette_size = tmp2; // number of palette colors power (2^n - so 0 - 8) + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //important_colors = tmp2; // number of important colors - 0 if all + if (image_size == 0) image_size = fsize - offset; + if ((comp == 0) && (bit_count == 32)) hasa = 1; // GIMP seems to store it this way + } + else if (head_size == 108) // Windows 95/NT4 + (v4) + { + short tmp; + int tmp2; + + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + w = tmp2; // width + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + h = tmp2; // height + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + //planes = tmp; // must be 1 + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + bit_count = tmp; // bits per pixel: 1, 4, 8, 16, 24 & 32 + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + comp = tmp2; // compression method + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + image_size = tmp2; // bitmap data size + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //hdpi = (tmp2 * 254) / 10000; // horizontal pixels/meter + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //vdpi = (tmp2 * 254) / 10000; // vertical pixles/meter + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + palette_size = tmp2; // number of palette colors power (2^n - so 0 - 8) + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //important_colors = tmp2; // number of important colors - 0 if all + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + rmask = tmp2; // red mask + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + gmask = tmp2; // green mask + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + bmask = tmp2; // blue mask + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + amask = tmp2; // alpha mask + if (!read_skip(fsize, &position, 36)) goto close_file; // skip unused cie + if (!read_skip(fsize, &position, 12)) goto close_file; // skip unused gamma + if (image_size == 0) image_size = fsize - offset; + if ((amask) && (bit_count == 32)) hasa = 1; + } + else if (head_size == 124) // Windows 98/2000 + (v5) + { + short tmp; + int tmp2; + + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + w = tmp2; // width + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + h = tmp2; // height + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + //planes = tmp; // must be 1 + if (!read_short(map, fsize, &position, &tmp)) goto close_file; + bit_count = tmp; // bits per pixel: 1, 4, 8, 16, 24 & 32 + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + comp = tmp2; // compression method + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + image_size = tmp2; // bitmap data size + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //hdpi = (tmp2 * 254) / 10000; // horizontal pixels/meter + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //vdpi = (tmp2 * 254) / 10000; // vertical pixles/meter + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + palette_size = tmp2; // number of palette colors power (2^n - so 0 - 8) + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + //important_colors = tmp2; // number of important colors - 0 if all + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + rmask = tmp2; // red mask + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + gmask = tmp2; // green mask + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + bmask = tmp2; // blue mask + if (!read_int(map, fsize, &position, &tmp2)) goto close_file; + amask = tmp2; // alpha mask + if (!read_skip(fsize, &position, 36)) goto close_file; // skip unused cie + if (!read_skip(fsize, &position, 12)) goto close_file; // skip unused gamma + if (!read_skip(fsize, &position, 16)) goto close_file; // skip others + if (image_size == 0) image_size = fsize - offset; + if ((amask) && (bit_count == 32)) hasa = 1; + } + else + goto close_file; + + if (h < 0) + { + h = -h; + right_way_up = 1; + } + if ((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE) || + IMG_TOO_BIG(w, h)) + { + if (IMG_TOO_BIG(w, h)) + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + else + *error = EVAS_LOAD_ERROR_GENERIC; + goto close_file; + } + /* It is not bad idea that bmp loader support scale down decoding + * because of memory issue in mobile world. */ + if (ilp->opts.scale_down_by > 1) + scale_ratio = ilp->opts.scale_down_by; + image_w = w; + image_h = h; + + if (scale_ratio > 1) + { + w /= scale_ratio; + h /= scale_ratio; + + if ((w < 1) || (h < 1) ) + { + *error = EVAS_LOAD_ERROR_GENERIC; + goto close_file; + } + } + + if ((w != (int)ilp->w) || (h != (int)ilp->h)) + { + *error = EVAS_LOAD_ERROR_GENERIC; + goto close_file; + } + surface = ilp->buffer; + if (!surface) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto close_file; + } + + row_size = ceil((double)(image_w * bit_count) / 32) * 4; + + if (bit_count < 16) + { + unsigned int i; + + if (bit_count == 1) + { + if ((palette_size <= 0) || (palette_size > 2)) pal_num = 2; + else pal_num = palette_size; + } + else if (bit_count == 4) + { + if ((palette_size <= 0) || (palette_size > 16)) pal_num = 16; + else pal_num = palette_size; + } + else if (bit_count == 8) + { + if ((palette_size <= 0) || (palette_size > 256)) pal_num = 256; + else pal_num = palette_size; + } + pal = alloca(256 * 4); + for (i = 0; i < pal_num; i++) + { + if (!read_uchar(map, fsize, &position, &b)) goto close_file; + if (!read_uchar(map, fsize, &position, &g)) goto close_file; + if (!read_uchar(map, fsize, &position, &r)) goto close_file; + if ((head_size != 12) /*&& (palette_size != 0)*/) + { // OS/2 V1 doesn't do the pad byte + if (!read_uchar(map, fsize, &position, &a)) goto close_file; + } + a = 0xff; // fillin a as solid for paletted images + pal[i] = ARGB_JOIN(a, r, g, b); + } + position = offset; + + if ((scale_ratio == 1) || (comp !=0)) + buffer = malloc(image_size + 8); // add 8 for padding to avoid checks + else + { + scale_surface = malloc(image_w * sizeof(DATA32)); //for one line decoding + if (!scale_surface) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto close_file; + } + buffer = malloc(row_size); // scale down is usually set because of memory issue, so read line by line + } + + if (!buffer) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto close_file; + } + if ((scale_ratio == 1) || (comp !=0)) + buffer_end = buffer + image_size; + else + buffer_end = buffer + row_size; + p = buffer; + + if ((scale_ratio == 1) || (comp !=0)) + { + if (!read_mem(map, fsize, &position, buffer, image_size)) goto close_file; + } + else + { + if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; + } + + if (bit_count == 1) + { + if (comp == 0) // no compression + { + pix = surface; + + for (y = 0; y < h; y++) + { + if (!right_way_up) pix = surface + ((h - 1 - y) * w); + if (scale_ratio > 1) pix = scale_surface; // one line decoding + + for (x = 0; x < image_w; x++) + { + if ((x & 0x7) == 0x0) + { + *pix = pal[*p >> 7]; + } + else if ((x & 0x7) == 0x1) + { + *pix = pal[(*p >> 6) & 0x1]; + } + else if ((x & 0x7) == 0x2) + { + *pix = pal[(*p >> 5) & 0x1]; + } + else if ((x & 0x7) == 0x3) + { + *pix = pal[(*p >> 4) & 0x1]; + } + else if ((x & 0x7) == 0x4) + { + *pix = pal[(*p >> 3) & 0x1]; + } + else if ((x & 0x7) == 0x5) + { + *pix = pal[(*p >> 2) & 0x1]; + } + else if ((x & 0x7) == 0x6) + { + *pix = pal[(*p >> 1) & 0x1]; + } + else + { + *pix = pal[*p & 0x1]; + p++; + } + if (p >= buffer_end) break; + pix++; + } + + if (scale_ratio > 1) + { + if (!right_way_up) scale_pix = surface + ((h - 1 - y) * w); + else scale_pix = surface + (y * w); + + pix = scale_surface; + for (x = 0; x < w; x++) + { + *scale_pix = *pix; + scale_pix ++; + pix += scale_ratio; + } + read_line += scale_ratio; + if (read_line >= image_h) break; + + position += row_size * (scale_ratio - 1); + if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; + p = buffer; + buffer_end = buffer + row_size; + } + else + { + if ((x & 0x7) != 0) p++; + fix = (int)(((unsigned long)p) & 0x3); + if (fix > 0) p += 4 - fix; // align row read + if (p >= buffer_end) break; + } + } + } + else + goto close_file; + } + else if (bit_count == 4) + { + if (comp == 0) // no compression + { + pix = surface; + for (y = 0; y < h; y++) + { + if (!right_way_up) pix = surface + ((h - 1 - y) * w); + if (scale_ratio > 1) pix = scale_surface; // one line decoding + for (x = 0; x < image_w; x++) + { + if ((x & 0x1) == 0x1) + { + *pix = pal[*p & 0x0f]; + p++; + } + else + { + *pix = pal[*p >> 4]; + } + if (p >= buffer_end) break; + pix++; + } + if (scale_ratio > 1) + { + if (!right_way_up) scale_pix = surface + ((h - 1 - y) * w); + else scale_pix = surface + (y * w); + + pix = scale_surface; + for (x = 0; x < w; x++) + { + *scale_pix = *pix; + scale_pix ++; + pix += scale_ratio; + } + read_line += scale_ratio; + if (read_line >= image_h) break; + + position += row_size * (scale_ratio - 1); + if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; + p = buffer; + buffer_end = buffer + row_size; + } + else + { + if ((x & 0x1) != 0) p++; + fix = (int)(((unsigned long)p) & 0x3); + if (fix > 0) p += 4 - fix; // align row read + if (p >= buffer_end) break; + } + } + } + else if (comp == 2) // rle 4bit/pixel + { + int count = 0, done = 0, wpad; + int scale_x = 0, scale_y = 0; + Eina_Bool scale_down_line = EINA_TRUE; + + pix = surface; + if (!right_way_up) pix = surface + ((h - 1 - y) * w); + wpad = ((image_w + 1) / 2) * 2; + while (p < buffer_end) + { + if (p[0]) + { + if (scale_down_line) + { + if ((x + p[0]) <= wpad) + { + unsigned int col1 = pal[p[1] >> 4]; + unsigned int col2 = pal[p[1] & 0xf]; + + count = p[0] / 2; + while (count > 0) + { + if (x < w) + { + if (((x % scale_ratio) == 0) && (scale_x < w)) + { + *pix = col1; + pix++; + scale_x++; + } + x++; + } + if (x < w) + { + if (((x % scale_ratio) == 0) && (scale_x < w)) + { + *pix = col2; + pix++; + scale_x++; + } + x++; + } + count--; + } + if (p[0] & 0x1) + { + if (((x % scale_ratio) == 0) && (scale_x < w)) + { + *pix = col1; + pix++; + scale_x++; + } + x++; + } + } + } + p += 2; + } + else + { + switch (p[1]) + { + case 0: // EOL + x = 0; + scale_x = 0; + y++; + if ((y % scale_ratio) == 0) + { + scale_y++; + scale_down_line = EINA_TRUE; + if (!right_way_up) + pix = surface + ((h - 1 - scale_y) * w); + else + pix = surface + (scale_y * w); + } + else + scale_down_line = EINA_FALSE; + if (scale_y >= h) + { + p = buffer_end; + } + p += 2; + break; + case 1: // EOB + p = buffer_end; + break; + case 2: // DELTA + x += p[2]; + y += p[3]; + scale_x = x / scale_ratio; + scale_y = y / scale_ratio; + if ((scale_x >= w) || (scale_y >= h)) + { + p = buffer_end; + } + if (!right_way_up) + pix = surface + scale_x + ((h - 1 - scale_y) * w); + else + pix = surface + scale_x + (scale_y * w); + p += 4; + break; + default: + count = p[1]; + if (((p + count) > buffer_end) || + ((x + count) > w)) + { + p = buffer_end; + break; + } + p += 2; + done = count; + count /= 2; + while (count > 0) + { + if (((x % scale_ratio) == 0) && (scale_x < w)) + { + *pix = pal[*p >> 4]; + pix++; + scale_x++; + } + x++; + if (((x % scale_ratio) == 0) && (scale_x < w)) + { + *pix = pal[*p & 0xf]; + pix++; + scale_x++; + } + x++; + + p++; + count--; + } + + if (done & 0x1) + { + if (((x % scale_ratio) == 0) && (scale_x < w)) + { + *pix = pal[*p >> 4]; + scale_x++; + } + x++; + p++; + } + if ((done & 0x3) == 0x1) + p += 2; + else if ((done & 0x3) == 0x2) + p += 1; + break; + } + } + } + } + else + goto close_file; + } + else if (bit_count == 8) + { + if (comp == 0) // no compression + { + pix = surface; + for (y = 0; y < h; y++) + { + if (!right_way_up) pix = surface + ((h - 1 - y) * w); + for (x = 0; x < w; x++) + { + *pix = pal[*p]; + p += scale_ratio; + if (p >= buffer_end) break; + pix++; + } + if (scale_ratio > 1) + { + read_line += scale_ratio; + if (read_line >= image_h) break; + + position += row_size * (scale_ratio - 1); + if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; + p = buffer; + buffer_end = buffer + row_size; + } + else + { + fix = (int)(((unsigned long)p) & 0x3); + if (fix > 0) p += 4 - fix; // align row read + if (p >= buffer_end) break; + } + } + } + else if (comp == 1) // rle 8bit/pixel + { + int count = 0, done = 0; + int scale_x = 0, scale_y = 0; + Eina_Bool scale_down_line = EINA_TRUE; + + pix = surface; + if (!right_way_up) pix = surface + ((h - 1 - y) * w); + + while (p < buffer_end) + { + if (p[0]) + { + if (scale_down_line) + { + if ((x + p[0]) <= image_w) + { + unsigned int col = pal[p[1]]; + + count = p[0]; + while (count > 0) + { + if (((x % scale_ratio) == 0) && (scale_x < w)) + { + *pix = col; + pix++; + scale_x ++; + } + x++; + count--; + } + } + } + p += 2; + } + else + { + switch (p[1]) + { + case 0: // EOL + x = 0; + scale_x = 0; + y++; + if ((y % scale_ratio) == 0) + { + scale_y++; + scale_down_line = EINA_TRUE; + if (!right_way_up) + pix = surface + ((h - 1 - scale_y) * w); + else + pix = surface + (scale_y * w); + } + else + scale_down_line = EINA_FALSE; + + if (scale_y >= h) + { + p = buffer_end; + } + p += 2; + break; + case 1: // EOB + p = buffer_end; + break; + case 2: // DELTA + x += p[2]; + y += p[3]; + scale_x = x / scale_ratio; + scale_y = y / scale_ratio; + if ((scale_x >= w) || (scale_y >= h)) + { + p = buffer_end; + } + if (!right_way_up) + pix = surface + scale_x + ((h - 1 - scale_y) * w); + else + pix = surface + scale_x + (scale_y * w); + p += 4; + break; + default: + count = p[1]; + if (((p + count) > buffer_end) || + ((x + count) > image_w)) + { + p = buffer_end; + break; + } + p += 2; + done = count; + while (count > 0) + { + if (((x % scale_ratio) == 0) && (scale_x < w)) + { + *pix = pal[*p]; + pix++; + scale_x ++; + } + p++; + x++; + count--; + } + if (done & 0x1) p++; + break; + } + } + } + } + else + goto close_file; + } + } + else if ((bit_count == 16) || (bit_count == 24) || (bit_count == 32)) + { + if (comp == 0) // no compression + { + position = offset; + if (scale_ratio == 1) + buffer = malloc(image_size + 8); // add 8 for padding to avoid checks + else + buffer = malloc(row_size); // scale down is usually set because of memory issue, so read line by line + if (!buffer) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto close_file; + } + if (scale_ratio == 1) + buffer_end = buffer + image_size; + else + buffer_end = buffer + row_size; + + p = buffer; + if (scale_ratio == 1) + { + if (!read_mem(map, fsize, &position, buffer, image_size)) goto close_file; + } + else + { + if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; + } + if (bit_count == 16) + { + unsigned short tmp; + + pix = surface; + for (y = 0; y < h; y++) + { + if (!right_way_up) pix = surface + ((h - 1 - y) * w); + for (x = 0; x < w; x++) + { + tmp = *((unsigned short *)(p)); + + r = (tmp >> 7) & 0xf8; r |= r >> 5; + g = (tmp >> 2) & 0xf8; g |= g >> 5; + b = (tmp << 3) & 0xf8; b |= b >> 5; + *pix = ARGB_JOIN(0xff, r, g, b); + + p += 2 * scale_ratio; + + if (p >= buffer_end) break; + pix++; + } + if (scale_ratio > 1) + { + read_line += scale_ratio; + if (read_line >= image_h) break; + + position += row_size * (scale_ratio - 1); + if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; + p = buffer; + buffer_end = buffer + row_size; + } + else + { + fix = (int)(((unsigned long)p) & 0x3); + if (fix > 0) p += 4 - fix; // align row read + if (p >= buffer_end) break; + } + } + } + else if (bit_count == 24) + { + pix = surface; + for (y = 0; y < h; y++) + { + if (!right_way_up) pix = surface + ((h - 1 - y) * w); + for (x = 0; x < w; x++) + { + b = p[0]; + g = p[1]; + r = p[2]; + *pix = ARGB_JOIN(0xff, r, g, b); + p += 3 * scale_ratio; + if (p >= buffer_end) break; + pix++; + } + if (scale_ratio > 1) + { + read_line += scale_ratio; + if (read_line >= image_h) break; + + position += row_size * (scale_ratio - 1); + if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; + p = buffer; + buffer_end = buffer + row_size; + } + else + { + fix = (int)(((unsigned long)p) & 0x3); + if (fix > 0) p += 4 - fix; // align row read + if (p >= buffer_end) break; + } + } + } + else if (bit_count == 32) + { + int none_zero_alpha = 0; + pix = surface; + for (y = 0; y < h; y++) + { + if (!right_way_up) pix = surface + ((h - 1 - y) * w); + for (x = 0; x < w; x++) + { + b = p[0]; + g = p[1]; + r = p[2]; + a = p[3]; + if (a) none_zero_alpha = 1; + if (!hasa) a = 0xff; + *pix = ARGB_JOIN(a, r, g, b); + p += 4 * scale_ratio; + + if (p >= buffer_end) break; + pix++; + } + if (scale_ratio > 1) + { + read_line += scale_ratio; + if (read_line >= image_h) break; + + position += row_size * (scale_ratio - 1); + if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; + p = buffer; + buffer_end = buffer + row_size; + } + else + { + fix = (int)(((unsigned long)p) & 0x3); + if (fix > 0) p += 4 - fix; // align row read + if (p >= buffer_end) break; + } + } + if (!none_zero_alpha) + { + ilp->alpha = 0; + if (hasa) + { + unsigned int *pixend = surface + (w * h); + + for (pix = surface; pix < pixend; pix++) + A_VAL(pix) = 0xff; + } + } + } + else + goto close_file; + } + else if (comp == 3) // bit field + { + if (!read_uint(map, fsize, &position, &rmask)) goto close_file; + if (!read_uint(map, fsize, &position, &gmask)) goto close_file; + if (!read_uint(map, fsize, &position, &bmask)) goto close_file; + + position = offset; + if (scale_ratio == 1) + buffer = malloc(image_size + 8); // add 8 for padding to avoid checks + else + buffer = malloc(row_size); // scale down is usually set because of memory issue, so read line by line + + if (!buffer) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto close_file; + } + if (scale_ratio == 1) + buffer_end = buffer + image_size; + else + buffer_end = buffer + row_size; + + p = buffer; + if (scale_ratio == 1) + { + if (!read_mem(map, fsize, &position, buffer, image_size)) goto close_file; + } + else + { + if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; + } + + if ((bit_count == 16) && + (rmask == 0xf800) && (gmask == 0x07e0) && (bmask == 0x001f) + ) + { + unsigned short tmp; + + pix = surface; + for (y = 0; y < h; y++) + { + if (!right_way_up) pix = surface + ((h - 1 - y) * w); + for (x = 0; x < w; x++) + { + tmp = *((unsigned short *)(p)); + + r = (tmp >> 8) & 0xf8; r |= r >> 5; + g = (tmp >> 3) & 0xfc; g |= g >> 6; + b = (tmp << 3) & 0xf8; b |= b >> 5; + *pix = ARGB_JOIN(0xff, r, g, b); + + p += 2 * scale_ratio; + + if (p >= buffer_end) break; + pix++; + } + if (scale_ratio > 1) + { + read_line += scale_ratio; + if (read_line >= image_h) break; + + position += row_size * (scale_ratio - 1); + if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; + p = buffer; + buffer_end = buffer + row_size; + } + else + { + fix = (int)(((unsigned long)p) & 0x3); + if (fix > 0) p += 4 - fix; // align row read + if (p >= buffer_end) break; + } + } + } + else if ((bit_count == 16) && + (rmask == 0x7c00) && (gmask == 0x03e0) && (bmask == 0x001f) + ) + { + unsigned short tmp; + pix = surface; + for (y = 0; y < h; y++) + { + if (!right_way_up) pix = surface + ((h - 1 - y) * w); + for (x = 0; x < w; x++) + { + tmp = *((unsigned short *)(p)); + + r = (tmp >> 7) & 0xf8; r |= r >> 5; + g = (tmp >> 2) & 0xf8; g |= g >> 5; + b = (tmp << 3) & 0xf8; b |= b >> 5; + *pix = ARGB_JOIN(0xff, r, g, b); + p += 2 * scale_ratio; + + if (p >= buffer_end) break; + pix++; + } + if (scale_ratio > 1) + { + read_line += scale_ratio; + if (read_line >= image_h) break; + + position += row_size * (scale_ratio - 1); + if (!read_mem(map, fsize, &position, buffer_end, row_size)) goto close_file; + p = buffer; + buffer_end = buffer + row_size; + } + else + { + fix = (int)(((unsigned long)p) & 0x3); + if (fix > 0) p += 4 - fix; // align row read + if (p >= buffer_end) break; + } + } + } + else if (bit_count == 32) + { + pix = surface; + for (y = 0; y < h; y++) + { + if (!right_way_up) pix = surface + ((h - 1 - y) * w); + for (x = 0; x < w; x++) + { + b = p[0]; + g = p[1]; + r = p[2]; + a = p[3]; + if (!hasa) a = 0xff; + *pix = ARGB_JOIN(a, r, g, b); + + p += 4 * scale_ratio; + + if (p >= buffer_end) break; + pix++; + } + if (scale_ratio > 1) + { + read_line += scale_ratio; + if (read_line >= image_h) break; + + position += row_size * (scale_ratio - 1); + if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; + p = buffer; + buffer_end = buffer + row_size; + } + else + { + fix = (int)(((unsigned long)p) & 0x3); + if (fix > 0) p += 4 - fix; // align row read + if (p >= buffer_end) break; + } + } + } + else + goto close_file; + } + else if (comp == 4) // jpeg - only printer drivers + { + goto close_file; + } + else if (comp == 3) // png - only printer drivers + { + goto close_file; + } + else + goto close_file; + } + else + goto close_file; + + if (buffer) free(buffer); + if (scale_surface) free(scale_surface); + + eina_file_map_free(f, map); + eina_file_close(f); + + evas_cserve2_image_premul(ilp); + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; + + close_file: + if (buffer) free(buffer); + if (scale_surface) free(scale_surface); + if (map) eina_file_map_free(f, map); + eina_file_close(f); + return EINA_FALSE; +} + +static Evas_Loader_Module_Api modapi = +{ + EVAS_CSERVE2_MODULE_API_VERSION, + "bmp", + evas_image_load_file_head_bmp, + evas_image_load_file_data_bmp +}; + +static Eina_Bool +module_init(void) +{ + return evas_cserve2_loader_register(&modapi); +} + +static void +module_shutdown(void) +{ +} + +EINA_MODULE_INIT(module_init); +EINA_MODULE_SHUTDOWN(module_shutdown); diff --git a/src/bin/loaders/eet/Makefile.am b/src/bin/loaders/eet/Makefile.am new file mode 100644 index 0000000..b6aedfb --- /dev/null +++ b/src/bin/loaders/eet/Makefile.am @@ -0,0 +1,34 @@ + +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +-I. \ +-I$(top_srcdir)/src/lib \ +-I$(top_srcdir)/src/lib/include \ +-I$(top_srcdir)/src/lib/cserve2 \ +-I$(top_srcdir)/src/bin \ +@evas_image_loader_eet_cflags@ \ +@EINA_CFLAGS@ + + +if BUILD_LOADER_EET +#if !EVAS_STATIC_BUILD_EET + +pkgdir = $(libdir)/evas/cserve2/loaders/eet/$(MODULE_ARCH) +pkg_LTLIBRARIES = module.la + +module_la_SOURCES = evas_image_load_eet.c + +module_la_LIBADD = @EINA_LIBS@ @evas_image_loader_eet_libs@ +module_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -module -avoid-version +module_la_LIBTOOLFLAGS = --tag=disable-static + +#else + +#noinst_LTLIBRARIES = libevas_loader_eet.la + +#libevas_loader_eet_la_SOURCES = evas_image_load_eet.c +#libevas_loader_eet_la_LIBADD = @evas_image_loader_eet_libs@ + +#endif +endif diff --git a/src/bin/loaders/eet/evas_image_load_eet.c b/src/bin/loaders/eet/evas_image_load_eet.c new file mode 100644 index 0000000..1eee2e7 --- /dev/null +++ b/src/bin/loaders/eet/evas_image_load_eet.c @@ -0,0 +1,158 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" /* so that EAPI in Eet.h is correctly defined */ +#endif + +#include + +#include "evas_macros.h" + +#include "evas_cserve2.h" +#include "evas_cserve2_slave.h" + + +static Eina_Bool +evas_image_load_file_head_eet(Evas_Img_Load_Params *ilp, const char *file, const char *key, int *error) +{ + int alpha, compression, quality, lossy; + unsigned int w, h; + Eet_File *ef; + int ok; + Eina_Bool res = EINA_FALSE; + + if (!key) + { + *error = CSERVE2_DOES_NOT_EXIST; + return EINA_FALSE; + } + + ef = eet_open((char *)file, EET_FILE_MODE_READ); + if (!ef) + { + *error = CSERVE2_DOES_NOT_EXIST; + return EINA_FALSE; + } + ok = eet_data_image_header_read(ef, key, + &w, &h, &alpha, &compression, &quality, &lossy); + if (!ok) + { + *error = CSERVE2_DOES_NOT_EXIST; + goto on_error; + } + if (IMG_TOO_BIG(w, h)) + { + *error = CSERVE2_RESOURCE_ALLOCATION_FAILED; + goto on_error; + } + if (alpha) ilp->alpha = 1; + ilp->w = w; + ilp->h = h; + res = EINA_TRUE; + *error = CSERVE2_NONE; + + on_error: + eet_close(ef); + return res; +} + +Eina_Bool +evas_image_load_file_data_eet(Evas_Img_Load_Params *ilp, const char *file, const char *key, int *error) +{ + unsigned int w, h; + int alpha, compression, quality, lossy, ok; + Eet_File *ef; + DATA32 *body, *p, *end, *data; + DATA32 nas = 0; + Eina_Bool res = EINA_FALSE; + + if (!key) + { + *error = CSERVE2_DOES_NOT_EXIST; + return EINA_FALSE; + } + ef = eet_open(file, EET_FILE_MODE_READ); + if (!ef) + { + *error = CSERVE2_DOES_NOT_EXIST; + return EINA_FALSE; + } + ok = eet_data_image_header_read(ef, key, + &w, &h, &alpha, &compression, &quality, &lossy); + if (IMG_TOO_BIG(w, h)) + { + *error = CSERVE2_RESOURCE_ALLOCATION_FAILED; + goto on_error; + } + if (!ok) + { + *error = CSERVE2_DOES_NOT_EXIST; + goto on_error; + } + data = ilp->buffer; + if (!data) + { + *error = CSERVE2_RESOURCE_ALLOCATION_FAILED; + goto on_error; + } + ok = eet_data_image_read_to_surface(ef, key, 0, 0, + data, w, h, w * 4, + &alpha, &compression, &quality, &lossy); + if (!ok) + { + *error = CSERVE2_GENERIC; + goto on_error; + } + if (alpha) + { + ilp->alpha = 1; + + body = ilp->buffer; + + end = body + (w * h); + for (p = body; p < end; p++) + { + DATA32 r, g, b, a; + + a = A_VAL(p); + r = R_VAL(p); + g = G_VAL(p); + b = B_VAL(p); + if ((a == 0) || (a == 255)) nas++; + if (r > a) r = a; + if (g > a) g = a; + if (b > a) b = a; + *p = ARGB_JOIN(a, r, g, b); + } + if ((ALPHA_SPARSE_INV_FRACTION * nas) >= (w * h)) + ilp->alpha_sparse = 1; + } + *error = CSERVE2_NONE; + res = EINA_TRUE; + + on_error: + eet_close(ef); + return res; +} + +static Evas_Loader_Module_Api modapi = +{ + EVAS_CSERVE2_MODULE_API_VERSION, + "eet", + evas_image_load_file_head_eet, + evas_image_load_file_data_eet +}; + +static Eina_Bool +module_init(void) +{ + eet_init(); + return evas_cserve2_loader_register(&modapi); +} + +static void +module_shutdown(void) +{ + eet_shutdown(); +} + +EINA_MODULE_INIT(module_init); +EINA_MODULE_SHUTDOWN(module_shutdown); diff --git a/src/bin/loaders/ico/Makefile.am b/src/bin/loaders/ico/Makefile.am new file mode 100644 index 0000000..10d0d4e --- /dev/null +++ b/src/bin/loaders/ico/Makefile.am @@ -0,0 +1,32 @@ + +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +-I. \ +-I$(top_srcdir)/src/lib \ +-I$(top_srcdir)/src/lib/include \ +-I$(top_srcdir)/src/lib/cserve2 \ +-I$(top_srcdir)/src/bin \ +@EINA_CFLAGS@ \ +@EVIL_CFLAGS@ + +if BUILD_LOADER_ICO +#if !EVAS_STATIC_BUILD_ICO + +pkgdir = $(libdir)/evas/cserve2/loaders/ico/$(MODULE_ARCH) +pkg_LTLIBRARIES = module.la + +module_la_SOURCES = evas_image_load_ico.c + +module_la_LIBADD = @EINA_LIBS@ @EVIL_LIBS@ +module_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -module -avoid-version +module_la_LIBTOOLFLAGS = --tag=disable-static + +#else + +#noinst_LTLIBRARIES = libevas_loader_ico.la +#libevas_loader_ico_la_SOURCES = evas_image_load_ico.c +#libevas_loader_ico_la_LIBADD = + +#endif +endif diff --git a/src/bin/loaders/ico/evas_image_load_ico.c b/src/bin/loaders/ico/evas_image_load_ico.c new file mode 100644 index 0000000..53c9b52 --- /dev/null +++ b/src/bin/loaders/ico/evas_image_load_ico.c @@ -0,0 +1,789 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#ifdef HAVE_EVIL +# include +#endif + +#include "evas_macros.h" + +#include "evas_cserve2.h" +#include "evas_cserve2_slave.h" + +static Eina_Bool +read_ushort(unsigned char *map, size_t length, size_t *position, unsigned short *ret) +{ + unsigned char b[2]; + + if (*position + 2 > length) return EINA_FALSE; + b[0] = map[(*position)++]; + b[1] = map[(*position)++]; + *ret = (b[1] << 8) | b[0]; + return EINA_TRUE; +} + +static Eina_Bool +read_uint(unsigned char *map, size_t length, size_t *position, unsigned int *ret) +{ + unsigned char b[4]; + unsigned int i; + + if (*position + 4 > length) return EINA_FALSE; + for (i = 0; i < 4; i++) + b[i] = map[(*position)++]; + *ret = ARGB_JOIN(b[3], b[2], b[1], b[0]); + return EINA_TRUE; +} + +static Eina_Bool +read_uchar(unsigned char *map, size_t length, size_t *position, unsigned char *ret) +{ + if (*position + 1 > length) return EINA_FALSE; + *ret = map[(*position)++]; + return EINA_TRUE; +} + +static Eina_Bool +read_mem(unsigned char *map, size_t length, size_t *position, void *buffer, int size) +{ + if (*position + size > length) return EINA_FALSE; + memcpy(buffer, map + *position, size); + *position += size; + return EINA_TRUE; +} + +enum +{ + SMALLEST, + BIGGEST, + SMALLER, + BIGGER +}; + +enum +{ + ICON = 1, + CURSOR = 2 +}; + +static Eina_Bool +evas_image_load_file_head_ico(Evas_Img_Load_Params *ilp, const char *file, const char *key, int *error) +{ + Eina_File *f; + void *map = NULL; + size_t position = 0; + unsigned short word; + unsigned char byte; + int wanted_w = 0, wanted_h = 0, w, h, cols, i, planes = 0, + bpp = 0, pdelta, search = -1, have_choice = 0, + hasa = 1; + unsigned int bmoffset, bmsize, fsize; + unsigned short reserved, type, count; + struct { + int pdelta; + int w, h; + int cols; + int bpp, planes; + int hot_x, hot_y; + unsigned int bmoffset, bmsize; + } chosen = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + f = eina_file_open(file, EINA_FALSE); + if (!f) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return EINA_FALSE; + } + + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + fsize = eina_file_size_get(f); + if (fsize < (6 + 16 + 40)) goto close_file; + + map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + if (!map) goto close_file; + + // key: + // NULL == highest res + // biggest == highest res + // smallest == lowest res + // + // smaller == next size SMALLER than load opts WxH (if possible) + // bigger == next size BIGGER than load opts WxH (if possible) + // more ? + + search = BIGGEST; + if ((ilp->opts.w > 0) && (ilp->opts.h > 0)) + { + wanted_w = ilp->opts.w; + wanted_h = ilp->opts.h; + search = SMALLER; + } + + if (!read_ushort(map, fsize, &position, &reserved)) goto close_file; + if (!read_ushort(map, fsize, &position, &type)) goto close_file; + if (!read_ushort(map, fsize, &position, &count)) goto close_file; + if (!((reserved == 0) && + ((type == ICON) || (type == CURSOR)) && (count > 0))) + goto close_file; + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + + if (key) + { + if (!strcmp(key, "biggest")) + { + wanted_w = 0; + wanted_h = 0; + search = BIGGEST; + chosen.pdelta = 0; + } + else if (!strcmp(key, "smallest")) + { + wanted_w = 1; + wanted_h = 1; + search = SMALLEST; + chosen.pdelta = 0x7fffffff; + } + else if (!strcmp(key, "smaller")) + { + chosen.pdelta = 0x7fffffff; + search = SMALLER; + } + else if (!strcmp(key, "bigger")) + { + chosen.pdelta = 0x7fffffff; + search = BIGGER; + } + } + for (i = 0; i < count; i++) + { + unsigned char tw = 0, th = 0, tcols = 0; + if (!read_uchar(map, fsize, &position, &tw)) goto close_file; + w = tw; + if (w <= 0) w = 256; + if (!read_uchar(map, fsize, &position, &th)) goto close_file; + h = th; + if (h <= 0) h = 256; + if (!read_uchar(map, fsize, &position, &tcols)) goto close_file; + cols = tcols; + if (cols <= 0) cols = 256; + if (!read_uchar(map, fsize, &position, &byte)) goto close_file; + if (!read_ushort(map, fsize, &position, &word)) goto close_file; + if (type == CURSOR) planes = word; + //else hot_x = word; + if (!read_ushort(map, fsize, &position, &word)) goto close_file; + if (type == CURSOR) bpp = word; + //else hot_y = word; + if (!read_uint(map, fsize, &position, &bmsize)) goto close_file; + if (!read_uint(map, fsize, &position, &bmoffset)) goto close_file; + if ((bmsize <= 0) || (bmoffset <= 0) || (bmoffset >= fsize)) goto close_file; + if (search == BIGGEST) + { + pdelta = w * h; + if ((!have_choice) || + ((pdelta >= chosen.pdelta) && + (((bpp >= 3) && (bpp >= chosen.bpp)) || + ((bpp < 3) && (cols >= chosen.cols))))) + { + have_choice = 1; + chosen.pdelta = pdelta; + chosen.w = w; + chosen.h = h; + chosen.cols = cols; + chosen.bpp = bpp; + chosen.planes = planes; + chosen.bmsize = bmsize; + chosen.bmoffset = bmoffset; + } + } + else + { + if (search == SMALLEST) + { + pdelta = w * h; + if ((!have_choice) || + ((pdelta <= chosen.pdelta) && + (((bpp >= 3) && (bpp >= chosen.bpp)) || + ((bpp < 3) && (cols >= chosen.cols))))) + { + have_choice = 1; + chosen.pdelta = pdelta; + chosen.w = w; + chosen.h = h; + chosen.cols = cols; + chosen.bpp = bpp; + chosen.planes = planes; + chosen.bmsize = bmsize; + chosen.bmoffset = bmoffset; + } + } + else if (search == SMALLER) + { + pdelta = (wanted_w * wanted_h) - (w * h); + if ((!have_choice) || + ((w <= wanted_w) && (h <= wanted_h) && + (pdelta <= chosen.pdelta) && + (((bpp >= 3) && (bpp >= chosen.bpp)) || + ((bpp < 3) && (cols >= chosen.cols))))) + { + have_choice = 1; + if (pdelta < 0) pdelta = 0x7fffffff; + chosen.pdelta = pdelta; + chosen.w = w; + chosen.h = h; + chosen.cols = cols; + chosen.bpp = bpp; + chosen.planes = planes; + chosen.bmsize = bmsize; + chosen.bmoffset = bmoffset; + } + } + else if (search == BIGGER) + { + pdelta = (w * h) - (wanted_w * wanted_h); + if ((!have_choice) || + ((w >= wanted_w) && (h >= wanted_h) && + (pdelta <= chosen.pdelta) && + (((bpp >= 3) && (bpp >= chosen.bpp)) || + ((bpp < 3) && (cols >= chosen.cols))))) + { + have_choice = 1; + if (pdelta < 0) pdelta = 0x7fffffff; + chosen.pdelta = pdelta; + chosen.w = w; + chosen.h = h; + chosen.cols = cols; + chosen.bpp = bpp; + chosen.planes = planes; + chosen.bmsize = bmsize; + chosen.bmoffset = bmoffset; + } + } + } + } + if (chosen.bmoffset == 0) goto close_file; + position = chosen.bmoffset; + + w = chosen.w; + h = chosen.h; + if ((w > 256) || (h > 256)) goto close_file; + if ((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE) || + IMG_TOO_BIG(w, h)) + { + if (IMG_TOO_BIG(w, h)) + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + else + *error = EVAS_LOAD_ERROR_GENERIC; + goto close_file; + } + + ilp->w = w; + ilp->h = h; + if (hasa) ilp->alpha = 1; + + eina_file_map_free(f, map); + eina_file_close(f); + + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; + + close_file: + if (map) eina_file_map_free(f, map); + eina_file_close(f); + return EINA_FALSE; +} + +static Eina_Bool +evas_image_load_file_data_ico(Evas_Img_Load_Params *ilp, const char *file, const char *key, int *error) +{ + Eina_File *f; + void *map = NULL; + size_t position = 0; + unsigned short word; + unsigned char byte; + unsigned int dword; + int wanted_w = 0, wanted_h = 0, w, h, cols, i, planes = 0, + bpp = 0, pdelta, search = -1, have_choice = 0, + stride, pstride, j, right_way_up = 0, diff_size = 0, cols2; + unsigned int bmoffset, bmsize, bitcount, fsize, + *pal, *surface, *pix, none_zero_alpha = 0; + unsigned short reserved, type, count; + unsigned char *maskbuf, *pixbuf, *p; + struct { + int pdelta; + int w, h; + int cols; + int bpp, planes; + int hot_x, hot_y; + unsigned int bmoffset, bmsize; + } chosen = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + f = eina_file_open(file, EINA_FALSE); + if (!f) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return EINA_FALSE; + } + + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + fsize = eina_file_size_get(f); + if (fsize < (6 + 16 + 40)) goto close_file; + + map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + if (!map) goto close_file; + + // key: + // NULL == highest res + // biggest == highest res + // smallest == lowest res + // + // smaller == next size SMALLER than load opts WxH (if possible) + // bigger == next size BIGGER than load opts WxH (if possible) + // more ? + + search = BIGGEST; + if ((ilp->opts.w > 0) && (ilp->opts.h > 0)) + { + wanted_w = ilp->opts.w; + wanted_h = ilp->opts.h; + search = SMALLER; + } + + if (!read_ushort(map, fsize, &position, &reserved)) goto close_file; + if (!read_ushort(map, fsize, &position, &type)) goto close_file; + if (!read_ushort(map, fsize, &position, &count)) goto close_file; + if (!((reserved == 0) && + ((type == ICON) || (type == CURSOR)) && (count > 0))) + goto close_file; + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + + if (key) + { + if (!strcmp(key, "biggest")) + { + wanted_w = 0; + wanted_h = 0; + search = BIGGEST; + chosen.pdelta = 0; + } + else if (!strcmp(key, "smallest")) + { + wanted_w = 1; + wanted_h = 1; + search = SMALLEST; + chosen.pdelta = 0x7fffffff; + } + else if (!strcmp(key, "smaller")) + { + chosen.pdelta = 0x7fffffff; + search = SMALLER; + } + else if (!strcmp(key, "bigger")) + { + chosen.pdelta = 0x7fffffff; + search = BIGGER; + } + } + for (i = 0; i < count; i++) + { + unsigned char tw = 0, th = 0, tcols = 0; + if (!read_uchar(map, fsize, &position, &tw)) goto close_file; + w = tw; + if (w <= 0) w = 256; + if (!read_uchar(map, fsize, &position, &th)) goto close_file; + h = th; + if (h <= 0) h = 256; + if (!read_uchar(map, fsize, &position, &tcols)) goto close_file; + cols = tcols; + if (cols <= 0) cols = 256; + if (!read_uchar(map, fsize, &position, &byte)) goto close_file; + if (!read_ushort(map, fsize, &position, &word)) goto close_file; + if (type == 1) planes = word; + //else hot_x = word; + if (!read_ushort(map, fsize, &position, &word)) goto close_file; + if (type == 1) bpp = word; + //else hot_y = word; + if (!read_uint(map, fsize, &position, &bmsize)) goto close_file; + if (!read_uint(map, fsize, &position, &bmoffset)) goto close_file; + if ((bmsize <= 0) || (bmoffset <= 0) || (bmoffset >= fsize)) goto close_file; + if (search == BIGGEST) + { + pdelta = w * h; + if ((!have_choice) || + ((pdelta >= chosen.pdelta) && + (((bpp >= 3) && (bpp >= chosen.bpp)) || + ((bpp < 3) && (cols >= chosen.cols))))) + { + have_choice = 1; + chosen.pdelta = pdelta; + chosen.w = w; + chosen.h = h; + chosen.cols = cols; + chosen.bpp = bpp; + chosen.planes = planes; + chosen.bmsize = bmsize; + chosen.bmoffset = bmoffset; + } + } + else + { + if (search == SMALLEST) + { + pdelta = w * h; + if ((!have_choice) || + ((pdelta <= chosen.pdelta) && + (((bpp >= 3) && (bpp >= chosen.bpp)) || + ((bpp < 3) && (cols >= chosen.cols))))) + { + have_choice = 1; + chosen.pdelta = pdelta; + chosen.w = w; + chosen.h = h; + chosen.cols = cols; + chosen.bpp = bpp; + chosen.planes = planes; + chosen.bmsize = bmsize; + chosen.bmoffset = bmoffset; + } + } + else if (search == SMALLER) + { + pdelta = (wanted_w * wanted_h) - (w * h); + if ((!have_choice) || + ((w <= wanted_w) && (h <= wanted_h) && + (pdelta <= chosen.pdelta) && + (((bpp >= 3) && (bpp >= chosen.bpp)) || + ((bpp < 3) && (cols >= chosen.cols))))) + { + have_choice = 1; + if (pdelta < 0) pdelta = 0x7fffffff; + chosen.pdelta = pdelta; + chosen.w = w; + chosen.h = h; + chosen.cols = cols; + chosen.bpp = bpp; + chosen.planes = planes; + chosen.bmsize = bmsize; + chosen.bmoffset = bmoffset; + } + } + else if (search == BIGGER) + { + pdelta = (w * h) - (wanted_w * wanted_h); + if ((!have_choice) || + ((w >= wanted_w) && (h >= wanted_h) && + (pdelta <= chosen.pdelta) && + (((bpp >= 3) && (bpp >= chosen.bpp)) || + ((bpp < 3) && (cols >= chosen.cols))))) + { + have_choice = 1; + if (pdelta < 0) pdelta = 0x7fffffff; + chosen.pdelta = pdelta; + chosen.w = w; + chosen.h = h; + chosen.cols = cols; + chosen.bpp = bpp; + chosen.planes = planes; + chosen.bmsize = bmsize; + chosen.bmoffset = bmoffset; + } + } + } + } + if (chosen.bmoffset == 0) goto close_file; + position = chosen.bmoffset; + + w = chosen.w; + h = chosen.h; + cols = chosen.cols; + bpp = chosen.bpp; + // changed since we loaded header? + if (((int)ilp->w != w) || ((int)ilp->h != h)) goto close_file; + + // read bmp header time... let's do some checking + if (!read_uint(map, fsize, &position, &dword)) goto close_file; // headersize - dont care + if (!read_uint(map, fsize, &position, &dword)) goto close_file; // width + if (dword > 0) + { + if ((int)dword != w) + { + w = dword; + diff_size = 1; + } + } + if (!read_uint(map, fsize, &position, &dword)) goto close_file; // height + if (dword > 0) + { + if ((int)dword != (h * 2)) + { + h = dword / 2; + diff_size = 1; + } + } + if (diff_size) + { + ERR("Broken ICO file: %s - " + " Reporting size of %ix%i in index, but bitmap is %ix%i. " + " May be expanded or cropped.", + file, ilp->w, ilp->h, w, h); + } + if (!read_ushort(map, fsize, &position, &word)) goto close_file; // planes + //planes2 = word; + if (!read_ushort(map, fsize, &position, &word)) goto close_file; // bitcount + bitcount = word; + if (!read_uint(map, fsize, &position, &dword)) goto close_file; // compression + //compression = dword; + if (!read_uint(map, fsize, &position, &dword)) goto close_file; // imagesize + //imagesize = dword; + if (!read_uint(map, fsize, &position, &dword)) goto close_file; // z pixels per m + if (!read_uint(map, fsize, &position, &dword)) goto close_file; // y pizels per m + if (!read_uint(map, fsize, &position, &dword)) goto close_file; // colors used + //colorsused = dword; + if (!read_uint(map, fsize, &position, &dword)) goto close_file; // colors important + //colorsimportant = dword; + + surface = ilp->buffer; + if (!surface) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto close_file; + } + memset(surface, 0, ilp->w * ilp->h * 4); + + if (!((bitcount == 1) || (bitcount == 4) || (bitcount == 8) || + (bitcount == 24) || (bitcount == 32))) + goto close_file; + if (bitcount <= 8) + { + cols2 = 1 << bitcount; + if (cols == 0) cols = cols2; + if (cols > cols2) cols = cols2; + if (cols > 256) cols = 256; + } + else + cols = 0; + if (bitcount > 8) cols = 0; + + pal = alloca(256 * 4); + for (i = 0; i < cols; i++) + { + unsigned char a, r, g, b; + + if (!read_uchar(map, fsize, &position, &b)) goto close_file; + if (!read_uchar(map, fsize, &position, &g)) goto close_file; + if (!read_uchar(map, fsize, &position, &r)) goto close_file; + if (!read_uchar(map, fsize, &position, &a)) goto close_file; + a = 0xff; + pal[i] = ARGB_JOIN(a, r, g, b); + } + stride = ((w + 31) / 32); + maskbuf = alloca(stride * h); + pixbuf = alloca(stride * 32 * 4); // more than enough + if (bitcount == 1) + { + pstride = stride * 4; + for (i = 0; i < h; i++) + { + pix = surface + (i * ilp->w); + if (!right_way_up) pix = surface + ((ilp->h - 1 - i) * ilp->w); + if (!read_mem(map, fsize, &position, pixbuf, pstride)) goto close_file; + p = pixbuf; + if (i >= (int)ilp->h) continue; + for (j = 0; j < w; j++) + { + if (j >= (int)ilp->w) break; + if ((j & 0x7) == 0x0) + { + *pix = pal[*p >> 7]; + } + else if ((j & 0x7) == 0x1) + { + *pix = pal[(*p >> 6) & 0x1]; + } + else if ((j & 0x7) == 0x2) + { + *pix = pal[(*p >> 5) & 0x1]; + } + else if ((j & 0x7) == 0x3) + { + *pix = pal[(*p >> 4) & 0x1]; + } + else if ((j & 0x7) == 0x4) + { + *pix = pal[(*p >> 3) & 0x1]; + } + else if ((j & 0x7) == 0x5) + { + *pix = pal[(*p >> 2) & 0x1]; + } + else if ((j & 0x7) == 0x6) + { + *pix = pal[(*p >> 1) & 0x1]; + } + else + { + *pix = pal[*p & 0x1]; + p++; + } + pix++; + } + } + } + else if (bitcount == 4) + { + pstride = ((w + 7) / 8) * 4; + for (i = 0; i < h; i++) + { + pix = surface + (i * ilp->w); + if (!right_way_up) pix = surface + ((ilp->h - 1 - i) * ilp->w); + if (!read_mem(map, fsize, &position, pixbuf, pstride)) goto close_file; + p = pixbuf; + if (i >= (int)ilp->h) continue; + for (j = 0; j < w; j++) + { + if (j >= (int)ilp->w) break; + if ((j & 0x1) == 0x1) + { + *pix = pal[*p & 0x0f]; + p++; + } + else + { + *pix = pal[*p >> 4]; + } + pix++; + } + } + } + else if (bitcount == 8) + { + pstride = ((w + 3) / 4) * 4; + for (i = 0; i < h; i++) + { + pix = surface + (i * ilp->w); + if (!right_way_up) pix = surface + ((ilp->h - 1 - i) * ilp->w); + if (!read_mem(map, fsize, &position, pixbuf, pstride)) goto close_file; + p = pixbuf; + if (i >= (int)ilp->h) continue; + for (j = 0; j < w; j++) + { + if (j >= (int)ilp->w) break; + *pix = pal[*p]; + p++; + pix++; + } + } + } + else if (bitcount == 24) + { + pstride = w * 3; + for (i = 0; i < h; i++) + { + pix = surface + (i * ilp->w); + if (!right_way_up) pix = surface + ((ilp->h - 1 - i) * ilp->w); + if (!read_mem(map, fsize, &position, pixbuf, pstride)) goto close_file; + p = pixbuf; + if (i >= (int)ilp->h) continue; + for (j = 0; j < w; j++) + { + unsigned char a, r, g, b; + + if (j >= (int)ilp->w) break; + b = p[0]; + g = p[1]; + r = p[2]; + p += 3; + a = 0xff; + *pix = ARGB_JOIN(a, r, g, b); + pix++; + } + } + } + else if (bitcount == 32) + { + pstride = w * 4; + for (i = 0; i < h; i++) + { + pix = surface + (i * ilp->w); + if (!right_way_up) pix = surface + ((ilp->h - 1 - i) * ilp->w); + if (!read_mem(map, fsize, &position, pixbuf, pstride)) goto close_file; + p = pixbuf; + if (i >= (int)ilp->h) continue; + for (j = 0; j < w; j++) + { + unsigned char a, r, g, b; + + if (j >= (int)ilp->w) break; + b = p[0]; + g = p[1]; + r = p[2]; + a = p[3]; + p += 4; + if (a) none_zero_alpha = 1; + *pix = ARGB_JOIN(a, r, g, b); + pix++; + } + } + } + if (!none_zero_alpha) + { + if (!read_mem(map, fsize, &position, maskbuf, stride * 4 * h)) goto close_file; + // apply mask + pix = surface; + for (i = 0; i < h; i++) + { + unsigned char *m; + + pix = surface + (i * ilp->w); + if (!right_way_up) pix = surface + ((ilp->h - 1 - i) * ilp->w); + m = maskbuf + (stride * i * 4); + if (i >= (int)ilp->h) continue; + for (j = 0; j < w; j++) + { + if (j >= (int)ilp->w) break; + if (*m & (1 << (7 - (j & 0x7)))) + A_VAL(pix) = 0x00; + else + A_VAL(pix) = 0xff; + if ((j & 0x7) == 0x7) m++; + pix++; + } + } + } + + eina_file_map_free(f, map); + eina_file_close(f); + + evas_cserve2_image_premul(ilp); + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; + + close_file: + if (map) eina_file_map_free(f, map); + eina_file_close(f); + return EINA_FALSE; +} + +static Evas_Loader_Module_Api modapi = +{ + EVAS_CSERVE2_MODULE_API_VERSION, + "ico", + evas_image_load_file_head_ico, + evas_image_load_file_data_ico +}; + +static Eina_Bool +module_init(void) +{ + return evas_cserve2_loader_register(&modapi); +} + +static void +module_shutdown(void) +{ +} + +EINA_MODULE_INIT(module_init); +EINA_MODULE_SHUTDOWN(module_shutdown); diff --git a/src/bin/loaders/jpeg/Makefile.am b/src/bin/loaders/jpeg/Makefile.am new file mode 100644 index 0000000..5bb093f --- /dev/null +++ b/src/bin/loaders/jpeg/Makefile.am @@ -0,0 +1,33 @@ + +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +-I. \ +-I$(top_srcdir)/src/lib \ +-I$(top_srcdir)/src/lib/include \ +-I$(top_srcdir)/src/lib/cserve2 \ +-I$(top_srcdir)/src/bin \ +@EINA_CFLAGS@ \ +@evas_image_loader_jpeg_cflags@ \ +@EVIL_CFLAGS@ + +if BUILD_LOADER_JPEG +#if !EVAS_STATIC_BUILD_JPEG + +pkgdir = $(libdir)/evas/cserve2/loaders/jpeg/$(MODULE_ARCH) +pkg_LTLIBRARIES = module.la + +module_la_SOURCES = evas_image_load_jpeg.c + +module_la_LIBADD = @EINA_LIBS@ @EVIL_LIBS@ @evas_image_loader_jpeg_libs@ +module_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -module -avoid-version +module_la_LIBTOOLFLAGS = --tag=disable-static + +#else + +#noinst_LTLIBRARIES = libevas_loader_jpeg.la +#libevas_loader_jpeg_la_SOURCES = evas_image_load_jpeg.c +#libevas_loader_jpeg_la_LIBADD = @evas_image_loader_jpeg_libs@ + +#endif +endif diff --git a/src/bin/loaders/jpeg/evas_image_load_jpeg.c b/src/bin/loaders/jpeg/evas_image_load_jpeg.c new file mode 100644 index 0000000..4fbd061 --- /dev/null +++ b/src/bin/loaders/jpeg/evas_image_load_jpeg.c @@ -0,0 +1,1118 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include +#include + +#include "evas_macros.h" + +#include "evas_cserve2.h" +#include "evas_cserve2_slave.h" + +typedef struct _JPEG_error_mgr *emptr; +struct _JPEG_error_mgr +{ + struct jpeg_error_mgr pub; + jmp_buf setjmp_buffer; +}; + +static void _JPEGFatalErrorHandler(j_common_ptr cinfo); +static void _JPEGErrorHandler(j_common_ptr cinfo); +static void _JPEGErrorHandler2(j_common_ptr cinfo, int msg_level); + +static void +_JPEGFatalErrorHandler(j_common_ptr cinfo) +{ + emptr errmgr; + + errmgr = (emptr) cinfo->err; + /* cinfo->err->output_message(cinfo);*/ + longjmp(errmgr->setjmp_buffer, 1); + return; +} + +static void +_JPEGErrorHandler(j_common_ptr cinfo __UNUSED__) +{ +/* emptr errmgr; */ + +/* errmgr = (emptr) cinfo->err; */ + /* cinfo->err->output_message(cinfo);*/ + /* longjmp(errmgr->setjmp_buffer, 1);*/ + return; +} + +static void +_JPEGErrorHandler2(j_common_ptr cinfo __UNUSED__, int msg_level __UNUSED__) +{ +/* emptr errmgr; */ + +/* errmgr = (emptr) cinfo->err; */ + /* cinfo->err->output_message(cinfo);*/ + /* longjmp(errmgr->setjmp_buffer, 1);*/ + return; +} + +struct jpeg_membuf_src +{ + struct jpeg_source_mgr pub; + + const unsigned char *buf; + size_t len; + struct jpeg_membuf_src *self; +}; + +static void +_evas_jpeg_membuf_src_init(j_decompress_ptr cinfo __UNUSED__) +{ +} + +static boolean +_evas_jpeg_membuf_src_fill(j_decompress_ptr cinfo) +{ + static const JOCTET jpeg_eoi[2] = { 0xFF, JPEG_EOI }; + struct jpeg_membuf_src *src = (struct jpeg_membuf_src *)cinfo->src; + + src->pub.bytes_in_buffer = sizeof(jpeg_eoi); + src->pub.next_input_byte = jpeg_eoi; + + return TRUE; +} + +static void +_evas_jpeg_membuf_src_skip(j_decompress_ptr cinfo, + long num_bytes) +{ + struct jpeg_membuf_src *src = (struct jpeg_membuf_src *)cinfo->src; + + if ((((long)src->pub.bytes_in_buffer - (long)src->len) > num_bytes) || + ((long)src->pub.bytes_in_buffer < num_bytes)) + { + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo)); + return; + } + src->pub.bytes_in_buffer -= num_bytes; + src->pub.next_input_byte += num_bytes; +} + +static void +_evas_jpeg_membuf_src_term(j_decompress_ptr cinfo) +{ + struct jpeg_membuf_src *src = (struct jpeg_membuf_src *)cinfo->src; + if (!src) return; + free(src); + cinfo->src = NULL; +} + +static int +_evas_jpeg_membuf_src(j_decompress_ptr cinfo, + void *map, size_t length) +{ + struct jpeg_membuf_src *src; + + src = calloc(1, sizeof(*src)); + if (!src) + return -1; + + src->self = src; + + cinfo->src = &src->pub; + src->buf = map; + src->len = length; + src->pub.init_source = _evas_jpeg_membuf_src_init; + src->pub.fill_input_buffer = _evas_jpeg_membuf_src_fill; + src->pub.skip_input_data = _evas_jpeg_membuf_src_skip; + src->pub.resync_to_restart = jpeg_resync_to_restart; + src->pub.term_source = _evas_jpeg_membuf_src_term; + src->pub.bytes_in_buffer = src->len; + src->pub.next_input_byte = src->buf; + + return 0; +} + +/*! Magic number for EXIF header & App1*/ +static const unsigned char ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; +static const unsigned char App1[] = {0xff, 0xe1}; +typedef enum { + EXIF_BYTE_ALIGN_II, + EXIF_BYTE_ALIGN_MM +} ExifByteAlign; + +static int +_get_orientation(void *map, size_t length) +{ + char *buf; + char orientation[2]; + ExifByteAlign byte_align; + unsigned int num_directory = 0; + unsigned int i, j; + int direction; + + /* open file and get 22 byte frome file */ + if (!map) return 0; + /* 1. read 22byte */ + if (length < 22) return 0; + buf = (char *)map; + + /* 2. check 2,3 bypte with APP1(0xFFE1) */ + if (memcmp(buf + 2, App1, sizeof (App1))) return 0; + + /* 3. check 6~11bype with Exif Header (0x45786966 0000) */ + if (memcmp(buf + 6, ExifHeader, sizeof (ExifHeader))) return 0; + + /* 4. get 12&13 byte get info of "II(0x4949)" or "MM(0x4d4d)" */ + /* 5. get [20]&[21] get directory entry # */ + if (!strncmp(buf + 12, "MM", 2)) + { + byte_align = EXIF_BYTE_ALIGN_MM; + num_directory = ((*(buf + 20) << 8) + *(buf + 21)); + orientation[0] = 0x01; + orientation[1] = 0x12; + } + else if (!strncmp(buf + 12, "II", 2)) + { + byte_align = EXIF_BYTE_ALIGN_II; + num_directory = ((*(buf + 21) << 8) + *(buf + 20)); + orientation[0] = 0x12; + orientation[1] = 0x01; + } + else return 0; + + buf = map + 22; + + if (length < (12 * num_directory + 22)) return 0; + + j = 0; + + for (i = 0; i < num_directory; i++ ) + { + if (!strncmp(buf + j, orientation, 2)) + { + /*get orientation tag */ + if (byte_align == EXIF_BYTE_ALIGN_MM) + direction = *(buf+ j + 11); + else direction = *(buf+ j + 8); + switch (direction) + { + case 3: + case 4: + return 180; + case 6: + case 7: + return 90; + case 5: + case 8: + return 270; + default: + return 0; + } + } + else + j = j + 12; + } + return 0; +} + +static Eina_Bool +evas_image_load_file_head_jpeg_internal(Evas_Img_Load_Params *ilp, + void *map, size_t length, + int *error) +{ + unsigned int w, h, scalew, scaleh; + struct jpeg_decompress_struct cinfo; + struct _JPEG_error_mgr jerr; + + /* for rotation decoding */ + int degree = 0; + Eina_Bool change_wh = EINA_FALSE; + unsigned int load_opts_w = 0, load_opts_h = 0; + + cinfo.err = jpeg_std_error(&(jerr.pub)); + jerr.pub.error_exit = _JPEGFatalErrorHandler; + jerr.pub.emit_message = _JPEGErrorHandler2; + jerr.pub.output_message = _JPEGErrorHandler; + if (setjmp(jerr.setjmp_buffer)) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + if (cinfo.saw_JFIF_marker) + *error = CSERVE2_CORRUPT_FILE; + else + *error = CSERVE2_UNKNOWN_FORMAT; + return EINA_FALSE; + } + + degree = 0; + change_wh = EINA_FALSE; + jpeg_create_decompress(&cinfo); + + if (_evas_jpeg_membuf_src(&cinfo, map, length)) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = CSERVE2_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + + jpeg_read_header(&cinfo, TRUE); + cinfo.do_fancy_upsampling = FALSE; + cinfo.do_block_smoothing = FALSE; + cinfo.dct_method = JDCT_ISLOW; // JDCT_FLOAT JDCT_IFAST(quality loss) + cinfo.dither_mode = JDITHER_ORDERED; + jpeg_start_decompress(&cinfo); + + /* rotation decoding */ + if (ilp->opts.orientation) + { + degree = _get_orientation(map, length); + if (degree != 0) + { + ilp->degree = degree; + ilp->rotated = EINA_TRUE; + + if (degree == 90 || degree == 270) + change_wh = EINA_TRUE; + } + + } + + /* head decoding */ + w = cinfo.output_width; + h = cinfo.output_height; + if ((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE) || + (IMG_TOO_BIG(w, h))) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + if (IMG_TOO_BIG(w, h)) + *error = CSERVE2_RESOURCE_ALLOCATION_FAILED; + else + *error = CSERVE2_GENERIC; + return EINA_FALSE; + } + if (ilp->opts.scale_down_by > 1) + { + w /= ilp->opts.scale_down_by; + h /= ilp->opts.scale_down_by; + } + else if (ilp->opts.dpi > 0.0) + { + w = (w * ilp->opts.dpi) / 90.0; + h = (h * ilp->opts.dpi) / 90.0; + } + else if ((ilp->opts.w > 0) && (ilp->opts.h > 0)) + { + unsigned int w2 = w, h2 = h; + /* user set load_opts' w,h on the assumption + that image already rotated according to it's orientation info */ + if (change_wh) + { + load_opts_w = ilp->opts.w; + load_opts_h = ilp->opts.h; + ilp->opts.w = load_opts_h; + ilp->opts.h = load_opts_w; + } + + if (ilp->opts.w > 0) + { + w2 = ilp->opts.w; + h2 = (ilp->opts.w * h) / w; + if ((ilp->opts.h > 0) && (h2 > ilp->opts.h)) + { + unsigned int w3; + h2 = ilp->opts.h; + w3 = (ilp->opts.h * w) / h; + if (w3 > w2) + w2 = w3; + } + } + else if (ilp->opts.h > 0) + { + h2 = ilp->opts.h; + w2 = (ilp->opts.h * w) / h; + } + w = w2; + h = h2; + if (change_wh) + { + ilp->opts.w = load_opts_w; + ilp->opts.h = load_opts_h; + } + } + if (w < 1) w = 1; + if (h < 1) h = 1; + + if ((w != cinfo.output_width) || (h != cinfo.output_height)) + { + scalew = cinfo.output_width / w; + scaleh = cinfo.output_height / h; + + ilp->scale = scalew; + if (scaleh < scalew) ilp->scale = scaleh; + + if (ilp->scale > 8) ilp->scale = 8; + else if (ilp->scale < 1) ilp->scale = 1; + + if (ilp->scale == 3) ilp->scale = 2; + else if (ilp->scale == 5) ilp->scale = 4; + else if (ilp->scale == 6) ilp->scale = 4; + else if (ilp->scale == 7) ilp->scale = 4; + } + + if (ilp->scale > 1) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + jpeg_create_decompress(&cinfo); + + if (_evas_jpeg_membuf_src(&cinfo, map, length)) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = CSERVE2_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + + jpeg_read_header(&cinfo, TRUE); + cinfo.do_fancy_upsampling = FALSE; + cinfo.do_block_smoothing = FALSE; + cinfo.scale_num = 1; + cinfo.scale_denom = ilp->scale; + jpeg_calc_output_dimensions(&(cinfo)); + jpeg_start_decompress(&cinfo); + } + + ilp->w = cinfo.output_width; + ilp->h = cinfo.output_height; + + // be nice and clip region to image. if its totally outside, fail load + if ((ilp->opts.rw > 0) && (ilp->opts.rh > 0)) + { + unsigned int load_region_x = 0, load_region_y = 0; + unsigned int load_region_w = 0, load_region_h = 0; + if (ilp->rotated) + { + load_region_x = ilp->opts.rx; + load_region_y = ilp->opts.ry; + load_region_w = ilp->opts.rw; + load_region_h = ilp->opts.rh; + + switch (degree) + { + case 90: + ilp->opts.rx = load_region_y; + ilp->opts.ry = h - (load_region_x + load_region_w); + ilp->opts.rw = load_region_h; + ilp->opts.rh = load_region_w; + break; + case 180: + ilp->opts.rx = w - (load_region_x+ load_region_w); + ilp->opts.ry = h - (load_region_y + load_region_h); + + break; + case 270: + ilp->opts.rx = w - (load_region_y + load_region_h); + ilp->opts.ry = load_region_x; + ilp->opts.rw = load_region_h; + ilp->opts.rh = load_region_w; + break; + default: + break; + } + + } + RECTS_CLIP_TO_RECT(ilp->opts.rx, ilp->opts.ry, + ilp->opts.rw, ilp->opts.rh, + 0, 0, ilp->w, ilp->h); + if ((ilp->opts.rw <= 0) || (ilp->opts.rh <= 0)) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = CSERVE2_GENERIC; + return EINA_FALSE; + } + ilp->w = ilp->opts.rw; + ilp->h = ilp->opts.rh; + if (ilp->rotated) + { + ilp->opts.rx = load_region_x; + ilp->opts.ry = load_region_y; + ilp->opts.rw = load_region_w; + ilp->opts.rh = load_region_h; + } + } +/* end head decoding */ + + if (change_wh) + { + unsigned int tmp; + tmp = ilp->w; + ilp->w = ilp->h; + ilp->h = tmp; + } + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = CSERVE2_NONE; + return EINA_TRUE; +} + +/* +static double +get_time(void) +{ + struct timeval timev; + + gettimeofday(&timev, NULL); + return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000); +} +*/ + +static Eina_Bool +evas_image_load_file_data_jpeg_internal(Evas_Img_Load_Params *ilp, + void *map, size_t size, + int *error) +{ + unsigned int w, h; + struct jpeg_decompress_struct cinfo; + struct _JPEG_error_mgr jerr; + DATA8 *ptr, *line[16], *data; + DATA32 *ptr2, *ptr_rotate = NULL; + unsigned int x, y, l, i, scans; + int region = 0; + /* rotation setting */ + unsigned int tmp; + unsigned int load_region_x = 0, load_region_y = 0; + unsigned int load_region_w = 0, load_region_h = 0; + volatile int degree = 0; + volatile Eina_Bool change_wh = EINA_FALSE; + Eina_Bool line_done = EINA_FALSE; + + if (ilp->rotated) + { + degree = ilp->degree; + if (degree == 90 || degree == 270) + change_wh = EINA_TRUE; + } + + cinfo.err = jpeg_std_error(&(jerr.pub)); + jerr.pub.error_exit = _JPEGFatalErrorHandler; + jerr.pub.emit_message = _JPEGErrorHandler2; + jerr.pub.output_message = _JPEGErrorHandler; + if (setjmp(jerr.setjmp_buffer)) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = CSERVE2_CORRUPT_FILE; + return EINA_FALSE; + } + jpeg_create_decompress(&cinfo); + + if (_evas_jpeg_membuf_src(&cinfo, map, size)) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = CSERVE2_RESOURCE_ALLOCATION_FAILED; + return 0; + } + + jpeg_read_header(&cinfo, TRUE); + cinfo.do_fancy_upsampling = FALSE; + cinfo.do_block_smoothing = FALSE; + cinfo.dct_method = JDCT_ISLOW; // JDCT_FLOAT JDCT_IFAST(quality loss) + cinfo.dither_mode = JDITHER_ORDERED; + + if (ilp->scale > 1) + { + cinfo.scale_num = 1; + cinfo.scale_denom = ilp->scale; + } + + /* Colorspace conversion options */ + /* libjpeg can do the following conversions: */ + /* GRAYSCLAE => RGB YCbCr => RGB and YCCK => CMYK */ + switch (cinfo.jpeg_color_space) + { + case JCS_UNKNOWN: + break; + case JCS_GRAYSCALE: + case JCS_RGB: + case JCS_YCbCr: + cinfo.out_color_space = JCS_RGB; + break; + case JCS_CMYK: + case JCS_YCCK: + cinfo.out_color_space = JCS_CMYK; + break; + default: + /* unhandled format, do something */ + break; + } + +/* head decoding */ + jpeg_calc_output_dimensions(&(cinfo)); + jpeg_start_decompress(&cinfo); + + w = cinfo.output_width; + h = cinfo.output_height; + + if (change_wh) + { + tmp = ilp->w; + ilp->w = ilp->h; + ilp->h = tmp; + } + + if ((ilp->opts.rw > 0) && (ilp->opts.rh > 0)) + { + region = 1; + + if (ilp->rotated) + { + load_region_x = ilp->opts.rx; + load_region_y = ilp->opts.ry; + load_region_w = ilp->opts.rw; + load_region_h = ilp->opts.rh; + + switch (degree) + { + case 90: + ilp->opts.rx = load_region_y; + ilp->opts.ry = h - (load_region_x + load_region_w); + ilp->opts.rw = load_region_h; + ilp->opts.rh = load_region_w; + break; + case 180: + ilp->opts.rx = w - (load_region_x+ load_region_w); + ilp->opts.ry = h - (load_region_y + load_region_h); + + break; + case 270: + ilp->opts.rx = w - (load_region_y + load_region_h); + ilp->opts.ry = load_region_x; + ilp->opts.rw = load_region_h; + ilp->opts.rh = load_region_w; + break; + default: + break; + } + + } +#ifdef BUILD_LOADER_JPEG_REGION + cinfo.region_x = ilp->opts.rx; + cinfo.region_y = ilp->opts.ry; + cinfo.region_w = ilp->opts.rw; + cinfo.region_h = ilp->opts.rh; +#endif + } + /* what to do about this? We'll check for this kind of races on loaders + * or cache invalidation due to file modification should always be + * detected by the server? */ + + if ((!region) && ((w != ilp->w) || (h != ilp->h))) + { + // race condition, the file could have change from when we call header + // this test will not solve the problem with region code. + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = CSERVE2_GENERIC; + return EINA_FALSE; + } + + if ((region) && + ((ilp->w != ilp->opts.rw) || (ilp->h != ilp->opts.rh))) + { + ilp->w = ilp->opts.rw; + ilp->h = ilp->opts.rh; + } + + if (!(((cinfo.out_color_space == JCS_RGB) && + ((cinfo.output_components == 3) || (cinfo.output_components == 1))) || + ((cinfo.out_color_space == JCS_CMYK) && (cinfo.output_components == 4)))) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = CSERVE2_UNKNOWN_FORMAT; + return EINA_FALSE; + } + +/* end head decoding */ +/* data decoding */ + if (cinfo.rec_outbuf_height > 16) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = CSERVE2_UNKNOWN_FORMAT; + return EINA_FALSE; + } + data = alloca(w * 16 * cinfo.output_components); + if ((ilp->rotated) && change_wh) + { + ptr2 = malloc(ilp->w * ilp->h * sizeof(DATA32)); + ptr_rotate = ptr2; + } + else + ptr2 = ilp->buffer;; + + if (!ptr2) + { + *error = CSERVE2_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + + /* We handle first CMYK (4 components) */ + if (cinfo.output_components == 4) + { + // FIXME: handle region + for (i = 0; (int)i < cinfo.rec_outbuf_height; i++) + line[i] = data + (i * w * 4); + for (l = 0; l < h; l += cinfo.rec_outbuf_height) + { + jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height); + scans = cinfo.rec_outbuf_height; + if ((h - l) < scans) scans = h - l; + ptr = data; + if (!region) + { + for (y = 0; y < scans; y++) + { + if (cinfo.saw_Adobe_marker) + { + for (x = 0; x < w; x++) + { + /* According to libjpeg doc, Photoshop inverse the values of C, M, Y and K, */ + /* that is C is replaces by 255 - C, etc...*/ + /* See the comment below for the computation of RGB values from CMYK ones. */ + *ptr2 = + (0xff000000) | + ((ptr[0] * ptr[3] / 255) << 16) | + ((ptr[1] * ptr[3] / 255) << 8) | + ((ptr[2] * ptr[3] / 255)); + ptr += 4; + ptr2++; + } + } + else + { + for (x = 0; x < w; x++) + { + /* Conversion from CMYK to RGB is done in 2 steps: */ + /* CMYK => CMY => RGB (see http://www.easyrgb.com/index.php?X=MATH) */ + /* after computation, if C, M, Y and K are between 0 and 1, we have: */ + /* R = (1 - C) * (1 - K) * 255 */ + /* G = (1 - M) * (1 - K) * 255 */ + /* B = (1 - Y) * (1 - K) * 255 */ + /* libjpeg stores CMYK values between 0 and 255, */ + /* so we replace C by C * 255 / 255, etc... and we obtain: */ + /* R = (255 - C) * (255 - K) / 255 */ + /* G = (255 - M) * (255 - K) / 255 */ + /* B = (255 - Y) * (255 - K) / 255 */ + /* with C, M, Y and K between 0 and 255. */ + *ptr2 = + (0xff000000) | + (((255 - ptr[0]) * (255 - ptr[3]) / 255) << 16) | + (((255 - ptr[1]) * (255 - ptr[3]) / 255) << 8) | + (((255 - ptr[2]) * (255 - ptr[3]) / 255)); + ptr += 4; + ptr2++; + } + } + } + } + else + { + // if line # > region last line, break + if (l >= (ilp->opts.ry + ilp->opts.rh)) + { + line_done = EINA_TRUE; + /* if rotation flag is set , we have to rotate image */ + goto done; + /*jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = NONE; + return EINA_FALSE;*/ + } + // els if scan block intersects region start or later + else if ((l + scans) > + (ilp->opts.ry)) + { + for (y = 0; y < scans; y++) + { + if (((y + l) >= ilp->opts.ry) && + ((y + l) < (ilp->opts.ry + ilp->opts.rh))) + { + ptr += ilp->opts.rx; + if (cinfo.saw_Adobe_marker) + { + for (x = 0; x < ilp->opts.rw; x++) + { + /* According to libjpeg doc, Photoshop inverse the values of C, M, Y and K, */ + /* that is C is replaces by 255 - C, etc...*/ + /* See the comment below for the computation of RGB values from CMYK ones. */ + *ptr2 = + (0xff000000) | + ((ptr[0] * ptr[3] / 255) << 16) | + ((ptr[1] * ptr[3] / 255) << 8) | + ((ptr[2] * ptr[3] / 255)); + ptr += 4; + ptr2++; + } + } + else + { + for (x = 0; x < ilp->opts.rw; x++) + { + /* Conversion from CMYK to RGB is done in 2 steps: */ + /* CMYK => CMY => RGB (see http://www.easyrgb.com/index.php?X=MATH) */ + /* after computation, if C, M, Y and K are between 0 and 1, we have: */ + /* R = (1 - C) * (1 - K) * 255 */ + /* G = (1 - M) * (1 - K) * 255 */ + /* B = (1 - Y) * (1 - K) * 255 */ + /* libjpeg stores CMYK values between 0 and 255, */ + /* so we replace C by C * 255 / 255, etc... and we obtain: */ + /* R = (255 - C) * (255 - K) / 255 */ + /* G = (255 - M) * (255 - K) / 255 */ + /* B = (255 - Y) * (255 - K) / 255 */ + /* with C, M, Y and K between 0 and 255. */ + *ptr2 = + (0xff000000) | + (((255 - ptr[0]) * (255 - ptr[3]) / 255) << 16) | + (((255 - ptr[1]) * (255 - ptr[3]) / 255) << 8) | + (((255 - ptr[2]) * (255 - ptr[3]) / 255)); + ptr += 4; + ptr2++; + } + } + ptr += (4 * (w - (ilp->opts.rx + ilp->opts.rw))); + } + else + ptr += (4 * w); + } + } + } + } + } + /* We handle then RGB with 3 components */ + else if (cinfo.output_components == 3) + { +/* + double t; + if (region) + { + // debug for now + printf("R| %p %5ix%5i %s: %5i %5i %5ix%5i - ", + ie, + ie->w, ie->h, + ie->file, + ilp->opts.rx, + ilp->opts.ry, + ilp->opts.rw, + ilp->opts.rh); + } + t = get_time(); + */ + for (i = 0; (int)i < cinfo.rec_outbuf_height; i++) + line[i] = data + (i * w * 3); + for (l = 0; l < h; l += cinfo.rec_outbuf_height) + { + jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height); + scans = cinfo.rec_outbuf_height; + if ((h - l) < scans) scans = h - l; + ptr = data; + if (!region) + { + for (y = 0; y < scans; y++) + { + for (x = 0; x < w; x++) + { + *ptr2 = ARGB_JOIN(0xff, ptr[0], ptr[1], ptr[2]); + ptr += 3; + ptr2++; + } + } + } + else + { + // if line # > region last line, break + // but not return immediately for rotation job + if (l >= (ilp->opts.ry + ilp->opts.rh)) + { + line_done = EINA_TRUE; + /* if rotation flag is set , we have to rotate image */ + goto done; + } + // else if scan block intersects region start or later + else if ((l + scans) > + (ilp->opts.ry)) + { + for (y = 0; y < scans; y++) + { + if (((y + l) >= ilp->opts.ry) && + ((y + l) < (ilp->opts.ry + ilp->opts.rh))) + { + ptr += (3 * ilp->opts.rx); + for (x = 0; x < ilp->opts.rw; x++) + { + *ptr2 = ARGB_JOIN(0xff, ptr[0], ptr[1], ptr[2]); + ptr += 3; + ptr2++; + } + ptr += (3 * (w - (ilp->opts.rx + ilp->opts.rw))); + } + else + ptr += (3 * w); + } + } + } + } +/* + t = get_time() - t; + printf("%3.3f\n", t); + */ + } + /* We finally handle RGB with 1 component */ + else if (cinfo.output_components == 1) + { + for (i = 0; (int)i < cinfo.rec_outbuf_height; i++) + line[i] = data + (i * w); + for (l = 0; l < h; l += cinfo.rec_outbuf_height) + { + jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height); + scans = cinfo.rec_outbuf_height; + if ((h - l) < scans) scans = h - l; + ptr = data; + if (!region) + { + for (y = 0; y < scans; y++) + { + for (x = 0; x < w; x++) + { + *ptr2 = ARGB_JOIN(0xff, ptr[0], ptr[0], ptr[0]); + ptr++; + ptr2++; + } + } + } + else + { + // if line # > region last line, break + if (l >= (ilp->opts.ry + ilp->opts.rh)) + { + line_done = EINA_TRUE; + /* if rotation flag is set , we have to rotate image */ + goto done; + /*jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = NONE; + return EINA_TRUE;*/ + } + // els if scan block intersects region start or later + else if ((l + scans) > + (ilp->opts.ry)) + { + for (y = 0; y < scans; y++) + { + if (((y + l) >= ilp->opts.ry) && + ((y + l) < (ilp->opts.ry + ilp->opts.rh))) + { + ptr += ilp->opts.rx; + for (x = 0; x < ilp->opts.rw; x++) + { + *ptr2 = ARGB_JOIN(0xff, ptr[0], ptr[0], ptr[0]); + ptr++; + ptr2++; + } + ptr += w - (ilp->opts.rx + ilp->opts.rw); + } + else + ptr += w; + } + } + } + } + } + /* if rotation operation need, rotate it */ +done: + + if (ilp->rotated) + { + DATA32 *data1, *data2, *to, *from; + int lx, ly, lw, lh, hw; + + if (change_wh) + { + tmp = ilp->w; + ilp->w = ilp->h; + ilp->h = tmp; + } + + lw = ilp->w; + lh = ilp->h; + hw = lw * lh; + + data1 = ilp->buffer; + + if (degree == 180) + { + DATA32 tmpd; + + data2 = data1 + (lh * lw) -1; + for (lx = (lw * lh) / 2; --lx >= 0;) + { + tmpd = *data1; + *data1 = *data2; + *data2 = tmpd; + data1++; + data2--; + } + } + else + { + data2 = NULL; + to = NULL; + if (ptr_rotate) data2 = ptr_rotate; + + if (degree == 90) + { + to = data1 + lw - 1; + hw = -hw - 1; + } + else if (degree == 270) + { + to = data1 + hw - lw; + lw = -lw; + hw = hw + 1; + } + + if (to) + { + from = data2; + for (lx = ilp->w; --lx >= 0;) + { + for (ly = ilp->h; --ly >= 0;) + { + *to = *from; + from++; + to += lw; + } + to += hw; + } + } + if (ptr_rotate) + { + free(ptr_rotate); + ptr_rotate = NULL; + } + } + if (region) + { + ilp->opts.rx = load_region_x; + ilp->opts.ry = load_region_y; + ilp->opts.rw = load_region_w; + ilp->opts.rh = load_region_h; + } + } + if (line_done) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = CSERVE2_NONE; + return EINA_FALSE; + } + /* end data decoding */ + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = CSERVE2_NONE; + return EINA_TRUE; +} + +Eina_Bool +evas_image_load_file_head_jpeg(Evas_Img_Load_Params *ilp, + const char *file, const char *key __UNUSED__, + int *error) +{ + Eina_File *f; + void *map; + Eina_Bool val = EINA_FALSE; + + f = eina_file_open(file, EINA_FALSE); + if (!f) + { + *error = CSERVE2_DOES_NOT_EXIST; + return EINA_FALSE; + } + map = eina_file_map_all(f, EINA_FILE_WILLNEED); + if (!map) + { + *error = CSERVE2_DOES_NOT_EXIST; + goto on_error; + } + + val = evas_image_load_file_head_jpeg_internal(ilp, + map, eina_file_size_get(f), + error); + + eina_file_map_free(f, map); + + on_error: + eina_file_close(f); + return val; +} + +static Eina_Bool +evas_image_load_file_data_jpeg(Evas_Img_Load_Params *ilp, + const char *file, const char *key __UNUSED__, + int *error) +{ + Eina_File *f; + void *map; + Eina_Bool val = EINA_FALSE; + + f = eina_file_open(file, EINA_FALSE); + if (!f) + { + *error = CSERVE2_DOES_NOT_EXIST; + return EINA_FALSE; + } + map = eina_file_map_all(f, EINA_FILE_WILLNEED); + if (!map) + { + *error = CSERVE2_DOES_NOT_EXIST; + goto on_error; + } + + val = evas_image_load_file_data_jpeg_internal(ilp, + map, eina_file_size_get(f), + error); + + eina_file_map_free(f, map); + + on_error: + eina_file_close(f); + return val; +} + +static Evas_Loader_Module_Api modapi = +{ + EVAS_CSERVE2_MODULE_API_VERSION, + "jpeg", + evas_image_load_file_head_jpeg, + evas_image_load_file_data_jpeg +}; + +static Eina_Bool +module_init(void) +{ + return evas_cserve2_loader_register(&modapi); +} + +static void +module_shutdown(void) +{ +} + +EINA_MODULE_INIT(module_init); +EINA_MODULE_SHUTDOWN(module_shutdown); diff --git a/src/bin/loaders/pmaps/Makefile.am b/src/bin/loaders/pmaps/Makefile.am new file mode 100644 index 0000000..1641041 --- /dev/null +++ b/src/bin/loaders/pmaps/Makefile.am @@ -0,0 +1,33 @@ + +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +-I. \ +-I$(top_srcdir)/src/lib \ +-I$(top_srcdir)/src/lib/include \ +-I$(top_srcdir)/src/lib/cserve2 \ +-I$(top_srcdir)/src/bin \ +@EINA_CFLAGS@ \ +@evas_image_loader_pmaps_cflags@ \ +@EVIL_CFLAGS@ + +if BUILD_LOADER_PMAPS +#if !EVAS_STATIC_BUILD_PMAPS + +pkgdir = $(libdir)/evas/cserve2/loaders/pmaps/$(MODULE_ARCH) +pkg_LTLIBRARIES = module.la + +module_la_SOURCES = evas_image_load_pmaps.c + +module_la_LIBADD = @evas_image_loader_pmaps_libs@ @EINA_LIBS@ @EVIL_LIBS@ +module_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -module -avoid-version +module_la_LIBTOOLFLAGS = --tag=disable-static + +#else + +#noinst_LTLIBRARIES = libevas_loader_pmaps.la +#libevas_loader_pmaps_la_SOURCES = evas_image_load_pmaps.c +#libevas_loader_pmaps_la_LIBADD = @evas_image_loader_pmaps_libs@ + +#endif +endif diff --git a/src/bin/loaders/pmaps/evas_image_load_pmaps.c b/src/bin/loaders/pmaps/evas_image_load_pmaps.c new file mode 100644 index 0000000..22a9791 --- /dev/null +++ b/src/bin/loaders/pmaps/evas_image_load_pmaps.c @@ -0,0 +1,573 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_EVIL +# include +#endif + +#include + +#include "evas_macros.h" + +#include "evas_cserve2.h" +#include "evas_cserve2_slave.h" + +#define FILE_BUFFER_SIZE 1024 * 32 +#define FILE_BUFFER_UNREAD_SIZE 16 + +/* The buffer to load pmaps images */ +typedef struct Pmaps_Buffer Pmaps_Buffer; + +struct Pmaps_Buffer +{ + Eina_File *file; + void *map; + size_t position; + + /* the buffer */ + DATA8 buffer[FILE_BUFFER_SIZE]; + DATA8 unread[FILE_BUFFER_UNREAD_SIZE]; + DATA8 *current; + DATA8 *end; + char type[3]; + unsigned char unread_len:7; + unsigned char last_buffer:1; + + /* image properties */ + int w; + int h; + int max; + + /* interface */ + int (*int_get) (Pmaps_Buffer *b, int *val); + int (*color_get) (Pmaps_Buffer *b, DATA32 *color); +}; + +/* internal used functions */ +static Eina_Bool pmaps_buffer_open(Pmaps_Buffer *b, const char *filename, int *error); +static void pmaps_buffer_close(Pmaps_Buffer *b); +static Eina_Bool pmaps_buffer_header_parse(Pmaps_Buffer *b, int *error); +static int pmaps_buffer_plain_int_get(Pmaps_Buffer *b, int *val); +static int pmaps_buffer_1byte_int_get(Pmaps_Buffer *b, int *val); +static int pmaps_buffer_2byte_int_get(Pmaps_Buffer *b, int *val); +static int pmaps_buffer_gray_get(Pmaps_Buffer *b, DATA32 *color); +static int pmaps_buffer_rgb_get(Pmaps_Buffer *b, DATA32 *color); +static int pmaps_buffer_plain_bw_get(Pmaps_Buffer *b, DATA32 *color); + +static size_t pmaps_buffer_plain_update(Pmaps_Buffer *b); +static size_t pmaps_buffer_raw_update(Pmaps_Buffer *b); +static int pmaps_buffer_comment_skip(Pmaps_Buffer *b); + +static Eina_Bool +evas_image_load_file_head_pmaps(Evas_Img_Load_Params *ilp, const char *file, const char *key __UNUSED__, int *error) +{ + Pmaps_Buffer b; + + if (!pmaps_buffer_open(&b, file, error)) + { + pmaps_buffer_close(&b); + return EINA_FALSE; + } + + if (!pmaps_buffer_header_parse(&b, error)) + { + pmaps_buffer_close(&b); + return EINA_FALSE; + } + + ilp->w = b.w; + ilp->h = b.h; + + pmaps_buffer_close(&b); + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; +} + +static Eina_Bool +evas_image_load_file_data_pmaps(Evas_Img_Load_Params *ilp, const char *file, const char *key __UNUSED__, int *error) +{ + Pmaps_Buffer b; + int pixels; + DATA32 *ptr; + + if (!pmaps_buffer_open(&b, file, error)) + { + pmaps_buffer_close(&b); + return EINA_FALSE; + } + + if (!pmaps_buffer_header_parse(&b, error)) + { + pmaps_buffer_close(&b); + return EINA_FALSE; + } + + pixels = b.w * b.h; + + ptr = ilp->buffer; + if (!ptr) + { + pmaps_buffer_close(&b); + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + + if (b.type[1] != '4') + { + while (pixels > 0 && b.color_get(&b, ptr)) + { + pixels--; + ptr++; + } + } + else + { + while (pixels > 0 + && (b.current != b.end || pmaps_buffer_raw_update(&b))) + { + int i; + + for (i = 7; i >= 0 && pixels > 0; i--) + { + if (*b.current & (1 << i)) + *ptr = 0xff000000; + else + *ptr = 0xffffffff; + ptr++; + pixels--; + } + b.current++; + } + } + + /* if there are some pix missing, give them a proper default */ + memset(ptr, 0xff, 4 * pixels); + pmaps_buffer_close(&b); + + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; +} + +/* internal used functions */ +static Eina_Bool +pmaps_buffer_open(Pmaps_Buffer *b, const char *filename, int *error) +{ + size_t len; + + b->file = eina_file_open(filename, EINA_FALSE); + if (!b->file) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return EINA_FALSE; + } + + b->map = eina_file_map_all(b->file, EINA_FILE_SEQUENTIAL); + if (!b->map) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + eina_file_close(b->file); + b->file = NULL; + return EINA_FALSE; + } + + b->position = 0; + *b->buffer = 0; + *b->unread = 0; + b->last_buffer = 0; + b->unread_len = 0; + + len = pmaps_buffer_plain_update(b); + + if (len < 3) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + eina_file_map_free(b->file, b->map); + eina_file_close(b->file); + b->map = NULL; + b->file = NULL; + return EINA_FALSE; + } + + /* copy the type */ + b->type[0] = b->buffer[0]; + b->type[1] = b->buffer[1]; + b->type[2] = 0; + /* skip the PX */ + b->current = b->buffer + 2; + + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; +} + +static void +pmaps_buffer_close(Pmaps_Buffer *b) +{ + if (b->file) + { + if (b->map) eina_file_map_free(b->file, b->map); + b->map = NULL; + eina_file_close(b->file); + b->file = NULL; + } +} + +static Eina_Bool +pmaps_buffer_header_parse(Pmaps_Buffer *b, int *error) +{ + /* if there is no P at the beginning it is not a file we can parse */ + if (b->type[0] != 'P') + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + + /* get the width */ + if (!pmaps_buffer_plain_int_get(b, &(b->w)) || b->w < 1) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + + /* get the height */ + if (!pmaps_buffer_plain_int_get(b, &(b->h)) || b->h < 1) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + + /* get the maximum value. P1 and P4 don't have a maximum value. */ + if (!(b->type[1] == '1' || b->type[1] == '4') + && (!pmaps_buffer_plain_int_get(b, &(b->max)) || b->max < 1)) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + + /* set up the color get callback */ + switch (b->type[1]) + { + /* Black and White */ + case '1': + b->color_get = pmaps_buffer_plain_bw_get; + break; + case '4': + /* Binary black and white use another format */ + b->color_get = NULL; + break; + case '2': + case '5': + b->color_get = pmaps_buffer_gray_get; + break; + case '3': + case '6': + b->color_get = pmaps_buffer_rgb_get; + break; + case '7': + /* XXX write me */ + return 0; + break; + default: + return 0; + } + /* set up the int get callback */ + switch (b->type[1]) + { + /* RAW */ + case '5': + case '6': + if (b->max < 256) + b->int_get = pmaps_buffer_1byte_int_get; + else + b->int_get = pmaps_buffer_2byte_int_get; + + if (b->current == b->end && !pmaps_buffer_raw_update(b)) + return 0; + + b->current++; + break; + /* Plain */ + case '2': + case '3': + b->int_get = pmaps_buffer_plain_int_get; + break; + /* Black and White Bitmaps don't use that callback */ + case '1': + case '4': + b->int_get = NULL; + /* we need to skip the next character fpr P4 it + * doesn't hurt if we do it for the P1 as well */ + b->current++; + break; + } + return 1; +} + +static size_t +pmaps_buffer_plain_update(Pmaps_Buffer *b) +{ + size_t r; + size_t max; + + /* if we already are in the last buffer we can not update it */ + if (b->last_buffer) + return 0; + + /* if we have unread bytes we need to put them before the new read + * stuff */ + if (b->unread_len) + memcpy(b->buffer, b->unread, b->unread_len); + + max = FILE_BUFFER_SIZE - b->unread_len - 1; + if (b->position + max > eina_file_size_get(b->file)) + max = eina_file_size_get(b->file) - b->position; + + memcpy(&b->buffer[b->unread_len], b->map + b->position, max); + b->position += max; + r = max + b->unread_len; + + /* we haven't read anything nor have we bytes in the unread buffer */ + if (r == 0) + { + b->buffer[0] = '\0'; + b->end = b->buffer; + b->last_buffer = 1; + return 0; + } + + if (r < FILE_BUFFER_SIZE - 1) + { + /*we reached eof */ ; + b->last_buffer = 1; + } + + b->buffer[r] = 0; + + b->unread[0] = '\0'; + b->unread_len = 0; + + b->buffer[r] = '\0'; + b->current = b->buffer; + b->end = b->buffer + r; + + return r; +} + +static size_t +pmaps_buffer_raw_update(Pmaps_Buffer *b) +{ + size_t r; + size_t max; + + if (b->last_buffer) + return 0; + + if (b->unread_len) + memcpy(b->buffer, b->unread, b->unread_len); + + max = FILE_BUFFER_SIZE - b->unread_len; + if (b->position + max > eina_file_size_get(b->file)) + max = eina_file_size_get(b->file) - b->position; + + memcpy(&b->buffer[b->unread_len], b->map + b->position, max); + b->position += max; + r = max + b->unread_len; + + if (r < FILE_BUFFER_SIZE) + { + /*we reached eof */ + b->last_buffer = 1; + } + + b->end = b->buffer + r; + b->current = b->buffer; + + if (b->unread_len) + { + /* the buffer is now read */ + *b->unread = 0; + b->unread_len = 0; + } + + return r; +} + +static int +pmaps_buffer_plain_int_get(Pmaps_Buffer *b, int *val) +{ + char *start; + DATA8 lastc; + + /* first skip all white space + * Note: we are skipping here actually every character than is not + * a digit */ + while (!isdigit(*b->current)) + { + if (*b->current == '\0') + { + if (!pmaps_buffer_plain_update(b)) + return 0; + + continue; + } + if (*b->current == '#' && !pmaps_buffer_comment_skip(b)) + return 0; + b->current++; + } + + start = (char *)b->current; + /* now find the end of the number */ + while (isdigit(*b->current)) + b->current++; + + lastc = *b->current; + *b->current = '\0'; + *val = atoi(start); + *b->current = lastc; + + return 1; +} + +static int +pmaps_buffer_1byte_int_get(Pmaps_Buffer *b, int *val) +{ + /* are we at the end of the buffer? */ + if (b->current == b->end && !pmaps_buffer_raw_update(b)) + return 0; + + *val = *b->current; + b->current++; + + return 1; +} +static int +pmaps_buffer_2byte_int_get(Pmaps_Buffer *b, int *val) +{ + /* are we at the end of the buffer? */ + if (b->current == b->end && !pmaps_buffer_raw_update(b)) + return 0; + + *val = (int)(*b->current << 8); + b->current++; + + /* are we at the end of the buffer? */ + if (b->current == b->end && !pmaps_buffer_raw_update(b)) + return 0; + + *val |= *b->current; + b->current++; + + return 1; +} + +static int +pmaps_buffer_comment_skip(Pmaps_Buffer *b) +{ + while (*b->current != '\n') + { + if (*b->current == '\0') + { + if (!pmaps_buffer_plain_update(b)) + return 0; + + continue; + } + b->current++; + } + return 1; +} + +static int +pmaps_buffer_rgb_get(Pmaps_Buffer *b, DATA32 *color) +{ + int vr, vg, vb; + + if (!b->int_get(b, &vr) || !b->int_get(b, &vg) || !b->int_get(b, &vb)) + return 0; + + if (b->max != 255) + { + vr = (vr * 255) / b->max; + vg = (vg * 255) / b->max; + vb = (vb * 255) / b->max; + } + if (vr > 255) + vr = 255; + if (vg > 255) + vg = 255; + if (vb > 255) + vb = 255; + + *color = ARGB_JOIN(0xff, vr, vg, vb); + + return 1; +} + +static int +pmaps_buffer_gray_get(Pmaps_Buffer *b, DATA32 *color) +{ + int val; + + if (!b->int_get(b, &val)) + return 0; + + if (b->max != 255) + val = (val * 255) / b->max; + if (val > 255) + val = 255; + *color = ARGB_JOIN(0xff, val, val, val); + + return 1; +} + +static int +pmaps_buffer_plain_bw_get(Pmaps_Buffer *b, DATA32 *val) +{ + /* first skip all white space + * Note: we are skipping here actually every character than is not + * a digit */ + while (!isdigit(*b->current)) + { + if (*b->current == '\0') + { + if (!pmaps_buffer_raw_update(b)) + return 0; + + continue; + } + if (*b->current == '#' && !pmaps_buffer_comment_skip(b)) + return 0; + b->current++; + } + + if (*b->current == '0') + *val = 0xffffffff; + else + *val = 0xff000000; + + b->current++; + + return 1; +} + +static Evas_Loader_Module_Api modapi = +{ + EVAS_CSERVE2_MODULE_API_VERSION, + "pmaps", + evas_image_load_file_head_pmaps, + evas_image_load_file_data_pmaps +}; + +static Eina_Bool +module_init(void) +{ + return evas_cserve2_loader_register(&modapi); +} + +static void +module_shutdown(void) +{ +} + +EINA_MODULE_INIT(module_init); +EINA_MODULE_SHUTDOWN(module_shutdown); diff --git a/src/bin/loaders/png/Makefile.am b/src/bin/loaders/png/Makefile.am new file mode 100644 index 0000000..e99660c --- /dev/null +++ b/src/bin/loaders/png/Makefile.am @@ -0,0 +1,33 @@ + +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +-I. \ +-I$(top_srcdir)/src/lib \ +-I$(top_srcdir)/src/lib/include \ +-I$(top_srcdir)/src/lib/cserve2 \ +-I$(top_srcdir)/src/bin \ +@EINA_CFLAGS@ \ +@evas_image_loader_png_cflags@ \ +@EVIL_CFLAGS@ + +if BUILD_LOADER_PNG +#if !EVAS_STATIC_BUILD_PNG + +pkgdir = $(libdir)/evas/cserve2/loaders/png/$(MODULE_ARCH) +pkg_LTLIBRARIES = module.la + +module_la_SOURCES = evas_image_load_png.c + +module_la_LIBADD = @EINA_LIBS@ @evas_image_loader_png_libs@ @EVIL_LIBS@ +module_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -module -avoid-version +module_la_LIBTOOLFLAGS = --tag=disable-static + +#else + +#noinst_LTLIBRARIES = libevas_loader_png.la +#libevas_loader_png_la_SOURCES = evas_image_load_png.c +#libevas_loader_png_la_LIBADD = @evas_image_loader_png_libs@ + +#endif +endif diff --git a/src/bin/loaders/png/evas_image_load_png.c b/src/bin/loaders/png/evas_image_load_png.c new file mode 100644 index 0000000..544fc6b --- /dev/null +++ b/src/bin/loaders/png/evas_image_load_png.c @@ -0,0 +1,310 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#ifdef HAVE_EVIL +# include +#endif + +#ifdef _WIN32_WCE +# define E_FOPEN(file, mode) evil_fopen_native((file), (mode)) +# define E_FREAD(buffer, size, count, stream) evil_fread_native(buffer, size, count, stream) +# define E_FCLOSE(stream) evil_fclose_native(stream) +#else +# define E_FOPEN(file, mode) fopen((file), (mode)) +# define E_FREAD(buffer, size, count, stream) fread(buffer, size, count, stream) +# define E_FCLOSE(stream) fclose(stream) +#endif + +#include "evas_macros.h" + +#include "evas_cserve2.h" +#include "evas_cserve2_slave.h" + +#define PNG_BYTES_TO_CHECK 4 + + +static Eina_Bool +evas_image_load_file_head_png(Evas_Img_Load_Params *ilp, const char *file, const char *key __UNUSED__, int *error) +{ + png_uint_32 w32, h32; + FILE *f; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + int bit_depth, color_type, interlace_type; + unsigned char buf[PNG_BYTES_TO_CHECK]; + char hasa; + + hasa = 0; + f = E_FOPEN(file, "rb"); + if (!f) + { + *error = CSERVE2_DOES_NOT_EXIST; + return EINA_FALSE; + } + + /* if we havent read the header before, set the header data */ + if (E_FREAD(buf, PNG_BYTES_TO_CHECK, 1, f) != 1) + { + *error = CSERVE2_UNKNOWN_FORMAT; + goto close_file; + } + + if (png_sig_cmp(buf, 0, PNG_BYTES_TO_CHECK)) + { + *error = CSERVE2_UNKNOWN_FORMAT; + goto close_file; + } + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + { + *error = CSERVE2_RESOURCE_ALLOCATION_FAILED; + goto close_file; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, NULL, NULL); + *error = CSERVE2_RESOURCE_ALLOCATION_FAILED; + goto close_file; + } + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + *error = CSERVE2_CORRUPT_FILE; + goto close_file; + } + png_init_io(png_ptr, f); + png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); + png_read_info(png_ptr, info_ptr); + png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *) (&w32), + (png_uint_32 *) (&h32), &bit_depth, &color_type, + &interlace_type, NULL, NULL); + if ((w32 < 1) || (h32 < 1) || (w32 > IMG_MAX_SIZE) || (h32 > IMG_MAX_SIZE) || + IMG_TOO_BIG(w32, h32)) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + if (IMG_TOO_BIG(w32, h32)) + *error = CSERVE2_RESOURCE_ALLOCATION_FAILED; + else + *error = CSERVE2_GENERIC; + goto close_file; + } + if (ilp->opts.scale_down_by > 1) + { + ilp->w = (int) w32 / ilp->opts.scale_down_by; + ilp->h = (int) h32 / ilp->opts.scale_down_by; + if ((ilp->w < 1) || (ilp->h < 1)) + { + *error = CSERVE2_GENERIC; + goto close_file; + } + } + else + { + ilp->w = (int) w32; + ilp->h = (int) h32; + } + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) hasa = 1; + if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) hasa = 1; + if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) hasa = 1; + if (hasa) ilp->alpha = 1; + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + E_FCLOSE(f); + + *error = CSERVE2_NONE; + return EINA_TRUE; + + close_file: + E_FCLOSE(f); + return EINA_FALSE; +} + +static Eina_Bool +evas_image_load_file_data_png(Evas_Img_Load_Params *ilp, const char *file, const char *key __UNUSED__, int *error) +{ + unsigned char *surface; + png_uint_32 w32, h32; + int w, h; + FILE *f; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + int bit_depth, color_type, interlace_type; + unsigned char buf[PNG_BYTES_TO_CHECK]; + unsigned char **lines; + char hasa; + int i, j; + int scale_ratio = 1, image_w = 0; + unsigned char *tmp_line; + DATA32 *src_ptr, *dst_ptr; + + hasa = 0; + f = E_FOPEN(file, "rb"); + if (!f) + { + *error = CSERVE2_DOES_NOT_EXIST; + return EINA_FALSE; + } + + /* if we havent read the header before, set the header data */ + if (E_FREAD(buf, PNG_BYTES_TO_CHECK, 1, f) != 1) + { + *error = CSERVE2_CORRUPT_FILE; + goto close_file; + } + if (png_sig_cmp(buf, 0, PNG_BYTES_TO_CHECK)) + { + *error = CSERVE2_CORRUPT_FILE; + goto close_file; + } + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + { + *error = CSERVE2_RESOURCE_ALLOCATION_FAILED; + goto close_file; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, NULL, NULL); + *error = CSERVE2_RESOURCE_ALLOCATION_FAILED; + goto close_file; + } + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + *error = CSERVE2_CORRUPT_FILE; + goto close_file; + } + png_init_io(png_ptr, f); + png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); + png_read_info(png_ptr, info_ptr); + png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *) (&w32), + (png_uint_32 *) (&h32), &bit_depth, &color_type, + &interlace_type, NULL, NULL); + image_w = w32; + if (ilp->opts.scale_down_by > 1) + { + scale_ratio = ilp->opts.scale_down_by; + w32 /= scale_ratio; + h32 /= scale_ratio; + } + surface = (unsigned char *) ilp->buffer; + if (!surface) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + *error = CSERVE2_RESOURCE_ALLOCATION_FAILED; + goto close_file; + } + + if ((w32 != ilp->w) || (h32 != ilp->h)) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + *error = CSERVE2_GENERIC; + goto close_file; + } + + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) hasa = 1; + if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) hasa = 1; + if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) hasa = 1; + if (hasa) ilp->alpha = 1; + + /* Prep for transformations... ultimately we want ARGB */ + /* expand palette -> RGB if necessary */ + if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png_ptr); + /* expand gray (w/reduced bits) -> 8-bit RGB if necessary */ + if ((color_type == PNG_COLOR_TYPE_GRAY) || + (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) + { + png_set_gray_to_rgb(png_ptr); + if (bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png_ptr); + } + /* expand transparency entry -> alpha channel if present */ + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(png_ptr); + /* reduce 16bit color -> 8bit color if necessary */ + if (bit_depth > 8) png_set_strip_16(png_ptr); + /* pack all pixels to byte boundaries */ + png_set_packing(png_ptr); + + w = ilp->w; + h = ilp->h; + /* we want ARGB */ +#ifdef WORDS_BIGENDIAN + png_set_swap_alpha(png_ptr); + if (!hasa) png_set_filler(png_ptr, 0xff, PNG_FILLER_BEFORE); +#else + png_set_bgr(png_ptr); + if (!hasa) png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); +#endif + + /* we read image line by line if scale down was set */ + if (scale_ratio == 1) + { + lines = (unsigned char **) alloca(h * sizeof(unsigned char *)); + for (i = 0; i < h; i++) + lines[i] = surface + (i * w * sizeof(DATA32)); + png_read_image(png_ptr, lines); + png_read_end(png_ptr, info_ptr); + } + else + { + tmp_line = (unsigned char *) alloca(image_w * sizeof(DATA32)); + dst_ptr = (DATA32 *)surface; + for (i = 0; i < h; i++) + { + png_read_row(png_ptr, tmp_line, NULL); + src_ptr = (DATA32 *)tmp_line; + for (j = 0; j < w; j++) + { + *dst_ptr = *src_ptr; + dst_ptr++; + src_ptr += scale_ratio; + } + for (j = 0; j < (scale_ratio - 1); j++) + { + png_read_row(png_ptr, tmp_line, NULL); + } + } + } + + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + E_FCLOSE(f); + evas_cserve2_image_premul(ilp); + + *error = CSERVE2_NONE; + return EINA_TRUE; + + close_file: + E_FCLOSE(f); + return EINA_FALSE; +} + +static Evas_Loader_Module_Api modapi = +{ + EVAS_CSERVE2_MODULE_API_VERSION, + "png", + evas_image_load_file_head_png, + evas_image_load_file_data_png +}; + +static Eina_Bool +module_init(void) +{ + return evas_cserve2_loader_register(&modapi); +} + +static void +module_shutdown(void) +{ +} + +EINA_MODULE_INIT(module_init); +EINA_MODULE_SHUTDOWN(module_shutdown); diff --git a/src/bin/loaders/psd/Makefile.am b/src/bin/loaders/psd/Makefile.am new file mode 100644 index 0000000..55a3ab2 --- /dev/null +++ b/src/bin/loaders/psd/Makefile.am @@ -0,0 +1,32 @@ + +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +-I. \ +-I$(top_srcdir)/src/lib \ +-I$(top_srcdir)/src/lib/include \ +-I$(top_srcdir)/src/lib/cserve2 \ +-I$(top_srcdir)/src/bin \ +@EINA_CFLAGS@ \ +@EVIL_CFLAGS@ + +if BUILD_LOADER_PSD +#if !EVAS_STATIC_BUILD_PSD + +pkgdir = $(libdir)/evas/cserve2/loaders/psd/$(MODULE_ARCH) +pkg_LTLIBRARIES = module.la + +module_la_SOURCES = evas_image_load_psd.c + +module_la_LIBADD = @EINA_LIBS@ @EVIL_LIBS@ +module_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -module -avoid-version +module_la_LIBTOOLFLAGS = --tag=disable-static + +#else + +#noinst_LTLIBRARIES = libevas_loader_psd.la +#libevas_loader_psd_la_SOURCES = evas_image_load_psd.c +#libevas_loader_psd_la_LIBADD = + +#endif +endif diff --git a/src/bin/loaders/psd/evas_image_load_psd.c b/src/bin/loaders/psd/evas_image_load_psd.c new file mode 100644 index 0000000..30a3117 --- /dev/null +++ b/src/bin/loaders/psd/evas_image_load_psd.c @@ -0,0 +1,981 @@ +#define _XOPEN_SOURCE + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef HAVE_NETINET_IN_H +# include +#endif + +#ifdef HAVE_UNISTD_H +# include +#endif + +#ifdef HAVE_EVIL +# include +#endif + +#include "evas_macros.h" + +#include "evas_cserve2.h" +#include "evas_cserve2_slave.h" + +typedef struct _PSD_Header PSD_Header; + +typedef enum _PSD_Mode + { + PSD_GREYSCALE = 1, + PSD_INDEXED = 2, + PSD_RGB = 3, + PSD_CMYK = 4 + } PSD_Mode; + +struct _PSD_Header +{ + unsigned char signature[4]; + unsigned short version; + unsigned char reserved[9]; + unsigned short channels; + unsigned int height; + unsigned int width; + unsigned short depth; + + unsigned short channel_num; + + PSD_Mode mode; +}; + +enum { + READ_COMPRESSED_SUCCESS, + READ_COMPRESSED_ERROR_FILE_CORRUPT, + READ_COMPRESSED_ERROR_FILE_READ_ERROR +}; + +static Eina_Bool get_compressed_channels_length(PSD_Header *Head, + const unsigned char *map, size_t length, size_t *position, + unsigned short *rle_table, + unsigned int *chanlen); + +static int +read_ushort(const unsigned char *map, size_t length, size_t *position, unsigned short *ret) +{ + if (*position + 2 > length) return 0; + // FIXME: need to check order + *ret = (map[(*position) + 0] << 8) | map[(*position) + 1]; + *position += 2; + return 1; +} + +static int +read_uint(const unsigned char *map, size_t length, size_t *position, unsigned int *ret) +{ + if (*position + 4 > length) return 0; + // FIXME: need to check order + *ret = ARGB_JOIN(map[(*position) + 0], map[(*position) + 1], map[(*position) + 2], map[(*position) + 3]); + *position += 4; + return 1; +} + +static int +read_block(const unsigned char *map, size_t length, size_t *position, void *target, size_t size) +{ + if (*position + size > length) return 0; + memcpy(target, map + *position, size); + *position += size; + return 1; +} + +// Internal function used to get the Psd header from the current file. +Eina_Bool +psd_get_header(PSD_Header *header, const unsigned char *map, size_t length, size_t *position) +{ + unsigned short tmp; + +#define CHECK_RET(Call) \ + if (!Call) return EINA_FALSE; + + CHECK_RET(read_block(map, length, position, header->signature, 4)); + CHECK_RET(read_ushort(map, length, position, &header->version)); + CHECK_RET(read_block(map, length, position, header->reserved, 6)); + CHECK_RET(read_ushort(map, length, position, &header->channels)); + CHECK_RET(read_uint(map, length, position, &header->height)); + CHECK_RET(read_uint(map, length, position, &header->width)); + CHECK_RET(read_ushort(map, length, position, &header->depth)); + + CHECK_RET(read_ushort(map, length, position, &tmp)); + header->mode = tmp; + +#undef CHECK_RET + + /* fprintf(stderr, "<<<<<<<<<<<\nsignature : %c%c%c%c\n", */ + /* header->signature[0], */ + /* header->signature[1], */ + /* header->signature[2], */ + /* header->signature[3]); */ + /* fprintf(stderr, "version : %i\n", header->version); */ + /* fprintf(stderr, "channels : %i\n", header->channels); */ + /* fprintf(stderr, "width x height : %dx%d\n", header->width, header->height); */ + /* fprintf(stderr, "depth : %i\n", header->depth); */ + /* fprintf(stderr, "mode : %i\n>>>>>>>>>>>>\n", header->mode); */ + + return EINA_TRUE; +} + + +// Internal function used to check if the HEADER is a valid Psd header. +Eina_Bool +is_psd(PSD_Header *header) +{ + if (strncmp((char*)header->signature, "8BPS", 4)) + return EINA_FALSE; + if (header->version != 1) + return EINA_FALSE; + if (header->channels < 1 || header->channels > 24) + return EINA_FALSE; + if (header->height < 1 || header->width < 1) + return EINA_FALSE; + if (header->depth != 1 && header->depth != 8 && header->depth != 16) + return EINA_FALSE; + + return EINA_TRUE; +} + +static Eina_Bool +evas_image_load_file_head_psd(Evas_Img_Load_Params *ilp, const char *FileName, + const char *key __UNUSED__, int *error) +{ + Eina_File *f; + void *map; + size_t length; + size_t position; + PSD_Header header; + Eina_Bool correct; + + *error = EVAS_LOAD_ERROR_NONE; + + f = eina_file_open(FileName, 0); + if (f == NULL) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return EINA_FALSE; + } + + map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + length = eina_file_size_get(f); + position = 0; + if (!map || length < 1) + { + eina_file_close(f); + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + correct = psd_get_header(&header, map, length, &position); + + eina_file_map_free(f, map); + eina_file_close(f); + + if (!correct || !is_psd(&header)) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + + ilp->w = header.width; + ilp->h = header.height; + if (header.channels == 3) ilp->alpha = 0; + else ilp->alpha = 1; + + return EINA_TRUE; +} + +static unsigned int +read_compressed_channel(const unsigned char *map, size_t length, size_t *position, + const unsigned int channel_length __UNUSED__, + unsigned int size, + unsigned char* channel) +{ + // FIXME: what does channel_length means, and why is it not used + unsigned int i; + char headbyte, c; + +#define CHECK_RET(Call) \ + if (!Call) return READ_COMPRESSED_ERROR_FILE_READ_ERROR; \ + + for (i = 0; i < size; ) + { + CHECK_RET(read_block(map, length, position, &headbyte, 1)); + + if (headbyte >= 0) + { + if (i + headbyte > size) + return READ_COMPRESSED_ERROR_FILE_CORRUPT; + CHECK_RET(read_block(map, length, position, channel + i, headbyte + 1)); + + i += headbyte + 1; + } + else if (headbyte >= -127 && headbyte <= -1) + { + int run; + + CHECK_RET(read_block(map, length, position, &c, 1)); + + run = c; + /* if (run == -1) */ + /* return READ_COMPRESSED_ERROR_FILE_READ_ERROR; */ + + if (i + (-headbyte + 1) > size) + return READ_COMPRESSED_ERROR_FILE_CORRUPT; + + memset(channel + i, run, -headbyte + 1); + i += -headbyte + 1; + } + } + +#undef CHECK_RET + + return READ_COMPRESSED_SUCCESS; +} + + +static Eina_Bool +psd_get_data(PSD_Header *head, + const unsigned char *map, size_t length, size_t *position, + unsigned char *buffer, Eina_Bool compressed, + int *error) +{ + unsigned int c, x, y, numchan, bps, bpc, bpp; + unsigned int pixels_count; + unsigned char *channel = NULL; + unsigned char *data = NULL; + + // Added 01-07-2009: This is needed to correctly load greyscale and + // paletted images. + switch (head->mode) + { + case PSD_GREYSCALE: + case PSD_INDEXED: + numchan = 1; + break; + default: + numchan = 3; + } + + bpp = head->channels; + bpc = head->depth / 8; + pixels_count = head->width * head->height; + + data = malloc(sizeof (unsigned char) * pixels_count * bpp); + if (!data) return EINA_FALSE; + + channel = malloc(sizeof (unsigned char) * pixels_count * bpc); + if (!channel) + { + free(data); + return EINA_FALSE; + } + + bps = head->width * head->channels * bpc; + // @TODO: Add support for this in, though I have yet to run across a .psd + // file that uses this. + if (compressed && bpc == 2) + { + free(data); + free(channel); + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + +#define CHECK_RET(Call) \ + if (!Call) \ + { \ + free(data); \ + free(channel); \ + return EINA_FALSE; \ + } + + if (!compressed) + { + if (bpc == 1) + { + for (c = 0; c < numchan; c++) + { + unsigned char *tmp = channel; + + CHECK_RET(read_block(map, length, position, tmp, pixels_count)); + + for (y = 0; y < head->height * bps; y += bps) + { + for (x = 0; x < bps; x += bpp, tmp++) + { + data[y + x + c] = *tmp; + } + } + } + + // Accumulate any remaining channels into a single alpha channel + //@TODO: This needs to be changed for greyscale images. + for (; c < head->channels; c++) + { + unsigned char *tmp = channel; + + CHECK_RET(read_block(map, length, position, channel, pixels_count)); + + for (y = 0; y < head->height * bps; y += bps) + { + for (x = 0; x < bps; x += bpp, tmp++) + { + unsigned short newval; + + // previous formula was : (old / 255 * new / 255) * 255 + newval = (*tmp) * data[y + x + 3]; + + data[y + x + 3] = newval >> 8; + } + } + } + } + else + { + int bps2; + + bps2 = bps / 2; + + // iCurImage->Bpc == 2 + for (c = 0; c < numchan; c++) + { + unsigned short *shortptr = (unsigned short*) channel; + + CHECK_RET(read_block(map, length, position, channel, pixels_count * 2)); + + for (y = 0; y < head->height * bps2; y += bps2) + { + for (x = 0; x < (unsigned int)bps2; x += bpp, shortptr++) + { + ((unsigned short*)data)[y + x + c] = *shortptr; + } + } + } + + // Accumulate any remaining channels into a single alpha channel + //@TODO: This needs to be changed for greyscale images. + for (; c < head->channels; c++) + { + unsigned short *shortptr = (unsigned short*) channel; + + CHECK_RET(read_block(map, length, position, channel, pixels_count * 2)); + + for (y = 0; y < head->height * bps2; y += bps2) + { + for (x = 0; x < (unsigned int)bps2; x += bpp, shortptr) + { + unsigned int newval; + + newval = *shortptr * ((unsigned short*)data)[y + x + 3]; + + ((unsigned short*)data)[y + x + 3] = newval >> 16; + } + } + } + } + } + else + { + unsigned short *rle_table; + unsigned int *chanlen; + + rle_table = alloca(head->height * head->channel_num * sizeof (unsigned short)); + chanlen = alloca(head->channel_num * sizeof (unsigned int)); + if (!get_compressed_channels_length(head, map, length, position, rle_table, chanlen)) + goto file_read_error; + + for (c = 0; c < numchan; c++) + { + unsigned char *tmp = channel; + int err; + + err = read_compressed_channel(map, length, position, + chanlen[c], + pixels_count, + channel); + if (err == READ_COMPRESSED_ERROR_FILE_CORRUPT) + goto file_corrupt; + else if (err == READ_COMPRESSED_ERROR_FILE_READ_ERROR) + goto file_read_error; + + for (y = 0; y < head->height * bps; y += bps) + { + for (x = 0; x < bps; x += bpp, tmp++) + { + data[y + x + c] = *tmp; + } + } + } + + // Initialize the alpha channel to solid + //@TODO: This needs to be changed for greyscale images. + if (head->channels >= 4) + { + for (y = 0; y < head->height * bps; y += bps) + { + for (x = 0; x < bps; x += bpp) + { + data[y + x + 3] = 255; + } + } + + for (; c < head->channels; c++) + { + unsigned char *tmp = channel; + int err; + + err = read_compressed_channel(map, length, position, + chanlen[c], + pixels_count, + channel); + if (err == READ_COMPRESSED_ERROR_FILE_CORRUPT) + goto file_corrupt; + else if (err == READ_COMPRESSED_ERROR_FILE_READ_ERROR) + goto file_read_error; + + for (y = 0; y < head->height * bps; y += bps) + { + for (x = 0; x < bps; x += bpp, tmp++) + { + unsigned short newval; + + newval = *tmp * data[y + x + 3]; + + data[y + x + 3] = newval >> 8; + } + } + } + } + } + + if (bpp == 3) + { + for (x = 0; x < pixels_count; x++) + { + buffer[x * 4 + 0] = data[x * 3 + 2]; + buffer[x * 4 + 1] = data[x * 3 + 1]; + buffer[x * 4 + 2] = data[x * 3 + 0]; + buffer[x * 4 + 3] = 255; + } + } + else + { + // BRGA to RGBA + for (x= 0; x < pixels_count; x++) + { + buffer[x * 4 + 0] = data[x * 4 + 2]; + buffer[x * 4 + 1] = data[x * 4 + 1]; + buffer[x * 4 + 2] = data[x * 4 + 0]; + buffer[x * 4 + 3] = data[x * 4 + 3]; + } + } + + free(channel); + free(data); + return EINA_TRUE; + +#undef CHECK_RET + + file_corrupt: + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + + file_read_error: + free(channel); + free(data); + + return EINA_FALSE; +} + + +static Eina_Bool +get_single_channel(PSD_Header *head, + const unsigned char *map, size_t length, size_t *position, + unsigned char *buffer, + Eina_Bool compressed) +{ + unsigned int i, bpc; + char headbyte; + int c; + int pixels_count; + + bpc = (head->depth / 8); + pixels_count = head->width * head->height; + +#define CHECK_RET(Call) \ + if (!Call) return EINA_FALSE; + + if (!compressed) + { + if (bpc == 1) + { + CHECK_RET(read_block(map, length, position, buffer, pixels_count)); + } + else + { // Bpc == 2 + CHECK_RET(read_block(map, length, position, buffer, pixels_count * 2)); + } + } + else + { + for (i = 0; i < (unsigned int)pixels_count; ) + { + CHECK_RET(read_block(map, length, position, &headbyte, 1)); + + if (headbyte >= 0) + { // && HeadByte <= 127 + CHECK_RET(read_block(map, length, position, buffer + i, headbyte + 1)); + + i += headbyte + 1; + } + if (headbyte >= -127 && headbyte <= -1) + { + int run; + + CHECK_RET(read_block(map, length, position, &c, 1)); + + run = c; + if (run == -1) return EINA_FALSE; + + memset(buffer + i, run, -headbyte + 1); + i += -headbyte + 1; + } + } + } + +#undef CHECK_RET + + return EINA_TRUE; +} + +static Eina_Bool +read_psd_grey(Evas_Img_Load_Params *ilp, PSD_Header *head, const unsigned char *map, size_t length, size_t *position, int *error) +{ + unsigned int color_mode, resource_size, misc_info; + unsigned short compressed; + void *surface = NULL; + + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + +#define CHECK_RET(Call) \ + if (!Call) return EINA_FALSE; + + CHECK_RET(read_uint(map, length, position, &color_mode)); + // Skip over the 'color mode data section' + *position += color_mode; + + CHECK_RET(read_uint(map, length, position, &resource_size)); + // Read the 'image resources section' + *position += resource_size; + + CHECK_RET(read_uint(map, length, position, &misc_info)); + *position += misc_info; + + CHECK_RET(read_ushort(map, length, position, &compressed)); + + ilp->w = head->width; + ilp->h = head->height; + if (head->channels == 3) ilp->alpha = 0; + else ilp->alpha = 1; + + head->channel_num = head->channels; + // Temporary to read only one channel...some greyscale .psd files have 2. + head->channels = 1; + + switch (head->depth) + { + case 8: + case 16: + break; + default: + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + + surface = ilp->buffer; + if (!surface) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto cleanup_error; + } + + if (!psd_get_data(head, map, length, position, surface, compressed, error)) + goto cleanup_error; + + return EINA_TRUE; + +#undef CHECK_RET + + cleanup_error: + return EINA_FALSE; +} + + +Eina_Bool +read_psd_indexed(Evas_Img_Load_Params *ilp, PSD_Header *head, const unsigned char *map, size_t length, size_t *position, int *error) +{ + unsigned int color_mode, resource_size, misc_info; + unsigned short compressed; + void *surface; + + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + +#define CHECK_RET(Call) \ + if (!(Call)) return EINA_FALSE; + + CHECK_RET(read_uint(map, length, position, &color_mode)); + CHECK_RET(!(color_mode % 3)); + /* + Palette = (unsigned char*)malloc(Colormode); + if (Palette == NULL) + return EINA_FALSE; + if (fread(&Palette, 1, Colormode, file) != Colormode) + goto cleanup_error; + */ + // Skip over the 'color mode data section' + *position += color_mode; + + // Read the 'image resources section' + CHECK_RET(read_uint(map, length, position, &resource_size)); + *position += resource_size; + + CHECK_RET(read_uint(map, length, position, &misc_info)); + *position += misc_info; + + CHECK_RET(read_ushort(map, length, position, &compressed)); + + if (head->channels != 1 || head->depth != 8) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + head->channel_num = head->channels; + + ilp->w = head->width; + ilp->h = head->height; + if (head->channels == 3) ilp->alpha = 0; + else ilp->alpha = 1; + + surface = ilp->buffer; + if (!surface) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + + if (!psd_get_data(head, map, length, position, surface, compressed, error)) + return EINA_FALSE; + return EINA_TRUE; + +#undef CHECK_RET +} + +Eina_Bool +read_psd_rgb(Evas_Img_Load_Params *ilp, PSD_Header *head, const unsigned char *map, size_t length, size_t *position, int *error) +{ + unsigned int color_mode, resource_size, misc_info; + unsigned short compressed; + void *surface; + +#define CHECK_RET(Call) \ + if (!Call) return EINA_FALSE; + + CHECK_RET(read_uint(map, length, position, &color_mode)); + // Skip over the 'color mode data section' + *position += color_mode; + + // Read the 'image resources section' + CHECK_RET(read_uint(map, length, position, &resource_size)); + *position += resource_size; + + CHECK_RET(read_uint(map, length, position, &misc_info)); + *position += misc_info; + + CHECK_RET(read_ushort(map, length, position, &compressed)); + + head->channel_num = head->channels; + + switch (head->depth) + { + case 8: + case 16: + break; + default: + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + ilp->w = head->width; + ilp->h = head->height; + if (head->channels == 3) ilp->alpha = 0; + else ilp->alpha = 1; + + surface = ilp->buffer; + if (!surface) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto cleanup_error; + } + + if (!psd_get_data(head, map, length, position, surface, compressed, error)) + goto cleanup_error; + + evas_cserve2_image_premul(ilp); + return EINA_TRUE; + +#undef CHECK_RET + + cleanup_error: + return EINA_FALSE; +} + +Eina_Bool +read_psd_cmyk(Evas_Img_Load_Params *ilp, PSD_Header *head, const unsigned char *map, size_t length, size_t *position, int *error) +{ + unsigned int color_mode, resource_size, misc_info, size, j, data_size; + unsigned short compressed; + unsigned int format, type; + unsigned char *kchannel = NULL; + void *surface; + + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + +#define CHECK_RET(Call) \ + if (!Call) return EINA_FALSE; + + CHECK_RET(read_uint(map, length, position, &color_mode)); + // Skip over the 'color mode data section' + *position += color_mode; + + CHECK_RET(read_uint(map, length, position, &resource_size)); + // Read the 'image resources section' + *position += resource_size; + + CHECK_RET(read_uint(map, length, position, &misc_info)); + *position += misc_info; + + CHECK_RET(read_ushort(map, length, position, &compressed)); + + switch (head->channels) + { + case 4: + format = 0x1907; + head->channel_num = 4; + head->channels = 3; + break; + case 5: + format = 0x1908; + head->channel_num = 5; + head->channels = 4; + break; + default: + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + + switch (head->depth) + { + case 8: + type = 1; + break; + case 16: + type = 2; + break; + default: + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + + ilp->w = head->width; + ilp->h = head->height; + if (head->channels == 3) ilp->alpha = 0; + else ilp->alpha = 1; + + surface = ilp->buffer; + if (!surface) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto cleanup_error; + } + + if (!psd_get_data(head, map, length, position, surface, compressed, error)) + goto cleanup_error; + + size = type * ilp->w * ilp->h; + kchannel = malloc(size); + if (kchannel == NULL) + goto cleanup_error; + if (!get_single_channel(head, map, length, position, kchannel, compressed)) + goto cleanup_error; + + data_size = head->channels * type * ilp->w * ilp->h; + if (format == 0x1907) + { + unsigned char *tmp = surface; + const unsigned char *limit = tmp + data_size; + + for (j = 0; tmp < limit; tmp++, j++) + { + int k; + + for (k = 0; k < 3; k++) + *tmp = (*tmp * kchannel[j]) >> 8; + + // FIXME: tmp[i+3] = 255; + } + } + else + { // RGBA + unsigned char *tmp = surface; + const unsigned char *limit = tmp + data_size; + + // The KChannel array really holds the alpha channel on this one. + for (j = 0; tmp < limit; tmp += 4, j++) + { + tmp[0] = (tmp[0] * tmp[3]) >> 8; + tmp[1] = (tmp[1] * tmp[3]) >> 8; + tmp[2] = (tmp[2] * tmp[3]) >> 8; + tmp[3] = kchannel[j]; // Swap 'K' with alpha channel. + } + } + + free(kchannel); + + evas_cserve2_image_premul(ilp); + return EINA_TRUE; + + cleanup_error: + free(kchannel); + return EINA_FALSE; +} + +static Eina_Bool +evas_image_load_file_data_psd(Evas_Img_Load_Params *ilp, + const char *file, + const char *key __UNUSED__, + int *error) +{ + Eina_File *f; + void *map; + size_t length; + size_t position; + PSD_Header header; + Eina_Bool bpsd = EINA_FALSE; + + f = eina_file_open(file, 0); + if (f == NULL) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return bpsd; + } + + map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + length = eina_file_size_get(f); + position = 0; + if (!map || length < 1) + { + eina_file_close(f); + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + + if (!psd_get_header(&header, map, length, &position) || !is_psd(&header)) + { + eina_file_map_free(f, map); + eina_file_close(f); + *error = EVAS_LOAD_ERROR_GENERIC; + return EINA_FALSE; + } + + ilp->w = header.width; + ilp->h = header.height; + + *error = EVAS_LOAD_ERROR_NONE; + + switch (header.mode) + { + case PSD_GREYSCALE: // Greyscale + bpsd = read_psd_grey(ilp, &header, map, length, &position, error); + break; + case PSD_INDEXED: // Indexed + bpsd = read_psd_indexed(ilp, &header, map, length, &position, error); + break; + case PSD_RGB: // RGB + bpsd = read_psd_rgb(ilp, &header, map, length, &position, error); + break; + case PSD_CMYK: // CMYK + bpsd = read_psd_cmyk(ilp, &header, map, length, &position, error); + break; + default : + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + bpsd = EINA_FALSE; + } + + eina_file_map_free(f, map); + eina_file_close(f); + + return bpsd; +} + +static Eina_Bool +get_compressed_channels_length(PSD_Header *head, + const unsigned char *map, size_t length, size_t *position, + unsigned short *rle_table, + unsigned int *chanlen) +{ + unsigned int j; + unsigned int c; + + if (!read_block(map, length, position, rle_table, + sizeof (unsigned short) * head->height * head->channel_num)) + return EINA_FALSE; + + memset(chanlen, 0, head->channel_num * sizeof(unsigned int)); + for (c = 0; c < head->channel_num; c++) + { + unsigned int i; + + j = c * head->height; + for (i = 0; i < head->height; i++) + { + chanlen[c] += rle_table[i + j]; + } + } + + return EINA_TRUE; +} + +static Evas_Loader_Module_Api modapi = +{ + EVAS_CSERVE2_MODULE_API_VERSION, + "psd", + evas_image_load_file_head_psd, + evas_image_load_file_data_psd +}; + +static Eina_Bool +module_init(void) +{ + return evas_cserve2_loader_register(&modapi); +} + +static void +module_shutdown(void) +{ +} + +EINA_MODULE_INIT(module_init); +EINA_MODULE_SHUTDOWN(module_shutdown); diff --git a/src/bin/loaders/tga/Makefile.am b/src/bin/loaders/tga/Makefile.am new file mode 100644 index 0000000..7b813ca --- /dev/null +++ b/src/bin/loaders/tga/Makefile.am @@ -0,0 +1,32 @@ + +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +-I. \ +-I$(top_srcdir)/src/lib \ +-I$(top_srcdir)/src/lib/include \ +-I$(top_srcdir)/src/lib/cserve2 \ +-I$(top_srcdir)/src/bin \ +@EINA_CFLAGS@ \ +@EVIL_CFLAGS@ + +if BUILD_LOADER_TGA +#if !EVAS_STATIC_BUILD_TGA + +pkgdir = $(libdir)/evas/cserve2/loaders/tga/$(MODULE_ARCH) +pkg_LTLIBRARIES = module.la + +module_la_SOURCES = evas_image_load_tga.c + +module_la_LIBADD = @EINA_LIBS@ @EVIL_LIBS@ +module_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -module -avoid-version +module_la_LIBTOOLFLAGS = --tag=disable-static + +#else + +#noinst_LTLIBRARIES = libevas_loader_tga.la +#libevas_loader_tga_la_SOURCES = evas_image_load_tga.c +#libevas_loader_tga_la_LIBADD = + +#endif +endif diff --git a/src/bin/loaders/tga/evas_image_load_tga.c b/src/bin/loaders/tga/evas_image_load_tga.c new file mode 100644 index 0000000..4c7d5f1 --- /dev/null +++ b/src/bin/loaders/tga/evas_image_load_tga.c @@ -0,0 +1,574 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#ifdef HAVE_EVIL +# include +#endif + +#include "evas_macros.h" + +#include "evas_cserve2.h" +#include "evas_cserve2_slave.h" + +/* TGA pixel formats */ +#define TGA_TYPE_MAPPED 1 // handle +#define TGA_TYPE_COLOR 2 +#define TGA_TYPE_GRAY 3 +#define TGA_TYPE_MAPPED_RLE 9 // handle +#define TGA_TYPE_COLOR_RLE 10 +#define TGA_TYPE_GRAY_RLE 11 + +/* TGA header flags */ +#define TGA_DESC_ABITS 0x0f +#define TGA_DESC_HORIZONTAL 0x10 +#define TGA_DESC_VERTICAL 0x20 + +#define TGA_SIGNATURE "TRUEVISION-XFILE" + +typedef struct _tga_header tga_header; +typedef struct _tga_footer tga_footer; + +struct _tga_header +{ + unsigned char idLength; + unsigned char colorMapType; + unsigned char imageType; + unsigned char colorMapIndexLo, colorMapIndexHi; + unsigned char colorMapLengthLo, colorMapLengthHi; + unsigned char colorMapSize; + unsigned char xOriginLo, xOriginHi; + unsigned char yOriginLo, yOriginHi; + unsigned char widthLo, widthHi; + unsigned char heightLo, heightHi; + unsigned char bpp; + unsigned char descriptor; +} __attribute__((packed)); + +struct _tga_footer +{ + unsigned int extensionAreaOffset; + unsigned int developerDirectoryOffset; + char signature[16]; + char dot; + char null; +} __attribute__((packed)); + + +static Eina_Bool +evas_image_load_file_head_tga(Evas_Img_Load_Params *ilp, const char *file, const char *key __UNUSED__, int *error) +{ + Eina_File *f; + unsigned char *seg = NULL, *filedata; + tga_header *header; + tga_footer *footer, tfooter; + char hasa = 0; + int w = 0, h = 0, bpp; + int x, y; + + f = eina_file_open(file, EINA_FALSE); + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + if (f == NULL) return EINA_FALSE; + + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + if (eina_file_size_get(f) < (off_t)(sizeof(tga_header) + sizeof(tga_footer))) + goto close_file; + seg = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + if (seg == NULL) goto close_file; + filedata = seg; + + header = (tga_header *)filedata; + // no unaligned data accessed, so ok + footer = (tga_footer *)(filedata + (eina_file_size_get(f) - sizeof(tga_footer))); + memcpy((unsigned char *)(&tfooter), + (unsigned char *)footer, + sizeof(tga_footer)); + //printf("0\n"); + if (!memcmp(tfooter.signature, TGA_SIGNATURE, sizeof(tfooter.signature))) + { + if ((tfooter.dot == '.') && (tfooter.null == 0)) + { + // footer is there and matches. this is a tga file - any problems now + // are a corrupt file + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + } + } +// else goto close_file; + //printf("1\n"); + + filedata = (unsigned char *)filedata + sizeof(tga_header); + switch (header->imageType) + { + case TGA_TYPE_COLOR_RLE: + case TGA_TYPE_GRAY_RLE: +// rle = 1; + break; + case TGA_TYPE_COLOR: + case TGA_TYPE_GRAY: +// rle = 0; + break; + default: + goto close_file; + } + bpp = header->bpp; + if (!((bpp == 32) || (bpp == 24) || (bpp == 16) || (bpp == 8))) + goto close_file; + if ((bpp == 32) && (header->descriptor & TGA_DESC_ABITS)) hasa = 1; + // don't handle colormapped images + if ((header->colorMapType) != 0) + goto close_file; + // if colormap size is anything other than legal sizes or 0 - not real tga + if (!((header->colorMapSize == 0) || + (header->colorMapSize == 15) || + (header->colorMapSize == 16) || + (header->colorMapSize == 24) || + (header->colorMapSize == 32))) + goto close_file; + x = (header->xOriginHi << 8) | (header->xOriginLo); + y = (header->yOriginHi << 8) | (header->yOriginLo); + w = (header->widthHi << 8) | header->widthLo; + h = (header->heightHi << 8) | header->heightLo; + // x origin gerater that width, y origin greater than height - wrong file + if ((x >= w) || (y >= h)) + goto close_file; + // if descriptor has either of the top 2 bits set... not tga + if (header->descriptor & 0xc0) + goto close_file; + + if ((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE) || + IMG_TOO_BIG(w, h)) + goto close_file; + + ilp->w = w; + ilp->h = h; + if (hasa) ilp->alpha = 1; + + eina_file_map_free(f, seg); + eina_file_close(f); + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; + +close_file: + if (seg != NULL) eina_file_map_free(f, seg); + eina_file_close(f); + return EINA_FALSE; +} + +static Eina_Bool +evas_image_load_file_data_tga(Evas_Img_Load_Params *ilp, const char *file, const char *key __UNUSED__, int *error) +{ + Eina_File *f; + unsigned char *seg = NULL, *filedata; + tga_header *header; + tga_footer *footer, tfooter; + char hasa = 0, footer_present = 0, vinverted = 0, rle = 0; + int w = 0, h = 0, x, y, bpp; + off_t size; + unsigned int *surface, *dataptr; + unsigned int datasize; + unsigned char *bufptr, *bufend; + int abits; + + f = eina_file_open(file, EINA_FALSE); + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + if (f == NULL) return EINA_FALSE; + + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + if (eina_file_size_get(f) < (off_t)(sizeof(tga_header) + sizeof(tga_footer))) + goto close_file; + seg = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + if (seg == NULL) goto close_file; + filedata = seg; + size = eina_file_size_get(f); + + header = (tga_header *)filedata; + // no unaligned data accessed, so ok + footer = (tga_footer *)(filedata + (size - sizeof(tga_footer))); + memcpy((unsigned char *)&tfooter, + (unsigned char *)footer, + sizeof(tga_footer)); + if (!memcmp(tfooter.signature, TGA_SIGNATURE, sizeof(tfooter.signature))) + { + if ((tfooter.dot == '.') && (tfooter.null == 0)) + { + // footer is there and matches. this is a tga file - any problems now + // are a corrupt file + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + footer_present = 1; + } + } + + filedata = (unsigned char *)filedata + sizeof(tga_header); + vinverted = !(header->descriptor & TGA_DESC_VERTICAL); + switch (header->imageType) + { + case TGA_TYPE_COLOR_RLE: + case TGA_TYPE_GRAY_RLE: + rle = 1; + break; + case TGA_TYPE_COLOR: + case TGA_TYPE_GRAY: + rle = 0; + break; + default: + goto close_file; + } + bpp = header->bpp; + if (!((bpp == 32) || (bpp == 24) || (bpp == 16) || (bpp == 8))) + goto close_file; + if ((bpp == 32) && (header->descriptor & TGA_DESC_ABITS)) hasa = 1; + abits = header->descriptor & TGA_DESC_ABITS; + // don't handle colormapped images + if ((header->colorMapType) != 0) + goto close_file; + // if colormap size is anything other than legal sizes or 0 - not real tga + if (!((header->colorMapSize == 0) || + (header->colorMapSize == 15) || + (header->colorMapSize == 16) || + (header->colorMapSize == 24) || + (header->colorMapSize == 32))) + goto close_file; + x = (header->xOriginHi << 8) | (header->xOriginLo); + y = (header->yOriginHi << 8) | (header->yOriginLo); + w = (header->widthHi << 8) | header->widthLo; + h = (header->heightHi << 8) | header->heightLo; + // x origin gerater that width, y origin greater than height - wrong file + if ((x >= w) || (y >= h)) + goto close_file; + // if descriptor has either of the top 2 bits set... not tga + if (header->descriptor & 0xc0) + goto close_file; + + if ((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE) || + IMG_TOO_BIG(w, h)) + goto close_file; + + if ((w != (int)ilp->w) || (h != (int)ilp->h)) + { + *error = EVAS_LOAD_ERROR_GENERIC; + goto close_file; + } + surface = ilp->buffer; + if (!surface) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto close_file; + } + + datasize = size - sizeof(tga_header) - header->idLength; + if (footer_present) + datasize = size - sizeof(tga_header) - header->idLength - + sizeof(tga_footer); + + bufptr = filedata + header->idLength; + bufend = filedata + datasize; + + if (!rle) + { + for (y = 0; y < h; y++) + { + if (vinverted) + /* some TGA's are stored upside-down! */ + dataptr = surface + ((h - y - 1) * w); + else + dataptr = surface + (y * w); + switch (bpp) + { + case 32: + for (x = 0; (x < w) && ((bufptr + 4) <= bufend); x++) + { + if (hasa) + { + int a = bufptr[3]; + + switch (abits) + { + case 1: + a = (a << 7) | (a << 6) | (a << 5) | (a << 4) | (a << 3) | (a << 2) | (a << 1) | (a); + case 2: + a = (a << 6) | (a << 4) | (a << 2) | (a); + case 3: + a = (a << 5) | (a << 2) | (a >> 1); + case 4: + a = (a << 4) | (a); + case 5: + a = (a << 3) | (a >> 2); + case 6: + a = (a << 2) | (a >> 4); + case 7: + a = (a << 1) | (a >> 6); + default: + break; + } + *dataptr = ARGB_JOIN(a, bufptr[2], bufptr[1], bufptr[0]); + } + else + *dataptr = ARGB_JOIN(0xff, bufptr[2], bufptr[1], bufptr[0]); + dataptr++; + bufptr += 4; + } + break; + case 24: + for (x = 0; (x < w) && ((bufptr + 3) <= bufend); x++) + { + *dataptr = ARGB_JOIN(0xff, bufptr[2], bufptr[1], bufptr[0]); + dataptr++; + bufptr += 3; + } + break; + case 16: + for (x = 0; (x < w) && ((bufptr + 3) <= bufend); x++) + { + unsigned char r, g, b, a; + unsigned short tmp; + + tmp = + (((unsigned short)bufptr[1]) << 8) | + (((unsigned short)bufptr[0])); + r = (tmp >> 7) & 0xf8; r |= r >> 5; + g = (tmp >> 2) & 0xf8; g |= g >> 5; + b = (tmp << 3) & 0xf8; b |= b >> 5; + a = 0xff; + if ((hasa) && (tmp & 0x8000)) a = 0; + *dataptr = ARGB_JOIN(a, r, g, b); + dataptr++; + bufptr += 2; + } + break; + case 8: + for (x = 0; (x < w) && ((bufptr + 1) <= bufend); x++) + { + *dataptr = ARGB_JOIN(0xff, bufptr[0], bufptr[0], bufptr[0]); + dataptr++; + bufptr += 1; + } + break; + default: + break; + } + } + } + else + { + int count, i; + unsigned char val; + unsigned int *dataend; + + dataptr = surface; + dataend = dataptr + (w * h); + while ((bufptr < bufend) && (dataptr < dataend)) + { + val = *bufptr; + bufptr++; + count = (val & 0x7f) + 1; + if (val & 0x80) // rel packet + { + switch (bpp) + { + case 32: + if (bufptr < (bufend - 4)) + { + unsigned char r, g, b; + int a = bufptr[3]; + + switch (abits) + { + case 1: + a = (a << 7) | (a << 6) | (a << 5) | (a << 4) | (a << 3) | (a << 2) | (a << 1) | (a); + case 2: + a = (a << 6) | (a << 4) | (a << 2) | (a); + case 3: + a = (a << 5) | (a << 2) | (a >> 1); + case 4: + a = (a << 4) | (a); + case 5: + a = (a << 3) | (a >> 2); + case 6: + a = (a << 2) | (a >> 4); + case 7: + a = (a << 1) | (a >> 6); + default: + break; + } + r = bufptr[2]; + g = bufptr[1]; + b = bufptr[0]; + if (!hasa) a = 0xff; + bufptr += 4; + for (i = 0; (i < count) && (dataptr < dataend); i++) + { + *dataptr = ARGB_JOIN(a, r, g, b); + dataptr++; + } + } + break; + case 24: + if (bufptr < (bufend - 3)) + { + unsigned char r, g, b; + + r = bufptr[2]; + g = bufptr[1]; + b = bufptr[0]; + bufptr += 3; + for (i = 0; (i < count) && (dataptr < dataend); i++) + { + *dataptr = ARGB_JOIN(0xff, r, g, b); + dataptr++; + } + } + break; + case 16: + if (bufptr < (bufend - 2)) + { + unsigned char r, g, b, a; + unsigned short tmp; + + tmp = + (((unsigned short)bufptr[1]) << 8) | + (((unsigned short)bufptr[0])); + r = (tmp >> 7) & 0xf8; r |= r >> 5; + g = (tmp >> 2) & 0xf8; g |= g >> 5; + b = (tmp << 3) & 0xf8; b |= b >> 5; + a = 0xff; + if ((hasa) && (tmp & 0x8000)) a = 0; + bufptr += 2; + for (i = 0; (i < count) && (dataptr < dataend); i++) + { + *dataptr = ARGB_JOIN(a, r, g, b); + dataptr++; + } + } + break; + case 8: + if (bufptr < (bufend - 1)) + { + unsigned char g; + + g = bufptr[0]; + bufptr += 1; + for (i = 0; (i < count) && (dataptr < dataend); i++) + { + *dataptr = ARGB_JOIN(0xff, g, g, g); + dataptr++; + } + } + break; + default: + break; + } + } + else // raw + { + switch (bpp) + { + case 32: + for (i = 0; (i < count) && (bufptr < (bufend - 4)) && (dataptr < dataend); i++) + { + if (hasa) +// *dataptr = ARGB_JOIN(255 - bufptr[3], bufptr[2], bufptr[1], bufptr[0]); + *dataptr = ARGB_JOIN(bufptr[3], bufptr[2], bufptr[1], bufptr[0]); + else + *dataptr = ARGB_JOIN(0xff, bufptr[2], bufptr[1], bufptr[0]); + dataptr++; + bufptr += 4; + } + break; + case 24: + for (i = 0; (i < count) && (bufptr < (bufend - 3)) && (dataptr < dataend); i++) + { + *dataptr = ARGB_JOIN(0xff, bufptr[2], bufptr[1], bufptr[0]); + dataptr++; + bufptr += 3; + } + break; + case 16: + for (i = 0; (i < count) && (bufptr < (bufend - 2)) && (dataptr < dataend); i++) + { + unsigned char r, g, b, a; + unsigned short tmp; + + tmp = + (((unsigned short)bufptr[1]) << 8) | + (((unsigned short)bufptr[0])); + r = (tmp >> 7) & 0xf8; r |= r >> 5; + g = (tmp >> 2) & 0xf8; g |= g >> 5; + b = (tmp << 3) & 0xf8; b |= b >> 5; + a = 0xff; + if ((hasa) && (tmp & 0x8000)) a = 0; + *dataptr = ARGB_JOIN(a, r, g, b); + dataptr++; + bufptr += 2; + } + break; + case 8: + for (i = 0; (i < count) && (bufptr < (bufend - 1)) && (dataptr < dataend); i++) + { + *dataptr = ARGB_JOIN(0xff, bufptr[0], bufptr[0], bufptr[0]); + dataptr++; + bufptr += 1; + } + break; + default: + break; + } + } + } + if (vinverted) + { + unsigned int *adv, *adv2, tmp; + + adv = surface; + adv2 = surface + (w * (h - 1)); + for (y = 0; y < (h / 2); y++) + { + for (x = 0; x < w; x++) + { + tmp = adv[x]; + adv[x] = adv2[x]; + adv2[x] = tmp; + } + adv2 -= w; + adv += w; + } + } + } + + evas_cserve2_image_premul(ilp); + + eina_file_map_free(f, seg); + eina_file_close(f); + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; + +close_file: + if (seg != NULL) eina_file_map_free(f, seg); + eina_file_close(f); + return EINA_FALSE; +} + +static Evas_Loader_Module_Api modapi = +{ + EVAS_CSERVE2_MODULE_API_VERSION, + "tga", + evas_image_load_file_head_tga, + evas_image_load_file_data_tga +}; + +static Eina_Bool +module_init(void) +{ + return evas_cserve2_loader_register(&modapi); +} + +static void +module_shutdown(void) +{ +} + +EINA_MODULE_INIT(module_init); +EINA_MODULE_SHUTDOWN(module_shutdown); diff --git a/src/bin/loaders/tiff/Makefile.am b/src/bin/loaders/tiff/Makefile.am new file mode 100644 index 0000000..19458eb --- /dev/null +++ b/src/bin/loaders/tiff/Makefile.am @@ -0,0 +1,34 @@ + +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +-I. \ +-I$(top_srcdir)/src/lib \ +-I$(top_srcdir)/src/lib/include \ +-I$(top_srcdir)/src/lib/cserve2 \ +-I$(top_srcdir)/src/bin \ +@EINA_CFLAGS@ \ +@evas_image_loader_tiff_cflags@ \ +@EVIL_CFLAGS@ + +if BUILD_LOADER_TIFF +#if !EVAS_STATIC_BUILD_TIFF + +pkgdir = $(libdir)/evas/cserve2/loaders/tiff/$(MODULE_ARCH) +pkg_LTLIBRARIES = module.la + +module_la_SOURCES = evas_image_load_tiff.c + +module_la_LIBADD = @EINA_LIBS@ @EVIL_LIBS@ @evas_image_loader_tiff_libs@ +module_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -module -avoid-version +module_la_LIBTOOLFLAGS = --tag=disable-static + +#else + +#noinst_LTLIBRARIES = libevas_loader_tiff.la + +#libevas_loader_tiff_la_SOURCES = evas_image_load_tiff.c +#libevas_loader_tiff_la_LIBADD = @evas_image_loader_tiff_libs@ + +#endif +endif diff --git a/src/bin/loaders/tiff/evas_image_load_tiff.c b/src/bin/loaders/tiff/evas_image_load_tiff.c new file mode 100644 index 0000000..e48e72f --- /dev/null +++ b/src/bin/loaders/tiff/evas_image_load_tiff.c @@ -0,0 +1,282 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#ifdef HAVE_EVIL +# include +#endif + +#include "evas_macros.h" + +#include "evas_cserve2.h" +#include "evas_cserve2_slave.h" + +typedef struct TIFFRGBAImage_Extra TIFFRGBAImage_Extra; + +struct TIFFRGBAImage_Extra { + TIFFRGBAImage rgba; + char pper; + uint32 num_pixels; + uint32 py; +}; + +static Eina_Bool +evas_image_load_file_head_tiff(Evas_Img_Load_Params *ilp, const char *file, const char *key __UNUSED__, int *error) +{ + char txt[1024]; + TIFFRGBAImage tiff_image; + TIFF *tif = NULL; + FILE *ffile; + int fd; + uint16 magic_number; + + ffile = fopen(file, "rb"); + if (!ffile) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return EINA_FALSE; + } + + if (fread(&magic_number, sizeof(uint16), 1, ffile) != 1) + { + fclose(ffile); + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + /* Apparently rewind(f) isn't sufficient */ + fseek(ffile, 0, SEEK_SET); + + if ((magic_number != TIFF_BIGENDIAN) /* Checks if actually tiff file */ + && (magic_number != TIFF_LITTLEENDIAN)) + { + fclose(ffile); + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + + fd = fileno(ffile); + fd = dup(fd); + lseek(fd, (long)0, SEEK_SET); + fclose(ffile); + + tif = TIFFFdOpen(fd, file, "r"); + if (!tif) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + + strcpy(txt, "Evas Tiff loader: cannot be processed by libtiff"); + if (!TIFFRGBAImageOK(tif, txt)) + { + TIFFClose(tif); + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + strcpy(txt, "Evas Tiff loader: cannot begin reading tiff"); + if (!TIFFRGBAImageBegin(& tiff_image, tif, 1, txt)) + { + TIFFClose(tif); + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + + if (tiff_image.alpha != EXTRASAMPLE_UNSPECIFIED) + ilp->alpha = 1; + if ((tiff_image.width < 1) || (tiff_image.height < 1) || + (tiff_image.width > IMG_MAX_SIZE) || (tiff_image.height > IMG_MAX_SIZE) || + IMG_TOO_BIG(tiff_image.width, tiff_image.height)) + { + TIFFClose(tif); + if (IMG_TOO_BIG(tiff_image.width, tiff_image.height)) + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + else + *error = EVAS_LOAD_ERROR_GENERIC; + return EINA_FALSE; + } + ilp->w = tiff_image.width; + ilp->h = tiff_image.height; + + TIFFRGBAImageEnd(&tiff_image); + TIFFClose(tif); + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; +} + +static Eina_Bool +evas_image_load_file_data_tiff(Evas_Img_Load_Params *ilp, const char *file, const char *key __UNUSED__, int *error) +{ + char txt[1024]; + TIFFRGBAImage_Extra rgba_image; + TIFF *tif = NULL; + FILE *ffile; + uint32 *rast = NULL; + uint32 num_pixels; + int fd, x, y; + uint16 magic_number; + unsigned int *surface; + + ffile = fopen(file, "rb"); + if (!ffile) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return EINA_FALSE; + } + + if (fread(&magic_number, sizeof(uint16), 1, ffile) != 1) + { + fclose(ffile); + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + /* Apparently rewind(f) isn't sufficient */ + fseek(ffile, (long)0, SEEK_SET); + + if ((magic_number != TIFF_BIGENDIAN) /* Checks if actually tiff file */ + && (magic_number != TIFF_LITTLEENDIAN)) + { + fclose(ffile); + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + + fd = fileno(ffile); + fd = dup(fd); + lseek(fd, (long)0, SEEK_SET); + fclose(ffile); + + tif = TIFFFdOpen(fd, file, "r"); + if (!tif) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + + strcpy(txt, "Evas Tiff loader: cannot be processed by libtiff"); + if (!TIFFRGBAImageOK(tif, txt)) + { + TIFFClose(tif); + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + strcpy(txt, "Evas Tiff loader: cannot begin reading tiff"); + if (!TIFFRGBAImageBegin((TIFFRGBAImage *) & rgba_image, tif, 0, txt)) + { + TIFFClose(tif); + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + + if (rgba_image.rgba.alpha != EXTRASAMPLE_UNSPECIFIED) + ilp->alpha = 1; + if ((rgba_image.rgba.width != ilp->w) || + (rgba_image.rgba.height != ilp->h)) + { + TIFFClose(tif); + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + + surface = ilp->buffer; + if (!surface) + { + TIFFRGBAImageEnd((TIFFRGBAImage *) & rgba_image); + TIFFClose(tif); + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + + rgba_image.num_pixels = num_pixels = ilp->w * ilp->h; + + rgba_image.pper = rgba_image.py = 0; + rast = (uint32 *) _TIFFmalloc(sizeof(uint32) * num_pixels); + + if (!rast) + { + ERR("Evas Tiff loader: out of memory"); + + TIFFRGBAImageEnd((TIFFRGBAImage *) & rgba_image); + TIFFClose(tif); + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + if (rgba_image.rgba.bitspersample == 8) + { + if (!TIFFRGBAImageGet((TIFFRGBAImage *) &rgba_image, rast, + rgba_image.rgba.width, rgba_image.rgba.height)) + { + _TIFFfree(rast); + TIFFRGBAImageEnd((TIFFRGBAImage *) & rgba_image); + TIFFClose(tif); + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + } + /* process rast -> image rgba. really same as prior code anyway just simpler */ + for (y = 0; y < (int)ilp->h; y++) + { + DATA32 *pix, *pd; + uint32 *ps, pixel; + unsigned int a, r, g, b; + + pix = surface; + pd = pix + ((ilp->h - y - 1) * ilp->w); + ps = rast + (y * ilp->w); + for (x = 0; x < (int)ilp->w; x++) + { + pixel = *ps; + a = TIFFGetA(pixel); + r = TIFFGetR(pixel); + g = TIFFGetG(pixel); + b = TIFFGetB(pixel); + if (!ilp->alpha) a = 255; + if ((rgba_image.rgba.alpha == EXTRASAMPLE_UNASSALPHA) && + (a < 255)) + { + r = (r * (a + 1)) >> 8; + g = (g * (a + 1)) >> 8; + b = (b * (a + 1)) >> 8; + } + *pd = ARGB_JOIN(a, r, g, b); + ps++; + pd++; + } + } + + _TIFFfree(rast); + + TIFFRGBAImageEnd((TIFFRGBAImage *) & rgba_image); + + TIFFClose(tif); + + evas_cserve2_image_alpha_sparse_set(ilp); + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; +} + +static Evas_Loader_Module_Api modapi = +{ + EVAS_CSERVE2_MODULE_API_VERSION, + "tiff", + evas_image_load_file_head_tiff, + evas_image_load_file_data_tiff +}; + +static Eina_Bool +module_init(void) +{ + return evas_cserve2_loader_register(&modapi); +} + +static void +module_shutdown(void) +{ +} + +EINA_MODULE_INIT(module_init); +EINA_MODULE_SHUTDOWN(module_shutdown); diff --git a/src/bin/loaders/wbmp/Makefile.am b/src/bin/loaders/wbmp/Makefile.am new file mode 100644 index 0000000..b96e179 --- /dev/null +++ b/src/bin/loaders/wbmp/Makefile.am @@ -0,0 +1,32 @@ + +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +-I. \ +-I$(top_srcdir)/src/lib \ +-I$(top_srcdir)/src/lib/include \ +-I$(top_srcdir)/src/lib/cserve2 \ +-I$(top_srcdir)/src/bin \ +@EINA_CFLAGS@ \ +@EVIL_CFLAGS@ + +if BUILD_LOADER_WBMP +#if !EVAS_STATIC_BUILD_WBMP + +pkgdir = $(libdir)/evas/cserve2/loaders/wbmp/$(MODULE_ARCH) +pkg_LTLIBRARIES = module.la + +module_la_SOURCES = evas_image_load_wbmp.c + +module_la_LIBADD = @EINA_LIBS@ @EVIL_LIBS@ +module_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -module -avoid-version +module_la_LIBTOOLFLAGS = --tag=disable-static + +#else + +#noinst_LTLIBRARIES = libevas_loader_wbmp.la +#libevas_loader_wbmp_la_SOURCES = evas_image_load_wbmp.c +#libevas_loader_wbmp_la_LIBADD = + +#endif +endif diff --git a/src/bin/loaders/wbmp/evas_image_load_wbmp.c b/src/bin/loaders/wbmp/evas_image_load_wbmp.c new file mode 100644 index 0000000..6c34aa6 --- /dev/null +++ b/src/bin/loaders/wbmp/evas_image_load_wbmp.c @@ -0,0 +1,189 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#ifdef HAVE_EVIL +# include +#endif + +#include "evas_macros.h" + +#include "evas_cserve2.h" +#include "evas_cserve2_slave.h" + +static int +read_mb(unsigned int *data, void *map, size_t length, size_t *position) +{ + int ac = 0, ct; + unsigned char buf; + + for (ct = 0;;) + { + if ((ct++) == 5) return -1; + if (*position > length) return -1; + buf = ((unsigned char *) map)[(*position)++]; + ac = (ac << 7) | (buf & 0x7f); + if ((buf & 0x80) == 0) break; + } + *data = ac; + return 0; +} + +static Eina_Bool +evas_image_load_file_head_wbmp(Evas_Img_Load_Params *ilp, const char *file, const char *key __UNUSED__, int *error) +{ + Eina_File *f; + void *map = NULL; + size_t position = 0; + size_t length; + unsigned int type, w, h; + + *error = EVAS_LOAD_ERROR_GENERIC; + f = eina_file_open(file, 0); + if (!f) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return EINA_FALSE; + } + + length = eina_file_size_get(f); + if (length <= 4) goto bail; + + map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + if (!map) goto bail; + + if (read_mb(&type, map, length, &position) < 0) goto bail; + + if (type != 0) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto bail; + } + + position++; /* skipping one byte */ + if (read_mb(&w, map, length, &position) < 0) goto bail; + if (read_mb(&h, map, length, &position) < 0) goto bail; + 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 bail; + } + + eina_file_map_free(f, map); + eina_file_close(f); + ilp->w = w; + ilp->h = h; + + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; +bail: + if (map) eina_file_map_free(f, map); + eina_file_close(f); + return EINA_FALSE; +} + +static Eina_Bool +evas_image_load_file_data_wbmp(Evas_Img_Load_Params *ilp, const char *file, const char *key __UNUSED__, int *error) +{ + Eina_File *f; + void *map = NULL; + size_t position = 0; + size_t length; + unsigned int type, w, h; + unsigned int line_length; + unsigned char *line = NULL; + int cur = 0, x, y; + DATA32 *dst_data; + + *error = EVAS_LOAD_ERROR_GENERIC; + f = eina_file_open(file, EINA_FALSE); + if (!f) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return EINA_FALSE; + } + + length = eina_file_size_get(f); + if (length <= 4) goto bail; + + map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + if (!map) goto bail; + + if (read_mb(&type, map, length, &position) < 0) goto bail; + position++; /* skipping one byte */ + if (read_mb(&w, map, length, &position) < 0) goto bail; + if (read_mb(&h, map, length, &position) < 0) goto bail; + + if (type != 0) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto bail; + } + + 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 bail; + } + + ilp->w = w; + ilp->h = h; + + dst_data = ilp->buffer; + if (!dst_data) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto bail; + } + + line_length = (ilp->w + 7) >> 3; + + for (y = 0; y < (int)ilp->h; y++) + { + if (position + line_length > length) goto bail; + line = ((unsigned char*) map) + position; + position += line_length; + for (x = 0; x < (int)ilp->w; x++) + { + int idx = x >> 3; + int offset = 1 << (0x07 - (x & 0x07)); + if (line[idx] & offset) dst_data[cur] = 0xffffffff; + else dst_data[cur] = 0xff000000; + cur++; + } + } + eina_file_map_free(f, map); + eina_file_close(f); + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; +bail: + if (map) eina_file_map_free(f, map); + eina_file_close(f); + return EINA_FALSE; +} + +static Evas_Loader_Module_Api modapi = +{ + EVAS_CSERVE2_MODULE_API_VERSION, + "wbmp", + evas_image_load_file_head_wbmp, + evas_image_load_file_data_wbmp +}; + +static Eina_Bool +module_init(void) +{ + return evas_cserve2_loader_register(&modapi); +} + +static void +module_shutdown(void) +{ +} + +EINA_MODULE_INIT(module_init); +EINA_MODULE_SHUTDOWN(module_shutdown); diff --git a/src/bin/loaders/xpm/Makefile.am b/src/bin/loaders/xpm/Makefile.am new file mode 100644 index 0000000..8edae6c --- /dev/null +++ b/src/bin/loaders/xpm/Makefile.am @@ -0,0 +1,34 @@ + +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +-I. \ +-I$(top_srcdir)/src/lib \ +-I$(top_srcdir)/src/lib/include \ +-I$(top_srcdir)/src/lib/cserve2 \ +-I$(top_srcdir)/src/bin \ +@EINA_CFLAGS@ \ +@evas_image_loader_xpm_cflags@ \ +@EVIL_CFLAGS@ + +if BUILD_LOADER_XPM +#if !EVAS_STATIC_BUILD_XPM +pkgdir = $(libdir)/evas/cserve2/loaders/xpm/$(MODULE_ARCH) + +pkg_LTLIBRARIES = module.la + +module_la_SOURCES = evas_image_load_xpm.c + +module_la_LIBADD = @EINA_LIBS@ @EVIL_LIBS@ @evas_image_loader_xpm_libs@ +module_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -module -avoid-version +module_la_LIBTOOLFLAGS = --tag=disable-static + +#else + +#noinst_LTLIBRARIES = libevas_loader_xpm.la + +#libevas_loader_xpm_la_SOURCES = evas_image_load_xpm.c +#libevas_loader_xpm_la_LIBADD = @evas_image_loader_xpm_libs@ + +#endif +endif diff --git a/src/bin/loaders/xpm/evas_image_load_xpm.c b/src/bin/loaders/xpm/evas_image_load_xpm.c new file mode 100644 index 0000000..8404509 --- /dev/null +++ b/src/bin/loaders/xpm/evas_image_load_xpm.c @@ -0,0 +1,650 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_EVIL +# include +#endif + +#include "evas_macros.h" + +#include "evas_cserve2.h" +#include "evas_cserve2_slave.h" + +static Eina_File *rgb_txt; +static void *rgb_txt_map; + +static void +xpm_parse_color(char *color, int *r, int *g, int *b) +{ + char *tmp; + char *max; + char *endline; + char buf[4096]; + + /* is a #ff00ff like color */ + if (color[0] == '#') + { + int len; + char val[32]; + + len = strlen(color) - 1; + if (len < 96) + { + int i; + + len /= 3; + for (i = 0; i < len; i++) + val[i] = color[1 + i + (0 * len)]; + val[i] = 0; + sscanf(val, "%x", r); + for (i = 0; i < len; i++) + val[i] = color[1 + i + (1 * len)]; + val[i] = 0; + sscanf(val, "%x", g); + for (i = 0; i < len; i++) + val[i] = color[1 + i + (2 * len)]; + val[i] = 0; + sscanf(val, "%x", b); + if (len == 1) + { + *r = (*r << 4) | *r; + *g = (*g << 4) | *g; + *b = (*b << 4) | *b; + } + else if (len > 2) + { + *r >>= (len - 2) * 4; + *g >>= (len - 2) * 4; + *b >>= (len - 2) * 4; + } + } + return; + } + /* look in rgb txt database */ + if (!rgb_txt) return; + tmp = rgb_txt_map; + max = tmp + eina_file_size_get(rgb_txt); + + while (tmp < max) + { + endline = memchr(tmp, '\n', max - tmp); + if (!endline) endline = max; + if ((*tmp != '!') && ((endline - tmp) < (int) (sizeof(buf) - 1))) + { + int rr, gg, bb; + char name[4096]; + + /* FIXME: not really efficient */ + memcpy(buf, tmp, endline - tmp); + buf[endline - tmp + 1] = '\0'; + + if (sscanf(buf, "%i %i %i %[^\n]", &rr, &gg, &bb, name) == 4) + { + if (!strcasecmp(name, color)) + { + *r = rr; + *g = gg; + *b = bb; + return; + } + } + } + tmp = endline + 1; + } +} + +/** FIXME: clean this up and make more efficient **/ +static Eina_Bool +evas_image_load_file_xpm(Evas_Img_Load_Params *ilp, const char *file, const char *key __UNUSED__, int load_data, int *error) +{ + DATA32 *ptr, *end; + Eina_File *f; + const char *map; + size_t length; + size_t position; + + int pc, c, i, j, k, w, h, ncolors, cpp, comment, transp, + quote, context, len, done, r, g, b, backslash, lu1, lu2; + char *line = NULL; + char s[256], tok[128], col[256], *tl; + int lsz = 256; + struct _cmap { + char str[6]; + unsigned char transp; + short r, g, b; + } *cmap = NULL; + + short lookup[128 - 32][128 - 32]; + int count, pixels; + + done = 0; +// transp = -1; + transp = 1; + + /* if immediate_load is 1, then dont delay image laoding as below, or */ + /* already data in this image - dont load it again */ + + f = eina_file_open(file, 0); + if (!f) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return EINA_FALSE; + } + length = eina_file_size_get(f); + position = 0; + if (length < 9) + { + eina_file_close(f); + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + + map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + if (!map) + { + eina_file_close(f); + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + + if (strncmp("/* XPM */", map, 9)) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto on_error; + } + + i = 0; + j = 0; + cmap = NULL; + w = 10; + h = 10; + ptr = NULL; + end = NULL; + c = ' '; + comment = 0; + quote = 0; + context = 0; + pixels = 0; + count = 0; + line = malloc(lsz); + if (!line) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto on_error; + } + + backslash = 0; + memset(lookup, 0, sizeof(lookup)); + while (!done) + { + pc = c; + if (position == length) break ; + c = (char) map[position++]; + if (!quote) + { + if ((pc == '/') && (c == '*')) + comment = 1; + else if ((pc == '*') && (c == '/') && (comment)) + comment = 0; + } + if (!comment) + { + if ((!quote) && (c == '"')) + { + quote = 1; + i = 0; + } + else if ((quote) && (c == '"')) + { + line[i] = 0; + quote = 0; + if (context == 0) + { + /* Header */ + if (sscanf(line, "%i %i %i %i", &w, &h, &ncolors, &cpp) != 4) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto on_error; + } + if ((ncolors > 32766) || (ncolors < 1)) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto on_error; + } + if ((cpp > 5) || (cpp < 1)) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto on_error; + } + if ((w > IMG_MAX_SIZE) || (w < 1)) + { + *error = EVAS_LOAD_ERROR_GENERIC; + goto on_error; + } + if ((h > IMG_MAX_SIZE) || (h < 1)) + { + *error = EVAS_LOAD_ERROR_GENERIC; + goto on_error; + } + if (IMG_TOO_BIG(w, h)) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto on_error; + } + + if (!cmap) + { + cmap = malloc(sizeof(struct _cmap) * ncolors); + if (!cmap) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto on_error; + } + } + ilp->w = w; + ilp->h = h; + + j = 0; + context++; + } + else if (context == 1) + { + /* Color Table */ + if (j < ncolors) + { + int slen; + int hascolor, iscolor; + + iscolor = 0; + hascolor = 0; + tok[0] = 0; + col[0] = 0; + s[0] = 0; + len = strlen(line); + strncpy(cmap[j].str, line, cpp); + cmap[j].str[cpp] = 0; + for (slen = 0; slen < cpp; slen++) + { + /* fix the ascii of the color string - if its < 32 - just limit to 32 */ + if (cmap[j].str[slen] < 32) cmap[j].str[slen] = 0; + } + cmap[j].r = -1; + cmap[j].transp = 0; + for (k = cpp; k < len; k++) + { + if (line[k] != ' ') + { + s[0] = 0; + sscanf(&line[k], "%255s", s); + slen = strlen(s); + k += slen; + if (!strcmp(s, "c")) iscolor = 1; + if ((!strcmp(s, "m")) || (!strcmp(s, "s")) + || (!strcmp(s, "g4")) || (!strcmp(s, "g")) + || (!strcmp(s, "c")) || (k >= len)) + { + if (k >= len) + { + if (col[0]) + { + if (strlen(col) < (sizeof(col) - 2)) + strcat(col, " "); + else + done = 1; + } + if ((strlen(col) + strlen(s)) < (sizeof(col) - 1)) + strcat(col, s); + } + if (col[0]) + { + if (!strcasecmp(col, "none")) + { + transp = 1; + cmap[j].transp = 1; + cmap[j].r = 0; + cmap[j].g = 0; + cmap[j].b = 0; + } + else + { + if ((((cmap[j].r < 0) || (!strcmp(tok, "c"))) && (!hascolor))) + { + r = g = b = 0; + xpm_parse_color(col, &r, &g, &b); + cmap[j].r = r; + cmap[j].g = g; + cmap[j].b = b; + if (iscolor) hascolor = 1; + } + } + } + strcpy(tok, s); + col[0] = 0; + } + else + { + if (col[0]) + { + if (strlen(col) < ( sizeof(col) - 2)) + strcat(col, " "); + else + done = 1; + } + if ((strlen(col) + strlen(s)) < (sizeof(col) - 1)) + strcat(col, s); + } + } + } + } + j++; + if (j >= ncolors) + { + if (cpp == 1) + { + for (i = 0; i < ncolors; i++) + lookup[(int)cmap[i].str[0] - 32][0] = i; + } + if (cpp == 2) + { + for (i = 0; i < ncolors; i++) + lookup[(int)cmap[i].str[0] - 32][(int)cmap[i].str[1] - 32] = i; + } + context++; + } + + if (transp) ilp->alpha = 1; + + if (load_data) + { + ptr = ilp->buffer; + if (!ptr) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto on_error; + } + pixels = w * h; + end = ptr + pixels; + } + else + { + *error = EVAS_LOAD_ERROR_NONE; + goto on_success; + } + } + else + { + /* Image Data */ + i = 0; + if (cpp == 0) + { + /* Chars per pixel = 0? well u never know */ + } + /* it's xpm - don't care about speed too much. still faster + * that most xpm loaders anyway */ + else if (cpp == 1) + { + if (transp) + { + for (i = 0; + ((i < 65536) && (ptr < end) && (line[i])); + i++) + { + lu1 = (int)line[i] - 32; + if (lu1 < 0) continue; + if (cmap[lookup[lu1][0]].transp) + { + r = (unsigned char)cmap[lookup[lu1][0]].r; + g = (unsigned char)cmap[lookup[lu1][0]].g; + b = (unsigned char)cmap[lookup[lu1][0]].b; + *ptr = RGB_JOIN(r, g, b); + ptr++; + count++; + } + else + { + r = (unsigned char)cmap[lookup[lu1][0]].r; + g = (unsigned char)cmap[lookup[lu1][0]].g; + b = (unsigned char)cmap[lookup[lu1][0]].b; + *ptr = ARGB_JOIN(0xff, r, g, b); + ptr++; + count++; + } + } + } + else + { + for (i = 0; + ((i < 65536) && (ptr < end) && (line[i])); + i++) + { + lu1 = (int)line[i] - 32; + if (lu1 < 0) continue; + r = (unsigned char)cmap[lookup[lu1][0]].r; + g = (unsigned char)cmap[lookup[lu1][0]].g; + b = (unsigned char)cmap[lookup[lu1][0]].b; + *ptr = ARGB_JOIN(0xff, r, g, b); + ptr++; + count++; + } + } + } + else if (cpp == 2) + { + if (transp) + { + for (i = 0; + ((i < 65536) && (ptr < end) && (line[i])); + i++) + { + lu1 = (int)line[i] - 32; + i++; + lu2 = (int)line[i] - 32; + if (lu1 < 0) continue; + if (lu2 < 0) continue; + if (cmap[lookup[lu1][lu2]].transp) + { + r = (unsigned char)cmap[lookup[lu1][lu2]].r; + g = (unsigned char)cmap[lookup[lu1][lu2]].g; + b = (unsigned char)cmap[lookup[lu1][lu2]].b; + *ptr = RGB_JOIN(r, g, b); + ptr++; + count++; + } + else + { + r = (unsigned char)cmap[lookup[lu1][lu2]].r; + g = (unsigned char)cmap[lookup[lu1][lu2]].g; + b = (unsigned char)cmap[lookup[lu1][lu2]].b; + *ptr = ARGB_JOIN(0xff, r, g, b); + ptr++; + count++; + } + } + } + else + { + for (i = 0; + ((i < 65536) && (ptr < end) && (line[i])); + i++) + { + lu1 = (int)line[i] - 32; + i++; + lu2 = (int)line[i] - 32; + if (lu1 < 0) continue; + if (lu2 < 0) continue; + r = (unsigned char)cmap[lookup[lu1][lu2]].r; + g = (unsigned char)cmap[lookup[lu1][lu2]].g; + b = (unsigned char)cmap[lookup[lu1][lu2]].b; + *ptr = ARGB_JOIN(0xff, r, g, b); + ptr++; + count++; + } + } + } + else + { + if (transp) + { + for (i = 0; + ((i < 65536) && (ptr < end) && (line[i])); + i++) + { + for (j = 0; j < cpp; j++, i++) + { + col[j] = line[i]; + if (col[j] < 32) col[j] = 32; + } + col[j] = 0; + i--; + for (j = 0; j < ncolors; j++) + { + if (!strcmp(col, cmap[j].str)) + { + if (cmap[j].transp) + { + r = (unsigned char)cmap[j].r; + g = (unsigned char)cmap[j].g; + b = (unsigned char)cmap[j].b; + *ptr = RGB_JOIN(r, g, b); + ptr++; + count++; + } + else + { + r = (unsigned char)cmap[j].r; + g = (unsigned char)cmap[j].g; + b = (unsigned char)cmap[j].b; + *ptr = ARGB_JOIN(0xff, r, g, b); + ptr++; + count++; + } + break; + } + } + } + } + else + { + for (i = 0; + ((i < 65536) && (ptr < end) && (line[i])); + i++) + { + for (j = 0; j < cpp; j++, i++) + { + col[j] = line[i]; + } + col[j] = 0; + i--; + for (j = 0; j < ncolors; j++) + { + if (!strcmp(col, cmap[j].str)) + { + r = (unsigned char)cmap[j].r; + g = (unsigned char)cmap[j].g; + b = (unsigned char)cmap[j].b; + *ptr = ARGB_JOIN(0xff, r, g, b); + ptr++; + count++; + break; + } + } + } + } + } + } + } + } + /* Scan in line from XPM file */ + if ((!comment) && (quote) && (c != '"')) + { + if (c < 32) c = 32; + else if (c > 127) c = 127; + if (c =='\\') + { + if (++backslash < 2) + line[i++] = c; + else + backslash = 0; + } + else + { + backslash = 0; + line[i++] = c; + } + } + if (i >= lsz) + { + lsz += 256; + tl = realloc(line, lsz); + if (!tl) break; + line = tl; + } + if (((ptr) && ((ptr - (DATA32 *)ilp->buffer) >= (w * h * (int)sizeof(DATA32)))) || + ((context > 1) && (count >= pixels))) + break; + } + + on_success: + free(cmap); + free(line); + + eina_file_map_free(f, (void*) map); + eina_file_close(f); + + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; + + on_error: + free(line); + eina_file_map_free(f, (void*) map); + eina_file_close(f); + return EINA_FALSE; +} + +static Eina_Bool +evas_image_load_file_head_xpm(Evas_Img_Load_Params *ilp, const char *file, const char *key, int *error) +{ + return evas_image_load_file_xpm(ilp, file, key, 0, error); +} + +static Eina_Bool +evas_image_load_file_data_xpm(Evas_Img_Load_Params *ilp, const char *file, const char *key, int *error) +{ + return evas_image_load_file_xpm(ilp, file, key, 1, error); +} + +static Evas_Loader_Module_Api modapi = +{ + EVAS_CSERVE2_MODULE_API_VERSION, + "xpm", + evas_image_load_file_head_xpm, + evas_image_load_file_data_xpm +}; + +static Eina_Bool +module_init(void) +{ + /* Shouldn't we make that PATH configurable ? */ + rgb_txt = eina_file_open("/usr/lib/X11/rgb.txt", 0); + if (!rgb_txt) rgb_txt = eina_file_open("/usr/X11/lib/X11/rgb.txt", 0); + if (!rgb_txt) rgb_txt = eina_file_open("/usr/X11R6/lib/X11/rgb.txt", 0); + if (!rgb_txt) rgb_txt = eina_file_open("/usr/openwin/lib/X11/rgb.txt", 0); + if (rgb_txt) + rgb_txt_map = eina_file_map_all(rgb_txt, EINA_FILE_SEQUENTIAL); + return evas_cserve2_loader_register(&modapi); +} + +static void +module_shutdown(void) +{ + if (rgb_txt) + { + eina_file_map_free(rgb_txt, rgb_txt_map); + eina_file_close(rgb_txt); + rgb_txt = NULL; + } +} + +EINA_MODULE_INIT(module_init); +EINA_MODULE_SHUTDOWN(module_shutdown); diff --git a/src/examples/Makefile.am b/src/examples/Makefile.am index 1bb3f37..757ca54 100644 --- a/src/examples/Makefile.am +++ b/src/examples/Makefile.am @@ -67,6 +67,10 @@ examples_PROGRAMS += evas_images2 evas_images2_SOURCES = evas-images2.c evas_images2_LDADD = $(top_builddir)/src/lib/libevas.la @ECORE_EVAS_LIBS@ +examples_PROGRAMS += evas_images3 +evas_images3_SOURCES = evas-images3.c +evas_images3_LDADD = $(top_builddir)/src/lib/libevas.la @ECORE_EVAS_LIBS@ + examples_PROGRAMS += evas_text evas_text_SOURCES = evas-text.c evas_text_LDADD = $(top_builddir)/src/lib/libevas.la @ECORE_EVAS_LIBS@ @@ -116,6 +120,7 @@ files_DATA += \ $(srcdir)/evas-init-shutdown.c \ $(srcdir)/evas-images.c \ $(srcdir)/evas-images2.c \ + $(srcdir)/evas-images3.c \ $(srcdir)/evas-object-manipulation.c \ $(srcdir)/evas-events.c \ $(srcdir)/evas-aspect-hints.c \ @@ -132,6 +137,7 @@ EXTRA_DIST = $(EDCS) \ $(srcdir)/evas-init-shutdown.c \ $(srcdir)/evas-images.c \ $(srcdir)/evas-images2.c \ + $(srcdir)/evas-images3.c \ $(srcdir)/evas-object-manipulation.c \ $(srcdir)/evas-events.c \ $(srcdir)/evas-aspect-hints.c \ diff --git a/src/examples/evas-images3.c b/src/examples/evas-images3.c new file mode 100644 index 0000000..5d627b0 --- /dev/null +++ b/src/examples/evas-images3.c @@ -0,0 +1,196 @@ +/** + * Simple Evas example illustrating some image objects functions + * + * You'll need at least one engine built for it (excluding the buffer + * one) and the png image loader/saver also built. See stdout/stderr + * for output. + * + * @verbatim + * gcc -o evas-images2 evas-images2.c `pkg-config --libs --cflags evas ecore ecore-evas` + * @endverbatim + */ + +#ifdef HAVE_CONFIG_H + +#include "config.h" +#else + +#define PACKAGE_EXAMPLES_DIR "." +#define __UNUSED__ + +#endif + +#include +#include +#include +#include + +#define WIDTH (320) +#define HEIGHT (240) + +static const char *img_path = PACKAGE_EXAMPLES_DIR "/enlightenment.png"; +static const char *commands = \ + "commands are:\n" + "\tw - write new pixel data to image\n" + "\ti - print image info\n" + "\ta - save noise image to disk (/tmp dir)\n" + "\th - print help\n"; + +const char *file_path = "/tmp/evas-images2-example.png"; +const char *quality_str = "quality=100"; + +struct test_data +{ + Ecore_Evas *ee; + Evas *evas; + Evas_Object *bg; + Evas_Object *logo, *logo1; +}; + +static struct test_data d = {0}; + +static void +_on_destroy(Ecore_Evas *ee __UNUSED__) +{ + ecore_main_loop_quit(); +} + +/* here just to keep our example's window size and background image's + * size in synchrony */ +static void +_canvas_resize_cb(Ecore_Evas *ee) +{ + int w, h; + + ecore_evas_geometry_get(ee, NULL, NULL, &w, &h); + evas_object_resize(d.bg, w, h); + evas_object_resize(d.logo, w / 2, h); + evas_object_move(d.logo1, w / 2, 0); + evas_object_resize(d.logo1, w / 2, h); +} + +static void +_on_keydown(void *data __UNUSED__, + Evas *evas __UNUSED__, + Evas_Object *o __UNUSED__, + void *einfo) +{ + Evas_Event_Key_Down *ev = einfo; + + if (strcmp(ev->keyname, "h") == 0) /* print help */ + { + fprintf(stdout, commands); + return; + } + + if (strcmp(ev->keyname, "i") == 0) /* change proxy's source */ + { + int stride = evas_object_image_stride_get(d.logo); + int w, h; + + evas_object_image_size_get(d.logo, &w, &h); + + printf("image size: %dx%d; stride: %d\n", w, h, stride); + + return; + } + + if (strcmp(ev->keyname, "w") == 0) /* save noise image to disk */ + { + int i; + char *pixels = evas_object_image_data_get(d.logo, EINA_FALSE); + char *bufpixels; + int w, h; + int stride; + Eina_Bool equal = EINA_TRUE; + + evas_object_image_size_get(d.logo, &w, &h); + stride = evas_object_image_stride_get(d.logo); + + bufpixels = malloc(sizeof(char) * stride * h); + memcpy(bufpixels, pixels, sizeof(char) * stride * h); + + pixels = evas_object_image_data_get(d.logo, EINA_TRUE); + + for (i = 0; i < (stride * h); i++) + { + if (bufpixels[i] != pixels[i]) + { + equal = EINA_FALSE; + break; + } + } + + free(bufpixels); + + if (!equal) + printf("write pixels different from readonly pixels.\n"); + + for (i = ((stride * h) / 4) ; i < ((stride * h) / 2) ; i++) + { + pixels[i] = 0; + } + + // evas_object_image_data_set(d.logo, pixels); + evas_object_image_data_update_add(d.logo, 0, 0, w, h); + return; + } +} + +int +main(void) +{ + // unsigned int i; + // unsigned int pixels[(WIDTH / 4) * (HEIGHT / 4)]; + + if (!ecore_evas_init()) + return EXIT_FAILURE; + + /* this will give you a window with an Evas canvas under the first + * engine available */ + d.ee = ecore_evas_new(NULL, 10, 10, WIDTH, HEIGHT, NULL); + if (!d.ee) + goto error; + + ecore_evas_callback_destroy_set(d.ee, _on_destroy); + ecore_evas_callback_resize_set(d.ee, _canvas_resize_cb); + ecore_evas_show(d.ee); + + /* the canvas pointer, de facto */ + d.evas = ecore_evas_get(d.ee); + + d.bg = evas_object_rectangle_add(d.evas); + evas_object_color_set(d.bg, 255, 255, 255, 255); /* white bg */ + evas_object_move(d.bg, 0, 0); /* at canvas' origin */ + evas_object_resize(d.bg, WIDTH, HEIGHT); /* covers full canvas */ + evas_object_show(d.bg); + + evas_object_focus_set(d.bg, EINA_TRUE); + evas_object_event_callback_add( + d.bg, EVAS_CALLBACK_KEY_DOWN, _on_keydown, NULL); + + d.logo = evas_object_image_filled_add(d.evas); + + evas_object_image_file_set(d.logo, img_path, NULL); + evas_object_resize(d.logo, WIDTH / 2, HEIGHT); + evas_object_show(d.logo); + + d.logo1 = evas_object_image_filled_add(d.evas); + evas_object_image_file_set(d.logo1, img_path, NULL); + evas_object_resize(d.logo1, WIDTH / 2, HEIGHT); + evas_object_move(d.logo1, WIDTH / 2, 0); + evas_object_show(d.logo1); + + fprintf(stdout, commands); + ecore_main_loop_begin(); + + ecore_evas_free(d.ee); + ecore_evas_shutdown(); + return 0; + +error: + fprintf(stderr, "you got to have at least one evas engine built and linked" + " up to ecore-evas for this example to run properly.\n"); + ecore_evas_shutdown(); + return -1; +} diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 1240b2b..9d9f3a1 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -1,6 +1,6 @@ MAINTAINERCLEANFILES = Makefile.in -SUBDIRS = canvas cache cserve file engines include +SUBDIRS = canvas cache cache2 cserve cserve2 file engines include EVAS_STATIC_MODULE = EVAS_STATIC_LIBADD = @@ -212,11 +212,18 @@ libevas_cserve_la = cserve/libevas_cserve.la endif +if EVAS_CSERVE2 + +libevas_cserve2_la = cserve2/libevas_cserve2.la + +endif + libevas_la_LIBADD = \ canvas/libevas_canvas.la \ file/libevas_file.la \ cache/libevas_cache.la \ $(libevas_cserve_la) \ +$(libevas_cserve2_la) \ engines/common/libevas_engine_common.la \ @FREETYPE_LIBS@ \ @FRIBIDI_LIBS@ \ @@ -235,9 +242,20 @@ canvas/libevas_canvas.la \ file/libevas_file.la \ cache/libevas_cache.la \ $(libevas_cserve_la) \ +$(libevas_cserve2_la) \ engines/common/libevas_engine_common.la \ $(EVAS_STATIC_MODULE) +if EVAS_CSERVE2 + +libevas_la_LIBADD += \ +cache2/libevas_cache2.la + +libevas_la_DEPENDENCIES += \ +cache2/libevas_cache2.la + +endif + if BUILD_ENGINE_SOFTWARE_16 libevas_la_LIBADD += engines/common_16/libevas_engine_common_16.la @@ -257,4 +275,4 @@ libevas_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -version-info @versio ### Evas_GL we are still using it in our code, so just don't install it. EXTRA_DIST=Evas_GL.h -DIST_SUBDIRS = canvas cache cserve file engines include +DIST_SUBDIRS = canvas cache cache2 cserve cserve2 file engines include diff --git a/src/lib/cache/evas_cache_image.c b/src/lib/cache/evas_cache_image.c index 8ebe806..44a9354 100644 --- a/src/lib/cache/evas_cache_image.c +++ b/src/lib/cache/evas_cache_image.c @@ -17,9 +17,9 @@ //#define CACHEDUMP 1 -#ifdef EVAS_CSERVE +#ifdef EVAS_CSERVE2 // FIXME: cache server and threaded preload clash badly atm - disable -//#undef BUILD_ASYNC_PRELOAD +#undef BUILD_ASYNC_PRELOAD #endif #ifdef BUILD_ASYNC_PRELOAD diff --git a/src/lib/cache2/Makefile.am b/src/lib/cache2/Makefile.am new file mode 100644 index 0000000..24806e3 --- /dev/null +++ b/src/lib/cache2/Makefile.am @@ -0,0 +1,29 @@ + +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = -I. \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib/include \ + -I$(top_srcdir)/src/lib/cserve2 \ + -DPACKAGE_BIN_DIR=\"$(bindir)\" \ + -DPACKAGE_LIB_DIR=\"$(libdir)\" \ + -DPACKAGE_DATA_DIR=\"$(datadir)/$(PACKAGE)\" \ + @EVIL_CFLAGS@ \ + @FREETYPE_CFLAGS@ \ + @PIXMAN_CFLAGS@ \ + @EINA_CFLAGS@ \ + @PIXMAN_CFLAGS@ + +if EVAS_CSERVE2 + +noinst_LTLIBRARIES = libevas_cache2.la +libevas_cache2_la_SOURCES = \ +evas_cache2.c + +noinst_HEADERS = evas_cache2.h + +libevas_cache2_la_LIBAD = @EVIL_LIBS@ + +libevas_cache2_la_DEPENDENCIES = $(top_builddir)/config.h + +endif diff --git a/src/lib/cache2/evas_cache2.c b/src/lib/cache2/evas_cache2.c new file mode 100644 index 0000000..8a15b3d --- /dev/null +++ b/src/lib/cache2/evas_cache2.c @@ -0,0 +1,932 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#ifdef HAVE_EVIL +# include +#endif + +#include "evas_common.h" +#include "evas_private.h" +#include "evas_cache2.h" +#include "evas_cs2.h" +#include "evas_cs2_private.h" + +#define FREESTRC(Var) \ + if (Var) \ +{ \ + eina_stringshare_del(Var); \ + Var = NULL; \ +} + +static void _evas_cache_image_dirty_add(Image_Entry *im); +static void _evas_cache_image_dirty_del(Image_Entry *im); +static void _evas_cache_image_activ_add(Image_Entry *im); +static void _evas_cache_image_activ_del(Image_Entry *im); +static void _evas_cache_image_lru_add(Image_Entry *im); +static void _evas_cache_image_lru_del(Image_Entry *im); +static void _evas_cache2_image_entry_preload_remove(Image_Entry *ie, const void *target); +// static void _evas_cache_image_lru_nodata_add(Image_Entry *im); +// static void _evas_cache_image_lru_nodata_del(Image_Entry *im); + +static void +_evas_cache_image_dirty_add(Image_Entry *im) +{ + if (im->flags.dirty) return; + _evas_cache_image_activ_del(im); + _evas_cache_image_lru_del(im); + // _evas_cache_image_lru_nodata_del(im); + im->flags.dirty = 1; + im->flags.cached = 1; + im->cache2->dirty = eina_inlist_prepend(im->cache2->dirty, EINA_INLIST_GET(im)); + if (im->cache_key) + { + eina_stringshare_del(im->cache_key); + im->cache_key = NULL; + } +} + +static void +_evas_cache_image_dirty_del(Image_Entry *im) +{ + if (!im->flags.dirty) return; + if (!im->cache2) return; + im->flags.dirty = 0; + im->flags.cached = 0; + im->cache2->dirty = eina_inlist_remove(im->cache2->dirty, EINA_INLIST_GET(im)); +} + +static void +_evas_cache_image_activ_add(Image_Entry *im) +{ + if (im->flags.activ) return; + _evas_cache_image_dirty_del(im); + _evas_cache_image_lru_del(im); + // _evas_cache_image_lru_nodata_del(im); + if (!im->cache_key) return; + im->flags.activ = 1; + im->flags.cached = 1; + eina_hash_direct_add(im->cache2->activ, im->cache_key, im); +} + +static void +_evas_cache_image_activ_del(Image_Entry *im) +{ + if (!im->flags.activ) return; + if (!im->cache_key) return; + im->flags.activ = 0; + im->flags.cached = 0; + eina_hash_del(im->cache2->activ, im->cache_key, im); +} + +static void +_evas_cache_image_lru_add(Image_Entry *im) +{ + if (im->flags.lru) return; + _evas_cache_image_dirty_del(im); + _evas_cache_image_activ_del(im); + // _evas_cache_image_lru_nodata_del(im); + if (!im->cache_key) return; + im->flags.lru = 1; + im->flags.cached = 1; + eina_hash_direct_add(im->cache2->inactiv, im->cache_key, im); + im->cache2->lru = eina_inlist_prepend(im->cache2->lru, EINA_INLIST_GET(im)); + im->cache2->usage += im->cache2->func.mem_size_get(im); +} + +static void +_evas_cache_image_lru_del(Image_Entry *im) +{ + if (!im->flags.lru) return; + if (!im->cache_key) return; + im->flags.lru = 0; + im->flags.cached = 0; + eina_hash_del(im->cache2->inactiv, im->cache_key, im); + im->cache2->lru = eina_inlist_remove(im->cache2->lru, EINA_INLIST_GET(im)); + im->cache2->usage -= im->cache2->func.mem_size_get(im); +} + +/* +static void +_evas_cache_image_lru_nodata_add(Image_Entry *im) +{ + if (im->flags.lru_nodata) return; + _evas_cache_image_dirty_del(im); + _evas_cache_image_activ_del(im); + _evas_cache_image_lru_del(im); + im->flags.lru = 1; + im->flags.cached = 1; + im->cache2->lru_nodata = eina_inlist_prepend(im->cache2->lru_nodata, EINA_INLIST_GET(im)); +} + +static void +_evas_cache_image_lru_nodata_del(Image_Entry *im) +{ + if (!im->flags.lru_nodata) return; + im->flags.lru = 0; + im->flags.cached = 0; + im->cache2->lru_nodata = eina_inlist_remove(im->cache2->lru_nodata, EINA_INLIST_GET(im)); +} +*/ + +static Eina_Bool +_timestamp_compare(Image_Timestamp *tstamp, struct stat *st) +{ + if (tstamp->mtime != st->st_mtime) return EINA_FALSE; + if (tstamp->size != st->st_size) return EINA_FALSE; + if (tstamp->ino != st->st_ino) return EINA_FALSE; +#ifdef _STAT_VER_LINUX +#if (defined __USE_MISC && defined st_mtime) + if (tstamp->mtime_nsec != (unsigned long int)st->st_mtim.tv_nsec) + return EINA_FALSE; +#else + if (tstamp->mtime_nsec != (unsigned long int)st->st_mtimensec) + return EINA_FALSE; +#endif +#endif + return EINA_TRUE; +} + +static void +_timestamp_build(Image_Timestamp *tstamp, struct stat *st) +{ + tstamp->mtime = st->st_mtime; + tstamp->size = st->st_size; + tstamp->ino = st->st_ino; +#ifdef _STAT_VER_LINUX +#if (defined __USE_MISC && defined st_mtime) + tstamp->mtime_nsec = (unsigned long int)st->st_mtim.tv_nsec; +#else + tstamp->mtime_nsec = (unsigned long int)st->st_mtimensec; +#endif +#endif +} + +static void +_evas_cache_image_entry_delete(Evas_Cache2 *cache, Image_Entry *ie) +{ + if (!ie) return; + + if (ie->flags.delete_me == 1) + return; + + if (ie->preload_rid) + { + ie->flags.delete_me = 1; + _evas_cache2_image_entry_preload_remove(ie, NULL); + return; + } + + _evas_cache_image_dirty_del(ie); + _evas_cache_image_activ_del(ie); + _evas_cache_image_lru_del(ie); + // _evas_cache_image_lru_nodata_del(ie); + + + if (ie->data1) + { + evas_cserve2_image_unload(ie); + evas_cache2_image_unload_data(ie); + evas_cserve2_image_free(ie); + } + else + { + if (cache) + cache->func.surface_delete(ie); + } + + FREESTRC(ie->cache_key); + FREESTRC(ie->file); + FREESTRC(ie->key); + ie->cache2 = NULL; + + evas_common_rgba_image_scalecache_shutdown(ie); + + free(ie); +} + +static Image_Entry * +_evas_cache_image_entry_new(Evas_Cache2 *cache, + const char *hkey, + Image_Timestamp *tstamp, + const char *file, + const char *key, + RGBA_Image_Loadopts *lo, + int *error) +{ + Image_Entry *ie; + RGBA_Image *im; + + // ie = cache->func.alloc(); + im = calloc(1, sizeof(RGBA_Image)); + if (!im) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return NULL; + } + + im->flags = RGBA_IMAGE_NOTHING; + im->ref = 1; + evas_common_rgba_image_scalecache_init(&im->cache_entry); + + ie = &im->cache_entry; + + ie->cache2 = cache; + if (hkey) ie->cache_key = eina_stringshare_add(hkey); + ie->flags.need_data = 1; + ie->space = EVAS_COLORSPACE_ARGB8888; + ie->w = -1; + ie->h = -1; + ie->scale = 1; + if (file) ie->file = eina_stringshare_add(file); + if (key) ie->key = eina_stringshare_add(key); + if (tstamp) ie->tstamp = *tstamp; + else memset(&ie->tstamp, 0, sizeof(Image_Timestamp)); + + if (lo) ie->load_opts = *lo; + if (ie->file) + { + if (!evas_cserve2_image_load(ie, ie->file, ie->key, &(ie->load_opts))) + { + ERR("couldn't load '%s' '%s' with cserve2!", + ie->file, ie->key ? ie->key : ""); + _evas_cache_image_entry_delete(cache, ie); + return NULL; + } + } + + if (ie->cache_key) _evas_cache_image_activ_add(ie); + else _evas_cache_image_dirty_add(ie); + return ie; +} + +EAPI void +evas_cache2_image_surface_alloc(Image_Entry *ie, int w, int h) +{ + Evas_Cache2 *cache = ie->cache2; + int wmin = w > 0 ? w : 1; + int hmin = h > 0 ? h : 1; + + if (cache->func.surface_alloc(ie, wmin, hmin)) + { + wmin = 0; + hmin = 0; + } + + ie->w = wmin; + ie->h = hmin; + ie->allocated.w = wmin; + ie->allocated.h = hmin; + ie->flags.loaded = EINA_TRUE; +} + +static void +_evas_cache2_image_preloaded_cb(void *data, Eina_Bool success) +{ + Image_Entry *ie = data; + Evas_Cache_Target *tmp; + + ie->cache2->preload = eina_list_remove(ie->cache2->preload, ie); + ie->flags.preload_done = success; + + while ((tmp = ie->targets)) + { + ie->targets = (Evas_Cache_Target *) + eina_inlist_remove(EINA_INLIST_GET(ie->targets), + EINA_INLIST_GET(ie->targets)); + if (!ie->flags.delete_me) + evas_object_inform_call_image_preloaded((Evas_Object *) tmp->target); + free(tmp); + } + + if (ie->flags.delete_me) + _evas_cache_image_entry_delete(ie->cache2, ie); +} + +static Eina_Bool +_evas_cache2_image_entry_preload_add(Image_Entry *ie, const void *target) +{ + Evas_Cache_Target *tg; + + if (ie->flags.preload_done) + return EINA_FALSE; + + tg = malloc(sizeof(Evas_Cache_Target)); + if (!tg) + return EINA_TRUE; + + tg->target = target; + ie->targets = (Evas_Cache_Target *) + eina_inlist_append(EINA_INLIST_GET(ie->targets), EINA_INLIST_GET(tg)); + + if (!ie->preload_rid) + { + ie->cache2->preload = eina_list_append(ie->cache2->preload, ie); + evas_cserve2_image_preload(ie, _evas_cache2_image_preloaded_cb); + } + + return EINA_TRUE; +} + +static void +_evas_cache2_image_entry_preload_remove(Image_Entry *ie, const void *target) +{ + if (target) + { + Evas_Cache_Target *tg; + + EINA_INLIST_FOREACH(ie->targets, tg) + { + if (tg->target == target) + { + ie->targets = (Evas_Cache_Target *) + eina_inlist_remove(EINA_INLIST_GET(ie->targets), + EINA_INLIST_GET(tg)); + free(tg); + break; + } + } + } + else + { + Evas_Cache_Target *tg; + + while (ie->targets) + { + tg = ie->targets; + ie->targets = (Evas_Cache_Target *) + eina_inlist_remove(EINA_INLIST_GET(ie->targets), + EINA_INLIST_GET(tg)); + free(tg); + } + } + + // FIXME: Should also send message to the server to cancel the request. +} + +EAPI Image_Entry * +evas_cache2_image_copied_data(Evas_Cache2 *cache, unsigned int w, unsigned int h, DATA32 *image_data, int alpha, int cspace) +{ + Image_Entry *im; + + if ((cspace == EVAS_COLORSPACE_YCBCR422P601_PL) || + (cspace == EVAS_COLORSPACE_YCBCR422P709_PL) || + (cspace == EVAS_COLORSPACE_YCBCR422601_PL)) + w &= ~0x1; + + im = _evas_cache_image_entry_new(cache, NULL, NULL, NULL, NULL, NULL, NULL); + if (!im) + return NULL; + + im->space = cspace; + im->flags.alpha = alpha; + evas_cache2_image_surface_alloc(im, w, h); + if (cache->func.copied_data(im, w, h, image_data, alpha, cspace) != 0) + { + _evas_cache_image_entry_delete(cache, im); + return NULL; + } + + im->references = 1; + im->flags.loaded = EINA_TRUE; + if (cache->func.debug) cache->func.debug("copied-data", im); + + return im; +} + +EAPI Image_Entry * +evas_cache2_image_data(Evas_Cache2 *cache, unsigned int w, unsigned int h, DATA32 *image_data, int alpha, int cspace) +{ + Image_Entry *im; + + if ((cspace == EVAS_COLORSPACE_YCBCR422P601_PL) || + (cspace == EVAS_COLORSPACE_YCBCR422P709_PL) || + (cspace == EVAS_COLORSPACE_YCBCR422601_PL)) + w &= ~0x1; + + im = _evas_cache_image_entry_new(cache, NULL, NULL, NULL, NULL, NULL, NULL); + if (!im) return NULL; + im->w = w; + im->h = h; + im->flags.alpha = alpha; + im->flags.loaded = 1; + if (cache->func.data(im, w, h, image_data, alpha, cspace) != 0) + { + _evas_cache_image_entry_delete(cache, im); + return NULL; + } + im->references = 1; + if (cache->func.debug) cache->func.debug("data", im); + return im; +} + +EAPI Image_Entry * +evas_cache2_image_empty(Evas_Cache2 *cache) +{ + Image_Entry *im; + + im = _evas_cache_image_entry_new(cache, NULL, NULL, NULL, NULL, NULL, NULL); + if (!im) + return NULL; + + im->references = 1; + return im; +} + +EAPI Image_Entry * +evas_cache2_image_size_set(Image_Entry *im, unsigned int w, unsigned h) +{ + Evas_Cache2 *cache; + Image_Entry *im2 = NULL; + int error; + + if ((im->space == EVAS_COLORSPACE_YCBCR422P601_PL) || + (im->space == EVAS_COLORSPACE_YCBCR422P709_PL) || + (im->space == EVAS_COLORSPACE_YCBCR422601_PL)) + w &= ~0x1; + + if ((im->w == w) && (im->h == h)) return im; + + cache = im->cache2; + im2 = _evas_cache_image_entry_new(cache, NULL, NULL, NULL, NULL, NULL, + NULL); + if (!im2) goto on_error; + + im2->flags.alpha = im->flags.alpha; + im2->space = im->space; + im2->load_opts = im->load_opts; + evas_cache2_image_surface_alloc(im2, w, h); + error = cache->func.size_set(im2, im, w, h); + if (error != 0) goto on_error; + + im2->references = 1; + + evas_cache2_image_close(im); + return im2; + +on_error: + if (im2) + _evas_cache_image_entry_delete(cache, im2); + return NULL; +} + +EAPI Evas_Cache2 * +evas_cache2_init(const Evas_Cache2_Image_Func *cb) +{ + Evas_Cache2 *cache = calloc(1, sizeof(Evas_Cache2)); + + cache->func = *cb; + cache->activ = eina_hash_string_superfast_new(NULL); + cache->inactiv = eina_hash_string_superfast_new(NULL); + + return cache; +} + +EAPI void +evas_cache2_shutdown(Evas_Cache2 *cache) +{ + eina_hash_free(cache->activ); + eina_hash_free(cache->inactiv); + + free(cache); +} + +static void +_create_hash_key(char *hkey, const char *path, size_t pathlen, const char *key, size_t keylen, RGBA_Image_Loadopts *lo) +{ + const char *ckey = "(null)"; + size_t size; + + /* generate hkey from file+key+load opts */ + memcpy(hkey, path, pathlen); + size = pathlen; + memcpy(hkey + size, "//://", 5); + size += 5; + if (key) ckey = key; + memcpy(hkey + size, ckey, keylen); + size += keylen; + if (lo) + { + memcpy(hkey + size, "//@/", 4); + size += 4; + size += eina_convert_xtoa(lo->scale_down_by, hkey + size); + hkey[size] = '/'; + size += 1; + size += eina_convert_dtoa(lo->dpi, hkey + size); + hkey[size] = '/'; + size += 1; + size += eina_convert_xtoa(lo->w, hkey + size); + hkey[size] = 'x'; + size += 1; + size += eina_convert_xtoa(lo->h, hkey + size); + hkey[size] = '/'; + size += 1; + size += eina_convert_xtoa(lo->region.x, hkey + size); + hkey[size] = '+'; + size += 1; + size += eina_convert_xtoa(lo->region.y, hkey + size); + hkey[size] = '.'; + size += 1; + size += eina_convert_xtoa(lo->region.w, hkey + size); + hkey[size] = 'x'; + size += 1; + size += eina_convert_xtoa(lo->region.h, hkey + size); + + if (lo->orientation) + { + hkey[size] = '/'; + size += 1; + hkey[size] = 'o'; + size += 1; + } + } + hkey[size] = '\0'; +} + +EAPI Image_Entry * +evas_cache2_image_open(Evas_Cache2 *cache, const char *path, const char *key, RGBA_Image_Loadopts *lo, int *error) +{ + size_t size; + size_t pathlen; + size_t keylen; + char *hkey; + Image_Entry *im; + int stat_done = 0, stat_failed = 0; + struct stat st; + Image_Timestamp tstamp; + Evas_Image_Load_Opts prevent = { 0, 0.0, 0, 0, 0, { 0, 0, 0, 0 }, EINA_FALSE }; + + if ((!path) || ((!path) && (!key))) + { + *error = EVAS_LOAD_ERROR_GENERIC; + return NULL; + } + + pathlen = strlen(path); + keylen = key ? strlen(key) : 6; + size = pathlen + keylen + 132; + hkey = alloca(sizeof(char) * size); + + _create_hash_key(hkey, path, pathlen, key, keylen, lo); + DBG("Looking at the hash for key '%s'", hkey); + + /* use local var to copy default load options to the image entry */ + if ((!lo) || + (lo && + (lo->scale_down_by == 0) && + (lo->dpi == 0.0) && + ((lo->w == 0) || (lo->h == 0)) && + ((lo->region.w == 0) || (lo->region.h == 0)) && + (lo->orientation == 0) + )) + { + lo = &prevent; + } + + im = eina_hash_find(cache->activ, hkey); + + if (im) + { + int ok = 1; + DBG("Found entry on active hash for key: '%s'", hkey); + + stat_done = 1; + if (stat(path, &st) < 0) + { + stat_failed = 1; + ok = 0; + } + else if (!_timestamp_compare(&(im->tstamp), &st)) ok = 0; + if (ok) goto on_ok; + /* image we found doesn't match what's on disk (stat info wise) + * so dirty the active cache entry so we never find it again. this + * also implicitly guarantees that we only have 1 active copy + * of an image at a given key. we wither find it and keep re-reffing + * it or we dirty it and get it out */ + DBG("Entry on inactive hash was invalid (file changed or deleted)."); + _evas_cache_image_dirty_add(im); + im = NULL; + } + + im = eina_hash_find(cache->inactiv, hkey); + + if (im) + { + int ok = 1; + DBG("Found entry on inactive hash for key: '%s'", hkey); + + if (!stat_done) + { + stat_done = 1; + if (stat(path, &st) < 0) + { + stat_failed = 1; + ok = 0; + } + else if (!_timestamp_compare(&(im->tstamp), &st)) ok = 0; + } + else if (!_timestamp_compare(&(im->tstamp), &st)) ok = 0; + + if (ok) + { + /* remove from lru and make it active again */ + _evas_cache_image_lru_del(im); + _evas_cache_image_activ_add(im); + goto on_ok; + } + DBG("Entry on inactive hash was invalid (file changed or deleted)."); + /* as avtive cache find - if we match in lru and its invalid, dirty */ + _evas_cache_image_dirty_add(im); + /* this image never used, so it have to be deleted */ + _evas_cache_image_entry_delete(cache, im); + im = NULL; + } + if (stat_failed) goto on_stat_error; + + if (!stat_done) + { + if (stat(path, &st) < 0) goto on_stat_error; + } + _timestamp_build(&tstamp, &st); + DBG("Creating a new entry for key '%s'.", hkey); + im = _evas_cache_image_entry_new(cache, hkey, &tstamp, path, key, + lo, error); + if (!im) goto on_stat_error; + +on_ok: + *error = EVAS_LOAD_ERROR_NONE; + DBG("Using entry on hash for key '%s'", hkey); + + im->references++; + + return im; + +on_stat_error: +#ifndef _WIN32 + if ((errno == ENOENT) || (errno == ENOTDIR) || + (errno == ENAMETOOLONG) || (errno == ELOOP)) +#else + if (errno == ENOENT) +#endif + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; +#ifndef _WIN32 + else if ((errno == ENOMEM) || (errno == EOVERFLOW)) +#else + else if (errno == ENOMEM) +#endif + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + else if (errno == EACCES) + *error = EVAS_LOAD_ERROR_PERMISSION_DENIED; + else + *error = EVAS_LOAD_ERROR_GENERIC; + + if (im) _evas_cache_image_entry_delete(cache, im); + return NULL; +} + +EAPI int +evas_cache2_image_open_wait(Image_Entry *im) +{ + DBG("Wait for open image '%s' '%s'", im->file, im->key); + if (evas_cserve2_image_load_wait(im) != CSERVE2_NONE) + return EVAS_LOAD_ERROR_GENERIC; + + return EVAS_LOAD_ERROR_NONE; +} + +EAPI void +evas_cache2_image_close(Image_Entry *im) +{ + Evas_Cache2 *cache; + int references; + + im->references--; + if (im->references < 0) + { + ERR("image with negative references: %d", im->references); + im->references = 0; + } + + references = im->references; + cache = im->cache2; + + if (references > 0) + return; + + if (im->flags.dirty) + { + _evas_cache_image_entry_delete(cache, im); + return; + } + + _evas_cache_image_lru_add(im); + if (cache) + evas_cache2_flush(cache); +} + +EAPI int +evas_cache2_image_load_data(Image_Entry *ie) +{ + int error = EVAS_LOAD_ERROR_NONE; + + if ((ie->flags.loaded) && (!ie->flags.animated)) + return error; + + ie->flags.in_progress = EINA_TRUE; + + DBG("try cserve2 image data '%s' '%s'", + ie->file, ie->key ? ie->key : ""); + if (evas_cserve2_image_data_load(ie)) + { + evas_cserve2_image_load_data_wait(ie); + RGBA_Image *im = (RGBA_Image *)ie; + im->image.data = evas_cserve2_image_data_get(ie); + DBG("try cserve2 image data '%s' '%s' loaded!", + ie->file, ie->key ? ie->key : ""); + if (im->image.data) + { + im->image.no_free = 1; + error = EVAS_LOAD_ERROR_NONE; + } + else + { + ERR("Failed to load data for image '%s' '%s'.", + ie->file, ie->key ? ie->key : ""); + error = EVAS_LOAD_ERROR_GENERIC; + } + } + else + { + ERR("Couldn't send LOAD message to cserve2."); + error = EVAS_LOAD_ERROR_GENERIC; + } + + ie->flags.in_progress = EINA_FALSE; + ie->flags.loaded = 1; + + if (error != EVAS_LOAD_ERROR_NONE) + ie->flags.loaded = 0; + + return error; +} + +EAPI void +evas_cache2_image_unload_data(Image_Entry *im) +{ + // FIXME: This function seems pretty useless, since we always have + // to send an UNLOAD message to the server when closing an image, + // even if we didn't send a LOAD message before, because the SETOPTS + // message increases the image refcount. + if (im->flags.in_progress) + return; + + if ((!im->file)) + return; + + if (!im->flags.loaded) + return; + +// if (im->data2) + im->flags.loaded = 0; +} + +EAPI void +evas_cache2_image_preload_data(Image_Entry *im, const void *target) +{ + RGBA_Image *img = (RGBA_Image *)im; + + if ((im->flags.loaded) && (img->image.data)) + { + evas_object_inform_call_image_preloaded((Evas_Object *)target); + return; + } + + if (!_evas_cache2_image_entry_preload_add(im, target)) + evas_object_inform_call_image_preloaded((Evas_Object *)target); +} + +EAPI void +evas_cache2_image_preload_cancel(Image_Entry *im, const void *target) +{ + if (!target) + return; + + _evas_cache2_image_entry_preload_remove(im, target); +} + +EAPI DATA32 * +evas_cache2_image_pixels(Image_Entry *im) +{ + return im->cache2->func.surface_pixels(im); +} + +EAPI Image_Entry * +evas_cache2_image_writable(Image_Entry *im) +{ + Evas_Cache2 *cache = im->cache2; + Image_Entry *im2 = NULL; + + if (!im->cache_key) + { + if (!im->flags.dirty) + _evas_cache_image_dirty_add(im); + return im; + } + + im2 = evas_cache2_image_copied_data(cache, im->w, im->h, + evas_cache2_image_pixels(im), + im->flags.alpha, im->space); + if (!im2) + goto on_error; + + evas_cache2_image_close(im); + return im2; + +on_error: + if (im2) + _evas_cache_image_entry_delete(cache, im2); + return NULL; +} + +EAPI Image_Entry * +evas_cache2_image_dirty(Image_Entry *im, unsigned int x, unsigned int y, unsigned int w, unsigned int h) +{ + Evas_Cache2 *cache = im->cache2; + Image_Entry *im2 = NULL; + + if (!im->cache_key) + { + if (!im->flags.dirty) + _evas_cache_image_dirty_add(im); + im2 = im; + } + else + { + im2 = evas_cache2_image_copied_data(cache, im->w, im->h, + evas_cache2_image_pixels(im), + im->flags.alpha, im->space); + if (!im2) + goto on_error; + + evas_cache2_image_close(im); + } + + if (cache->func.dirty_region) + cache->func.dirty_region(im2, x, y, w, h); + + return im2; + +on_error: + if (im2) + _evas_cache_image_entry_delete(cache, im2); + evas_cache2_image_close(im); + return NULL; +} + +EAPI int +evas_cache2_flush(Evas_Cache2 *cache) +{ + if (cache->limit == -1) return -1; + + while ((cache->lru) && (cache->limit < cache->usage)) + { + Image_Entry *im; + + im = (Image_Entry *)cache->lru->last; + DBG("Remove unused entry from cache."); + _evas_cache_image_entry_delete(cache, im); + } + + return cache->usage; +} + +EAPI void +evas_cache2_limit_set(Evas_Cache2 *cache, int limit) +{ + if (cache->limit == limit) + return; + + DBG("Cache2 limit set to %d", limit); + + cache->limit = limit; + + evas_cache2_flush(cache); +} + +EAPI int +evas_cache2_limit_get(Evas_Cache2 *cache) +{ + return cache->limit; +} + +EAPI int +evas_cache2_usage_get(Evas_Cache2 *cache) +{ + return cache->usage; +} diff --git a/src/lib/cache2/evas_cache2.h b/src/lib/cache2/evas_cache2.h new file mode 100644 index 0000000..7028338 --- /dev/null +++ b/src/lib/cache2/evas_cache2.h @@ -0,0 +1,87 @@ +#ifndef _EVAS_CACHE2_H +#define _EVAS_CACHE2_H + +typedef struct _Evas_Cache2_Image_Func Evas_Cache2_Image_Func; +typedef struct _Evas_Cache2 Evas_Cache2; + +struct _Evas_Cache2_Image_Func +{ + // Image_Entry *(*alloc)(void); + // void (*dealloc)(Image_Entry *im); + + /* The cache provide some helpers for surface manipulation. */ + int (*surface_alloc)(Image_Entry *im, unsigned int w, unsigned int h); + void (*surface_delete)(Image_Entry *im); + DATA32 *(*surface_pixels)(Image_Entry *im); + + /* The cache is doing the allocation and deallocation, you must just do the rest. */ + // int (*constructor)(Image_Entry *im); /**< return is EVAS_LOAD_ERROR_* or EVAS_LOAD_ERROR_NONE! */ + // void (*destructor)(Image_Entry *im); + + void (*dirty_region)(Image_Entry *im, unsigned int x, unsigned int y, unsigned int w, unsigned int h); + /* Only called when references > 0. Need to provide a fresh copie of im. */ + /* The destination surface does have a surface, but no allocated pixel data. */ + int (*dirty)(Image_Entry *dst, const Image_Entry *src); + /* Only called when references == 1. We will call drop on `im'. */ + /* The destination surface does not have any surface. */ + int (*size_set)(Image_Entry *dst, const Image_Entry *src, unsigned int w, unsigned int h); + + /* The destination surface does not have any surface. */ + int (*copied_data)(Image_Entry *dst, unsigned int w, unsigned int h, DATA32 *image_data, int alpha, int cspace); + /* The destination surface does not have any surface. */ + int (*data)(Image_Entry *dst, unsigned int w, unsigned int h, DATA32 *image_data, int alpha, int cspace); + int (*color_space)(Image_Entry *dst, int cspace); + + /* This function need to update im->w and im->h. */ + // int (*load)(Image_Entry *im); /**< return is EVAS_LOAD_ERROR_* or EVAS_LOAD_ERROR_NONE! */ + int (*mem_size_get)(Image_Entry *im); + void (*debug)(const char *context, Image_Entry *im); +}; + +struct _Evas_Cache2 +{ + Evas_Cache2_Image_Func func; + + Eina_List *preload; + + Eina_Hash *inactiv; + Eina_Hash *activ; + Eina_Inlist *dirty; + Eina_Inlist *lru; + int usage; + int limit; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +EAPI Evas_Cache2* evas_cache2_init(const Evas_Cache2_Image_Func *cb); +EAPI void evas_cache2_shutdown(Evas_Cache2 *cache); +EAPI Image_Entry * evas_cache2_image_open(Evas_Cache2 *cache, const char *path, const char *key, RGBA_Image_Loadopts *lo, int *error); +EAPI int evas_cache2_image_open_wait(Image_Entry *im); +EAPI void evas_cache2_image_close(Image_Entry *im); +EAPI int evas_cache2_image_load_data(Image_Entry *ie); +EAPI void evas_cache2_image_unload_data(Image_Entry *im); +EAPI void evas_cache2_image_preload_data(Image_Entry *im, const void *target); + +EAPI DATA32 * evas_cache2_image_pixels(Image_Entry *im); +EAPI Image_Entry * evas_cache2_image_writable(Image_Entry *im); +EAPI Image_Entry * evas_cache2_image_data(Evas_Cache2 *cache, unsigned int w, unsigned int h, DATA32 *image_data, int alpha, int cspace); +EAPI Image_Entry * evas_cache2_image_copied_data(Evas_Cache2 *cache, unsigned int w, unsigned int h, DATA32 *image_data, int alpha, int cspace); +EAPI Image_Entry * evas_cache2_image_size_set(Image_Entry *im, unsigned int w, unsigned h); +EAPI Image_Entry * evas_cache2_image_dirty(Image_Entry *im, unsigned int x, unsigned int y, unsigned int w, unsigned int h); +EAPI Image_Entry * evas_cache2_image_empty(Evas_Cache2 *cache); +EAPI void evas_cache2_image_surface_alloc(Image_Entry *ie, int w, int h); + +EAPI int evas_cache2_flush(Evas_Cache2 *cache); +EAPI void evas_cache2_limit_set(Evas_Cache2 *cache, int limit); +EAPI int evas_cache2_limit_get(Evas_Cache2 *cache); +EAPI int evas_cache2_usage_get(Evas_Cache2 *cache); + +#ifdef __cplusplus +} +#endif + + +#endif /* _EVAS_CACHE2_H */ diff --git a/src/lib/canvas/Makefile.am b/src/lib/canvas/Makefile.am index 875107e..4e4e83c 100644 --- a/src/lib/canvas/Makefile.am +++ b/src/lib/canvas/Makefile.am @@ -5,6 +5,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib/include \ -I$(top_srcdir)/src/lib/cserve \ +-I$(top_srcdir)/src/lib/cserve2 \ -DPACKAGE_BIN_DIR=\"$(bindir)\" \ -DPACKAGE_LIB_DIR=\"$(libdir)\" \ -DPACKAGE_DATA_DIR=\"$(datadir)/$(PACKAGE)\" \ diff --git a/src/lib/canvas/evas_main.c b/src/lib/canvas/evas_main.c index 309eb98..b02405d 100644 --- a/src/lib/canvas/evas_main.c +++ b/src/lib/canvas/evas_main.c @@ -1,6 +1,9 @@ #include "evas_common.h" #include "evas_private.h" #include "evas_cs.h" +#ifdef EVAS_CSERVE2 +#include "evas_cs2_private.h" +#endif #ifdef LKDEBUG EAPI Eina_Bool lockdebug = EINA_FALSE; @@ -47,6 +50,9 @@ evas_init(void) #ifdef EVAS_CSERVE if (getenv("EVAS_CSERVE")) evas_cserve_init(); #endif +#ifdef EVAS_CSERVE2 + if (getenv("EVAS_CSERVE2")) evas_cserve2_init(); +#endif #ifdef BUILD_ASYNC_PRELOAD _evas_preload_thread_init(); #endif diff --git a/src/lib/canvas/evas_render.c b/src/lib/canvas/evas_render.c index 460d0fe..f9c7f33 100644 --- a/src/lib/canvas/evas_render.c +++ b/src/lib/canvas/evas_render.c @@ -1366,6 +1366,10 @@ evas_render_updates_internal(Evas *e, MAGIC_CHECK_END(); if (!e->changed) return NULL; +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + evas_cserve2_dispatch(); +#endif evas_call_smarts_calculate(e); RD("[--- RENDER EVAS (size: %ix%i)\n", e->viewport.w, e->viewport.h); diff --git a/src/lib/cserve2/Makefile.am b/src/lib/cserve2/Makefile.am new file mode 100644 index 0000000..252899f --- /dev/null +++ b/src/lib/cserve2/Makefile.am @@ -0,0 +1,27 @@ +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +-I. \ +-I$(top_srcdir)/src/lib \ +-I$(top_srcdir)/src/lib/include \ +@EINA_CFLAGS@ \ +@FREETYPE_CFLAGS@ \ +@FRIBIDI_CFLAGS@ \ +@EET_CFLAGS@ \ +@FONTCONFIG_CFLAGS@ \ +@pthread_cflags@ \ +@PIXMAN_CFLAGS@ + +if EVAS_CSERVE2 + +noinst_LTLIBRARIES = libevas_cserve2.la + +libevas_cserve2_la_SOURCES = \ +evas_cs2.h \ +evas_cs2_private.h \ +evas_cs2_image_data.c \ +evas_cs2_client.c + +libevas_cserve2_la_LIBADD = @EINA_LIBS@ + +endif diff --git a/src/lib/cserve2/evas_cs2.h b/src/lib/cserve2/evas_cs2.h new file mode 100644 index 0000000..a33857f --- /dev/null +++ b/src/lib/cserve2/evas_cs2.h @@ -0,0 +1,133 @@ +#ifndef EVAS_CS2_H +#define EVAS_CS2_H 1 + +#include + +#ifdef EVAS_CSERVE2 + +typedef enum { + CSERVE2_OPEN = 1, + CSERVE2_OPENED, + CSERVE2_SETOPTS, + CSERVE2_SETOPTSED, + CSERVE2_LOAD, + CSERVE2_LOADED, + CSERVE2_PRELOAD, + CSERVE2_PRELOADED, + CSERVE2_UNLOAD, + CSERVE2_CLOSE, + CSERVE2_ERROR +} Message_Type; + +typedef enum { + CSERVE2_NONE, + CSERVE2_GENERIC, + CSERVE2_DOES_NOT_EXIST, + CSERVE2_PERMISSION_DENIED, + CSERVE2_RESOURCE_ALLOCATION_FAILED, + CSERVE2_CORRUPT_FILE, + CSERVE2_UNKNOWN_FORMAT, + CSERVE2_INVALID_COMMAND, + CSERVE2_LOADER_DIED, + CSERVE2_LOADER_EXEC_ERR, + CSERVE2_INVALID_CACHE, // invalid cserve cache entry + CSERVE2_FILE_CHANGED, + CSERVE2_REQUEST_CANCEL +} Error_Type; + +struct _Msg_Base { + int type; + unsigned int rid; +}; + +typedef struct _Msg_Base Msg_Base; + +struct _Msg_Open { + Msg_Base base; + unsigned int file_id; + int path_offset; + int key_offset; +}; + +struct _Msg_Opened { + Msg_Base base; + struct { + int w, h; + int frame_count; + int loop_count; + int loop_hint; /* include Evas.h? Copy the enum around? */ + Eina_Bool alpha : 1; + } image; +}; + +struct _Msg_Setopts { + Msg_Base base; + unsigned int file_id; + unsigned int image_id; + struct { + double dpi; + int w, h; + int scale_down; + int rx, ry, rw, rh; + Eina_Bool orientation; + } opts; +}; + +struct _Msg_Setoptsed { + Msg_Base base; +}; + +struct _Msg_Load { + Msg_Base base; + unsigned int image_id; +}; + +struct _Msg_Loaded { + Msg_Base base; + struct { + int mmap_offset; + int use_offset; + int mmap_size; + int image_size; + } shm; + Eina_Bool alpha_sparse : 1; +}; + +struct _Msg_Preload { + Msg_Base base; + unsigned int image_id; +}; + +struct _Msg_Preloaded { + Msg_Base base; +}; + +struct _Msg_Unload { + Msg_Base base; + unsigned int image_id; +}; + +struct _Msg_Close { + Msg_Base base; + unsigned int file_id; +}; + +struct _Msg_Error { + Msg_Base base; + int error; +}; + +typedef struct _Msg_Open Msg_Open; +typedef struct _Msg_Opened Msg_Opened; +typedef struct _Msg_Setopts Msg_Setopts; +typedef struct _Msg_Setoptsed Msg_Setoptsed; +typedef struct _Msg_Load Msg_Load; +typedef struct _Msg_Loaded Msg_Loaded; +typedef struct _Msg_Preload Msg_Preload; +typedef struct _Msg_Preloaded Msg_Preloaded; +typedef struct _Msg_Unload Msg_Unload; +typedef struct _Msg_Close Msg_Close; +typedef struct _Msg_Error Msg_Error; + +#endif +#endif diff --git a/src/lib/cserve2/evas_cs2_client.c b/src/lib/cserve2/evas_cs2_client.c new file mode 100644 index 0000000..adc582a --- /dev/null +++ b/src/lib/cserve2/evas_cs2_client.c @@ -0,0 +1,742 @@ +#include "config.h" + +#include +#include +#include + +#include +#include +#include + +#include + +#include "evas_cs2.h" +#include "evas_cs2_private.h" + +#ifdef EVAS_CSERVE2 + +typedef void (*Op_Callback)(void *data, const void *msg); + +struct _File_Entry { + unsigned int file_id; +}; + +struct _Client_Request { + Message_Type type; + unsigned int rid; + Op_Callback cb; + void *data; +}; + +typedef struct _File_Entry File_Entry; +typedef struct _Client_Request Client_Request; + +static int cserve2_init = 0; +static int socketfd = -1; + +static unsigned int _rid_count = 0; +static unsigned int _file_id = 0; +static unsigned int _data_id = 0; + +static Eina_List *_requests = NULL; + +static struct sockaddr_un socksize; +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX sizeof(socksize.sun_path) +#endif + +static void +_socket_path_set(char *path) +{ + char *env; + char buf[UNIX_PATH_MAX]; + + env = getenv("EVAS_CSERVE2_SOCKET"); + if (env && env[0]) + { + strncpy(path, env, UNIX_PATH_MAX - 1); + return; + } + + env = getenv("XDG_RUNTIME_DIR"); + if (!env || !env[0]) + { + env = getenv("HOME"); + if (!env || !env[0]) + { + env = getenv("TMPDIR"); + if (!env || !env[0]) + env = "/tmp"; + } + } + + snprintf(buf, sizeof(buf), "%s/evas-cserve2-%x.socket", env, getuid()); + /* FIXME: check we can actually create this socket */ + strcpy(path, buf); +} + +static Eina_Bool +_server_connect(void) +{ + int s, len; + struct sockaddr_un remote; + + if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + { + ERR("socket"); + return EINA_FALSE; + } + + remote.sun_family = AF_UNIX; + _socket_path_set(remote.sun_path); + len = strlen(remote.sun_path) + sizeof(remote.sun_family); + if (connect(s, (struct sockaddr *)&remote, len) == -1) + { + ERR("connect"); + return EINA_FALSE; + } + + fcntl(s, F_SETFL, O_NONBLOCK); + + socketfd = s; + + DBG("connected to cserve2 server."); + return EINA_TRUE; +} + +static void +_server_disconnect(void) +{ + close(socketfd); + socketfd = -1; +} + +static void +_request_answer_add(Message_Type type, unsigned int rid, Op_Callback cb, void *data) +{ + Client_Request *cr = calloc(1, sizeof(*cr)); + + cr->type = type; + cr->rid = rid; + cr->cb = cb; + cr->data = data; + + _requests = eina_list_append(_requests, cr); +} + +static Eina_Bool +_server_send(const void *buf, int size, Op_Callback cb, void *data) +{ + const Msg_Base *msg; + if (send(socketfd, &size, sizeof(size), MSG_NOSIGNAL) == -1) + { + ERR("Couldn't send message size to server."); + return EINA_FALSE; + } + if (send(socketfd, buf, size, MSG_NOSIGNAL) == -1) + { + ERR("Couldn't send message body to server."); + return EINA_FALSE; + } + + msg = buf; + switch (msg->type) + { + case CSERVE2_OPEN: + case CSERVE2_SETOPTS: + case CSERVE2_LOAD: + case CSERVE2_PRELOAD: + _request_answer_add(msg->type, msg->rid, cb, data); + break; + default: + break; + } + + return EINA_TRUE; +} + +static int sr_size = 0; +static int sr_got = 0; +static char *sr_buf = NULL; + +static void * +_server_read(int *size) +{ + int n; + void *ret; + + if (sr_size) + goto get_data; + + n = recv(socketfd, &sr_size, sizeof(sr_size), 0); + if (n < 0) + return NULL; + + sr_buf = malloc(sr_size); + +get_data: + n = recv(socketfd, sr_buf + sr_got, sr_size - sr_got, 0); + if (n < 0) + return NULL; + + sr_got += n; + if (sr_got < sr_size) + return NULL; + + *size = sr_size; + sr_size = 0; + sr_got = 0; + ret = sr_buf; + sr_buf = NULL; + + return ret; +} + +int +evas_cserve2_init(void) +{ + if (cserve2_init++) + return cserve2_init; + + DBG("Connecting to cserve2."); + if (!_server_connect()) + { + cserve2_init = 0; + return 0; + } + + return cserve2_init; +} + +int +evas_cserve2_shutdown(void) +{ + if ((--cserve2_init) > 0) + return cserve2_init; + + DBG("Disconnecting from cserve2."); + _server_disconnect(); + + return cserve2_init; +} + +int +evas_cserve2_use_get(void) +{ + return cserve2_init; +} + +static unsigned int +_next_rid(void) +{ + if (!_rid_count) + _rid_count++; + + return _rid_count++; +} + +static unsigned int +_server_dispatch(void) +{ + int size; + unsigned int rid; + Eina_List *l, *l_next; + Client_Request *cr; + Msg_Base *msg; + + msg = _server_read(&size); + if (!msg) + return 0; + + EINA_LIST_FOREACH_SAFE(_requests, l, l_next, cr) + { + if (cr->rid != msg->rid) // dispatch this answer + continue; + + _requests = eina_list_remove_list(_requests, l); + if (cr->cb) + cr->cb(cr->data, msg); + free(cr); + } + + rid = msg->rid; + free(msg); + + return rid; +} + +static void +_server_dispatch_until(unsigned int rid) +{ + Eina_Bool done = EINA_FALSE; + + while (!done) + { + if (_server_dispatch() == rid) + done = EINA_TRUE; + } +} + +static void +_image_opened_cb(void *data, const void *msg_received) +{ + const Msg_Base *answer = msg_received; + const Msg_Opened *msg = msg_received; + Image_Entry *ie = data; + + ie->open_rid = 0; + + if (answer->type == CSERVE2_ERROR) + { + const Msg_Error *msg_error = msg_received; + ERR("Couldn't open image: '%s':'%s'; error: %d", + ie->file, ie->key, msg_error->error); + free(ie->data1); + ie->data1 = NULL; + return; + } + + ie->w = msg->image.w; + ie->h = msg->image.h; + ie->flags.alpha = msg->image.alpha; + ie->loop_hint = msg->image.loop_hint; + ie->loop_count = msg->image.loop_count; + ie->frame_count = msg->image.frame_count; +} + +static void +_image_loaded_cb(void *data, const void *msg_received) +{ + const Msg_Base *answer = msg_received; + const Msg_Loaded *msg = msg_received; + Image_Entry *ie = data; + Data_Entry *dentry = ie->data2; + const char *shmpath; + int fd; + + ie->load_rid = 0; + + if (!ie->data2) + return; + + if (answer->type == CSERVE2_ERROR) + { + const Msg_Error *msg_error = msg_received; + ERR("Couldn't load image: '%s':'%s'; error: %d", + ie->file, ie->key, msg_error->error); + free(ie->data2); + ie->data2 = NULL; + return; + } + + shmpath = ((const char *)msg) + sizeof(*msg); + + // dentry->shm.path = strdup(shmpath); + dentry->shm.mmap_offset = msg->shm.mmap_offset; + dentry->shm.use_offset = msg->shm.use_offset; + dentry->shm.mmap_size = msg->shm.mmap_size; + dentry->shm.image_size = msg->shm.image_size; + + fd = shm_open(shmpath, O_RDONLY, S_IRUSR); + if (fd < 0) + { + free(dentry); + ie->data2 = NULL; + return; + } + + dentry->shm.data = mmap(NULL, dentry->shm.mmap_size, PROT_READ, + MAP_SHARED, fd, dentry->shm.mmap_offset); + + if (dentry->shm.data == MAP_FAILED) + { + free(dentry); + ie->data2 = NULL; + } + + close(fd); + + if (ie->data2) + { + ie->flags.loaded = EINA_TRUE; + ie->flags.alpha_sparse = msg->alpha_sparse; + } +} + +static void +_image_preloaded_cb(void *data, const void *msg_received) +{ + const Msg_Base *answer = msg_received; + const Msg_Opened *msg = msg_received; + Image_Entry *ie = data; + Data_Entry *dentry = ie->data2; + + DBG("Received PRELOADED for RID: %d", answer->rid); + ie->preload_rid = 0; + + if (answer->type == CSERVE2_ERROR) + { + const Msg_Error *msg_error = msg_received; + ERR("Couldn't preload image: '%s':'%s'; error: %d", + ie->file, ie->key, msg_error->error); + dentry->preloaded_cb(data, EINA_FALSE); + dentry->preloaded_cb = NULL; + return; + } + + if (dentry && (dentry->preloaded_cb)) + { + dentry->preloaded_cb(data, EINA_TRUE); + dentry->preloaded_cb = NULL; + } +} + +static const char * +_build_absolute_path(const char *path, char buf[], int size) +{ + char *p; + int len; + + if (!path) + return NULL; + + p = buf; + + if (path[0] == '/') + strncpy(p, path, size); + else if (path[0] == '~') + { + const char *home = getenv("HOME"); + if (!home) + return NULL; + strncpy(p, home, size); + len = strlen(p); + size -= len + 1; + p += len; + p[0] = '/'; + p++; + strncpy(p, path + 2, size); + } + else + { + if (!getcwd(p, size)) + return NULL; + len = strlen(p); + size -= len + 1; + p += len; + p[0] = '/'; + p++; + strncpy(p, path, size); + } + + return buf; +} + +static unsigned int +_image_open_server_send(Image_Entry *ie, const char *file, const char *key, RGBA_Image_Loadopts *lopt) +{ + int flen, klen; + int size; + char *buf; + char filebuf[PATH_MAX]; + Msg_Open msg_open; + File_Entry *fentry; + + if (cserve2_init == 0) + { + ERR("Server not initialized."); + return 0; + } + + if (!key) key = ""; + + _build_absolute_path(file, filebuf, sizeof(filebuf)); + + fentry = calloc(1, sizeof(*fentry)); + + memset(&msg_open, 0, sizeof(msg_open)); + + fentry->file_id = ++_file_id; + if (fentry->file_id == 0) + fentry->file_id = ++_file_id; + + flen = strlen(filebuf) + 1; + klen = strlen(key) + 1; + + msg_open.base.rid = _next_rid(); + msg_open.base.type = CSERVE2_OPEN; + msg_open.file_id = fentry->file_id; + msg_open.path_offset = 0; + msg_open.key_offset = flen; + + size = sizeof(msg_open) + flen + klen; + buf = malloc(size); + if (!buf) return EINA_FALSE; + memcpy(buf, &msg_open, sizeof(msg_open)); + memcpy(buf + sizeof(msg_open), filebuf, flen); + memcpy(buf + sizeof(msg_open) + flen, key, klen); + + if (!_server_send(buf, size, _image_opened_cb, ie)) + { + ERR("Couldn't send message to server."); + free(buf); + return 0; + } + + free(buf); + ie->data1 = fentry; + + return msg_open.base.rid; +} + +unsigned int +_image_setopts_server_send(Image_Entry *ie) +{ + File_Entry *fentry; + Data_Entry *dentry; + Msg_Setopts msg; + + if (cserve2_init == 0) + return 0; + + fentry = ie->data1; + + dentry = calloc(1, sizeof(*dentry)); + + memset(&msg, 0, sizeof(msg)); + dentry->image_id = ++_data_id; + if (dentry->image_id == 0) + dentry->image_id = ++_data_id; + + msg.base.rid = _next_rid(); + msg.base.type = CSERVE2_SETOPTS; + msg.file_id = fentry->file_id; + msg.image_id = dentry->image_id; + + if (!_server_send(&msg, sizeof(msg), 0, NULL)) + return 0; + + ie->data2 = dentry; + + return msg.base.rid; +} + +unsigned int +_image_load_server_send(Image_Entry *ie) +{ + Data_Entry *dentry; + Msg_Load msg; + + if (cserve2_init == 0) + return 0; + + if (!ie->data1) + { + ERR("No data for opened file."); + return 0; + } + + dentry = ie->data2; + + memset(&msg, 0, sizeof(msg)); + + msg.base.rid = _next_rid(); + msg.base.type = CSERVE2_LOAD; + msg.image_id = dentry->image_id; + + if (!_server_send(&msg, sizeof(msg), _image_loaded_cb, ie)) + return 0; + + return msg.base.rid; +} + +unsigned int +_image_preload_server_send(Image_Entry *ie, void (*preloaded_cb)(void *im, Eina_Bool success)) +{ + Data_Entry *dentry; + Msg_Preload msg; + + if (cserve2_init == 0) + return 0; + + dentry = ie->data2; + dentry->preloaded_cb = preloaded_cb; + + memset(&msg, 0, sizeof(msg)); + + msg.base.rid = _next_rid(); + msg.base.type = CSERVE2_PRELOAD; + msg.image_id = dentry->image_id; + + if (!_server_send(&msg, sizeof(msg), _image_preloaded_cb, ie)) + return 0; + + return msg.base.rid; +} + +unsigned int +_image_close_server_send(Image_Entry *ie) +{ + Msg_Close msg; + File_Entry *fentry; + + if (cserve2_init == 0) + return 0; + + if (!ie->data1) + return 0; + + fentry = ie->data1; + + if (ie->data2) + { + free(ie->data2); + ie->data2 = NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.base.rid = _next_rid(); + msg.base.type = CSERVE2_CLOSE; + msg.file_id = fentry->file_id; + + free(fentry); + ie->data1 = NULL; + + if (!_server_send(&msg, sizeof(msg), NULL, NULL)) + return 0; + + return msg.base.rid; +} + +unsigned int +_image_unload_server_send(Image_Entry *ie) +{ + Msg_Unload msg; + Data_Entry *dentry; + + if (cserve2_init == 0) + return 0; + + if (!ie->data2) + return 0; + + dentry = ie->data2; + + // if (dentry->shm.path) + // free(dentry->shm.path); + memset(&msg, 0, sizeof(msg)); + msg.base.rid = _next_rid(); + msg.base.type = CSERVE2_UNLOAD; + msg.image_id = dentry->image_id; + + free(dentry); + ie->data2 = NULL; + + if (!_server_send(&msg, sizeof(msg), NULL, NULL)) + return 0; + + return msg.base.rid; +} + +Eina_Bool +evas_cserve2_image_load(Image_Entry *ie, const char *file, const char *key, RGBA_Image_Loadopts *lopt) +{ + unsigned int rid; + + rid = _image_open_server_send(ie, file, key, lopt); + if (!rid) + return EINA_FALSE; + + ie->open_rid = rid; + + _image_setopts_server_send(ie); + + // _server_dispatch_until(rid); + + if (ie->data1) + return EINA_TRUE; + else + return EINA_FALSE; +} + +int +evas_cserve2_image_load_wait(Image_Entry *ie) +{ + if (ie->open_rid) + { + _server_dispatch_until(ie->open_rid); + if (!ie->data1) + return CSERVE2_GENERIC; + return CSERVE2_NONE; + } + else + return CSERVE2_GENERIC; +} + +Eina_Bool +evas_cserve2_image_data_load(Image_Entry *ie) +{ + unsigned int rid; + + rid = _image_load_server_send(ie); + if (!rid) + return EINA_FALSE; + + ie->load_rid = rid; + + if (ie->data2) + return EINA_TRUE; + else + return EINA_FALSE; +} + +void +evas_cserve2_image_load_data_wait(Image_Entry *ie) +{ + if (ie->load_rid) + _server_dispatch_until(ie->load_rid); +} + +Eina_Bool +evas_cserve2_image_preload(Image_Entry *ie, void (*preloaded_cb)(void *im, Eina_Bool success)) +{ + unsigned int rid; + + if (!ie->data1) + return EINA_FALSE; + + rid = _image_preload_server_send(ie, preloaded_cb); + if (!rid) + return EINA_FALSE; + + ie->preload_rid = rid; + + return EINA_FALSE; +} + +void +evas_cserve2_image_free(Image_Entry *ie) +{ + if (!ie->data1) + return; + + if (!_image_close_server_send(ie)) + WRN("Couldn't send close message to cserve2."); +} + +void +evas_cserve2_image_unload(Image_Entry *ie) +{ + if (!ie->data2) + return; + + if (!_image_unload_server_send(ie)) + WRN("Couldn't send unload message to cserve2."); +} + +void +evas_cserve2_dispatch(void) +{ + _server_dispatch_until(0); +} +#endif diff --git a/src/lib/cserve2/evas_cs2_image_data.c b/src/lib/cserve2/evas_cs2_image_data.c new file mode 100644 index 0000000..a5e49da --- /dev/null +++ b/src/lib/cserve2/evas_cs2_image_data.c @@ -0,0 +1,23 @@ +#include "config.h" + +#include +#include +#include + +#include "evas_cs2.h" +#include "evas_cs2_private.h" + +#ifdef EVAS_CSERVE2 + +void * +evas_cserve2_image_data_get(Image_Entry *ie) +{ + Data_Entry *dentry = ie->data2; + + if (!dentry) + return NULL; + + return dentry->shm.data; +} + +#endif diff --git a/src/lib/cserve2/evas_cs2_private.h b/src/lib/cserve2/evas_cs2_private.h new file mode 100644 index 0000000..5521fd8 --- /dev/null +++ b/src/lib/cserve2/evas_cs2_private.h @@ -0,0 +1,34 @@ +#ifndef EVAS_CS2_PRIVATE_H +#define EVAS_CS2_PRIVATE_H 1 + +#include "evas_common.h" + +struct _Data_Entry { + unsigned int image_id; + void (*preloaded_cb)(void *, Eina_Bool); + struct { + const char *path; + int mmap_offset; + int use_offset; + int mmap_size; + int image_size; + void *data; + } shm; +}; + +typedef struct _Data_Entry Data_Entry; + +int evas_cserve2_init(void); +int evas_cserve2_shutdown(void); +EAPI int evas_cserve2_use_get(void); +Eina_Bool evas_cserve2_image_load(Image_Entry *ie, const char *file, const char *key, RGBA_Image_Loadopts *lopt); +int evas_cserve2_image_load_wait(Image_Entry *ie); +Eina_Bool evas_cserve2_image_data_load(Image_Entry *ie); +void evas_cserve2_image_load_data_wait(Image_Entry *ie); +void evas_cserve2_image_free(Image_Entry *ie); +void evas_cserve2_image_unload(Image_Entry *ie); +Eina_Bool evas_cserve2_image_preload(Image_Entry *ie, void (*preloaded_cb)(void *im, Eina_Bool success)); +void evas_cserve2_dispatch(void); + +void *evas_cserve2_image_data_get(Image_Entry *ie); +#endif diff --git a/src/lib/engines/common/Makefile.am b/src/lib/engines/common/Makefile.am index 0937394..262c759 100644 --- a/src/lib/engines/common/Makefile.am +++ b/src/lib/engines/common/Makefile.am @@ -6,6 +6,7 @@ MAINTAINERCLEANFILES = Makefile.in AM_CPPFLAGS = -I. \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib/cserve \ + -I$(top_srcdir)/src/lib/cserve2 \ -I$(top_srcdir)/src/lib/include \ -DPACKAGE_BIN_DIR=\"$(bindir)\" \ -DPACKAGE_LIB_DIR=\"$(libdir)\" \ diff --git a/src/lib/engines/common/evas_image.h b/src/lib/engines/common/evas_image.h index 300697c..766c90c 100644 --- a/src/lib/engines/common/evas_image.h +++ b/src/lib/engines/common/evas_image.h @@ -18,6 +18,9 @@ EAPI void evas_common_image_set_alpha_sparse (Image_Entry /* EAPI RGBA_Image *evas_common_image_create (int w, int h); */ EAPI RGBA_Image *evas_common_image_new (unsigned int w, unsigned int h, unsigned int alpha); EAPI Evas_Cache_Image *evas_common_image_cache_get (void); +#ifdef EVAS_CSERVE2 +EAPI Evas_Cache2 *evas_common_image_cache2_get (void); +#endif EAPI void evas_common_image_set_cache (unsigned int size); EAPI int evas_common_image_get_cache (void); @@ -32,6 +35,8 @@ EAPI void evas_common_image_alpha_line_buffer_free (RGBA_Image * EAPI RGBA_Image *evas_common_load_image_from_file (const char *file, const char *key, RGBA_Image_Loadopts *lo, int *error); EAPI int evas_common_save_image_to_file (RGBA_Image *im, const char *file, const char *key, int quality, int compress); +EAPI void evas_common_rgba_image_scalecache_init(Image_Entry *ie); +EAPI void evas_common_rgba_image_scalecache_shutdown(Image_Entry *ie); EAPI void evas_common_rgba_image_scalecache_size_set(unsigned int size); EAPI unsigned int evas_common_rgba_image_scalecache_size_get(void); EAPI void evas_common_rgba_image_scalecache_flush(void); diff --git a/src/lib/engines/common/evas_image_load.c b/src/lib/engines/common/evas_image_load.c index 21ac5d4..386bb76 100644 --- a/src/lib/engines/common/evas_image_load.c +++ b/src/lib/engines/common/evas_image_load.c @@ -9,6 +9,9 @@ #include "evas_common.h" #include "evas_private.h" #include "evas_cs.h" +#ifdef EVAS_CSERVE2 +#include "evas_cs2_private.h" +#endif struct ext_loader_s { @@ -183,6 +186,20 @@ evas_common_load_rgba_image_module_from_file(Image_Entry *ie) } } #endif + +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + ERR("This function shouldn't be called anymore!"); + // DBG("try cserve2 '%s' '%s'", ie->file, ie->key ? ie->key : ""); + // if (evas_cserve2_image_load(ie, ie->file, ie->key, &(ie->load_opts))) + // { + // DBG("try cserve2 '%s' '%s' loaded!", + // ie->file, ie->key ? ie->key : ""); + // return EVAS_LOAD_ERROR_NONE; + // } + } +#endif if (stat(ie->file, &st) != 0 || S_ISDIR(st.st_mode)) { DBG("trying to open directory '%s' !", ie->file); @@ -333,6 +350,28 @@ evas_common_load_rgba_image_data_from_file(Image_Entry *ie) } #endif +#ifdef EVAS_CSERVE2 + if (ie->data1) + { + ERR("This function shouldn't be called anymore!"); + // DBG("try cserve2 image data '%s' '%s'", + // ie->file, ie->key ? ie->key : ""); + // if (evas_cserve2_image_data_load(ie)) + // { + // RGBA_Image *im = (RGBA_Image *)ie; + // im->image.data = evas_cserve2_image_data_get(ie); + // DBG("try cserve2 image data '%s' '%s' loaded!", + // ie->file, ie->key ? ie->key : ""); + // if (im->image.data) + // { + // im->image.no_free = 1; + // return EVAS_LOAD_ERROR_NONE; + // } + // } + // return EVAS_LOAD_ERROR_GENERIC; + } +#endif + if (!ie->info.module) return EVAS_LOAD_ERROR_GENERIC; // printf("load data [%p] %s %s\n", ie, ie->file, ie->key); diff --git a/src/lib/engines/common/evas_image_main.c b/src/lib/engines/common/evas_image_main.c index 0fcb3df..f8053a8 100644 --- a/src/lib/engines/common/evas_image_main.c +++ b/src/lib/engines/common/evas_image_main.c @@ -19,6 +19,9 @@ //#define SURFDBG 1 static Evas_Cache_Image * eci = NULL; +#ifdef EVAS_CSERVE2 +static Evas_Cache2 * eci2 = NULL; +#endif static int reference = 0; /* static RGBA_Image *evas_rgba_line_buffer = NULL; */ @@ -78,11 +81,38 @@ static const Evas_Cache_Image_Func _evas_common_image_func = NULL }; +#ifdef EVAS_CSERVE2 +static const Evas_Cache2_Image_Func _evas_common_image_func2 = +{ + // _evas_common_rgba_image_new, + // _evas_common_rgba_image_delete, + _evas_common_rgba_image_surface_alloc, + _evas_common_rgba_image_surface_delete, + _evas_common_rgba_image_surface_pixels, + // evas_common_load_rgba_image_module_from_file, + // _evas_common_rgba_image_unload, + NULL, // _evas_common_rgba_image_dirty_region, + NULL, // _evas_common_rgba_image_dirty, + evas_common_rgba_image_size_set, + evas_common_rgba_image_from_copied_data, + evas_common_rgba_image_from_data, + NULL, // evas_common_rgba_image_colorspace_set, + // evas_common_load_rgba_image_data_from_file, + _evas_common_rgba_image_ram_usage, +/* _evas_common_rgba_image_debug */ + NULL +}; +#endif + EAPI void evas_common_image_init(void) { if (!eci) eci = evas_cache_image_init(&_evas_common_image_func); +#ifdef EVAS_CSERVE2 + if (!eci2) + eci2 = evas_cache2_init(&_evas_common_image_func2); +#endif reference++; //// ERR("REF++=%i", reference); @@ -115,6 +145,10 @@ evas_common_image_shutdown(void) // ENABLE IT AGAIN, hope it is fixed. Gustavo @ January 22nd, 2009. evas_cache_image_shutdown(eci); eci = NULL; +#ifdef EVAS_CSERVE2 + evas_cache2_shutdown(eci2); + eci2 = NULL; +#endif } #ifdef BUILD_LOADER_EET @@ -165,6 +199,12 @@ _evas_common_rgba_image_delete(Image_Entry *ie) #ifdef EVAS_CSERVE if (ie->data1) evas_cserve_image_free(ie); #endif +#ifdef EVAS_CSERVE2 + if (ie->data1) + ERR("Shouldn't reach this point since we are using cache2: '%s' '%s'", + ie->file, ie->key); + // if (ie->data1) evas_cserve2_image_free(ie); +#endif /* * FIXME: This doesn't seem to be needed... But I'm not sure why. * -- nash @@ -266,6 +306,22 @@ evas_common_rgba_image_unload(Image_Entry *ie) } #endif +#ifdef EVAS_CSERVE2 + if (ie->data1) + { + ERR("Shouldn't reach this point since we are using cache2."); +// evas_cserve2_image_unload(ie); +// im->image.data = NULL; +// ie->allocated.w = 0; +// ie->allocated.h = 0; +// ie->flags.loaded = 0; +#ifdef BUILD_ASYNC_PRELOAD + ie->flags.preload_done = 0; +#endif + return; + } +#endif + if (im->image.data && !im->image.no_free) { free(im->image.data); @@ -334,6 +390,9 @@ _evas_common_rgba_image_surface_alloc(Image_Entry *ie, unsigned int w, unsigned #ifdef EVAS_CSERVE if (ie->data1) return 0; #endif +#ifdef EVAS_CSERVE2 + if (ie->data1) return 0; +#endif if (im->image.no_free) return 0; if (im->flags & RGBA_IMAGE_ALPHA_ONLY) @@ -411,6 +470,11 @@ _evas_common_rgba_image_surface_delete(Image_Entry *ie) else if (ie->data1) evas_cserve_image_free(ie); #endif +// #ifdef EVAS_CSERVE2 +// else if (ie->data1) +// ERR("Shouldn't reach this point since we are using cache2."); +// // evas_cserve2_image_free(ie); +// #endif im->image.data = NULL; ie->allocated.w = 0; @@ -441,6 +505,10 @@ _evas_common_rgba_image_dirty_region(Image_Entry* ie, unsigned int x __UNUSED__, #ifdef EVAS_CSERVE if (ie->data1) evas_cserve_image_free(ie); #endif +#ifdef EVAS_CSERVE2 + // if (ie->data1) evas_cserve2_image_free(ie); + if (ie->data1) ERR("Shouldn't reach this point since we are using cache2."); +#endif im->flags |= RGBA_IMAGE_IS_DIRTY; evas_common_rgba_image_scalecache_dirty(&im->cache_entry); } @@ -461,12 +529,20 @@ _evas_common_rgba_image_dirty(Image_Entry *ie_dst, const Image_Entry *ie_src) #ifdef EVAS_CSERVE if (ie_src->data1) evas_cserve_image_free((Image_Entry*) ie_src); #endif +#ifdef EVAS_CSERVE2 + // if (ie_src->data1) evas_cserve2_image_free((Image_Entry*) ie_src); + if (ie_src->data1) ERR("Shouldn't reach this point since we are using cache2."); +#endif return 1; } #ifdef EVAS_CSERVE if (ie_src->data1) evas_cserve_image_free((Image_Entry*) ie_src); #endif +#ifdef EVAS_CSERVE2 + // if (ie_src->data1) evas_cserve2_image_free((Image_Entry*) ie_src); + if (ie_src->data1) ERR("Shouldn't reach this point since we are using cache2."); +#endif evas_common_image_colorspace_normalize(src); evas_common_image_colorspace_normalize(dst); /* evas_common_blit_rectangle(src, dst, 0, 0, src->cache_entry.w, src->cache_entry.h, 0, 0); */ @@ -487,7 +563,9 @@ _evas_common_rgba_image_ram_usage(Image_Entry *ie) if (im->image.data) { -#ifdef EVAS_CSERVE +#if defined(EVAS_CSERVE) + if ((!im->image.no_free) || (ie->data1)) +#elif defined(EVAS_CSERVE2) if ((!im->image.no_free) || (ie->data1)) #else if ((!im->image.no_free)) @@ -640,6 +718,10 @@ evas_common_image_colorspace_normalize(RGBA_Image *im) #ifdef EVAS_CSERVE if (((Image_Entry *)im)->data1) evas_cserve_image_free(&im->cache_entry); #endif +#ifdef EVAS_CSERVE2 + // if (((Image_Entry *)im)->data1) evas_cserve2_image_free(&im->cache_entry); + if (((Image_Entry *)im)->data1) ERR("Shouldn't reach this point since we are using cache2."); +#endif if (!im->image.no_free) { free(im->image.data); @@ -702,6 +784,10 @@ evas_common_image_set_cache(unsigned int size) { if (eci) evas_cache_image_set(eci, size); +#ifdef EVAS_CSERVE2 + if (eci2) + evas_cache2_limit_set(eci2, size); +#endif } EAPI int @@ -733,6 +819,14 @@ evas_common_image_cache_get(void) return eci; } +#ifdef EVAS_CSERVE2 +EAPI Evas_Cache2* +evas_common_image_cache2_get(void) +{ + return eci2; +} +#endif + EAPI RGBA_Image * evas_common_image_line_buffer_obtain(int len) { diff --git a/src/lib/engines/common/evas_image_private.h b/src/lib/engines/common/evas_image_private.h index 1bec6c8..f40866c 100644 --- a/src/lib/engines/common/evas_image_private.h +++ b/src/lib/engines/common/evas_image_private.h @@ -8,8 +8,6 @@ int evas_common_rgba_image_colorspace_set (Image_Entry* dst, void evas_common_scalecache_init(void); void evas_common_scalecache_shutdown(void); -void evas_common_rgba_image_scalecache_init(Image_Entry *ie); -void evas_common_rgba_image_scalecache_shutdown(Image_Entry *ie); void evas_common_rgba_image_scalecache_dirty(Image_Entry *ie); void evas_common_rgba_image_scalecache_orig_use(Image_Entry *ie); int evas_common_rgba_image_scalecache_usage_get(Image_Entry *ie); diff --git a/src/lib/engines/common/evas_image_scalecache.c b/src/lib/engines/common/evas_image_scalecache.c index 0a6b4e6..99ecd30 100644 --- a/src/lib/engines/common/evas_image_scalecache.c +++ b/src/lib/engines/common/evas_image_scalecache.c @@ -8,6 +8,9 @@ #include +#ifdef EVAS_CSERVE2 +#include "evas_cs2_private.h" +#endif #include "evas_common.h" #include "evas_private.h" #include "evas_image_private.h" @@ -523,7 +526,14 @@ evas_common_rgba_image_scalecache_do(Image_Entry *ie, RGBA_Image *dst, if ((src_region_w == dst_region_w) && (src_region_h == dst_region_h)) { if (im->cache_entry.space == EVAS_COLORSPACE_ARGB8888) - evas_cache_image_load_data(&im->cache_entry); + { +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + evas_cache2_image_load_data(&im->cache_entry); + else +#endif + evas_cache_image_load_data(&im->cache_entry); + } evas_common_image_colorspace_normalize(im); // noscales++; @@ -546,7 +556,14 @@ evas_common_rgba_image_scalecache_do(Image_Entry *ie, RGBA_Image *dst, if (!sci) { if (im->cache_entry.space == EVAS_COLORSPACE_ARGB8888) - evas_cache_image_load_data(&im->cache_entry); + { +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + evas_cache2_image_load_data(&im->cache_entry); + else +#endif + evas_cache_image_load_data(&im->cache_entry); + } evas_common_image_colorspace_normalize(im); // misses++; @@ -630,7 +647,14 @@ evas_common_rgba_image_scalecache_do(Image_Entry *ie, RGBA_Image *dst, evas_common_draw_context_set_render_op(ct, _EVAS_RENDER_COPY); } if (im->cache_entry.space == EVAS_COLORSPACE_ARGB8888) - evas_cache_image_load_data(&im->cache_entry); + { +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + evas_cache2_image_load_data(&im->cache_entry); + else +#endif + evas_cache_image_load_data(&im->cache_entry); + } evas_common_image_colorspace_normalize(im); if (im->image.data) { @@ -733,6 +757,9 @@ evas_common_rgba_image_scalecache_do(Image_Entry *ie, RGBA_Image *dst, #ifdef EVAS_CSERVE || (ie->data1) #endif +#ifdef EVAS_CSERVE2 + || (ie->data1) +#endif ) && (im->cache_entry.space == EVAS_COLORSPACE_ARGB8888))) { @@ -740,7 +767,12 @@ evas_common_rgba_image_scalecache_do(Image_Entry *ie, RGBA_Image *dst, (im->cache.newest_usage / 20))) { //FIXME: imagedataunload - inform owners - evas_common_rgba_image_unload(&im->cache_entry); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + evas_cache2_image_unload_data(&im->cache_entry); + else +#endif + evas_common_rgba_image_unload(&im->cache_entry); } } } @@ -748,7 +780,14 @@ evas_common_rgba_image_scalecache_do(Image_Entry *ie, RGBA_Image *dst, else { if (im->cache_entry.space == EVAS_COLORSPACE_ARGB8888) - evas_cache_image_load_data(&im->cache_entry); + { +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + evas_cache2_image_load_data(&im->cache_entry); + else +#endif + evas_cache_image_load_data(&im->cache_entry); + } evas_common_image_colorspace_normalize(im); // misses++; LKU(im->cache.lock); @@ -771,7 +810,14 @@ evas_common_rgba_image_scalecache_do(Image_Entry *ie, RGBA_Image *dst, #else RGBA_Image *im = (RGBA_Image *)ie; if (im->cache_entry.space == EVAS_COLORSPACE_ARGB8888) - evas_cache_image_load_data(&im->cache_entry); + { +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + evas_cache2_image_load_data(&im->cache_entry); + else +#endif + evas_cache_image_load_data(&im->cache_entry); + } evas_common_image_colorspace_normalize(im); if (im->image.data) { diff --git a/src/lib/include/evas_common.h b/src/lib/include/evas_common.h index 89527b9..7989ba9 100644 --- a/src/lib/include/evas_common.h +++ b/src/lib/include/evas_common.h @@ -435,6 +435,9 @@ typedef void (*Gfx_Func_Copy) (DATA32 *src, DATA32 *dst, int len); typedef void (*Gfx_Func_Convert) (DATA32 *src, DATA8 *dst, int src_jump, int dst_jump, int w, int h, int dith_x, int dith_y, DATA8 *pal); #include "../cache/evas_cache.h" +#ifdef EVAS_CSERVE2 +#include "../cache2/evas_cache2.h" +#endif /*****************************************************************************/ @@ -569,6 +572,9 @@ struct _Image_Entry EINA_INLIST; Evas_Cache_Image *cache; +#ifdef EVAS_CSERVE2 + Evas_Cache2 *cache2; +#endif const char *cache_key; @@ -614,6 +620,9 @@ struct _Image_Entry Image_Entry_Flags flags; Evas_Image_Scale_Hint scale_hint; void *data1, *data2; +#ifdef EVAS_CSERVE2 + unsigned int open_rid, load_rid, preload_rid; +#endif int server_id; int connect_num; int channel; diff --git a/src/modules/engines/buffer/Makefile.am b/src/modules/engines/buffer/Makefile.am index f8cd364..27f4912 100644 --- a/src/modules/engines/buffer/Makefile.am +++ b/src/modules/engines/buffer/Makefile.am @@ -5,6 +5,7 @@ AM_CPPFLAGS = \ -I. \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib/include \ +-I$(top_srcdir)/src/lib/cserve2 \ -I$(top_srcdir)/src/modules/engines \ @EINA_CFLAGS@ \ @FREETYPE_CFLAGS@ \ diff --git a/src/modules/engines/buffer/evas_outbuf.c b/src/modules/engines/buffer/evas_outbuf.c index 0fa1842..7b79ebb 100644 --- a/src/modules/engines/buffer/evas_outbuf.c +++ b/src/modules/engines/buffer/evas_outbuf.c @@ -1,3 +1,11 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef EVAS_CSERVE2 +#include "evas_cs2_private.h" +#endif + #include "evas_common.h" #include "evas_engine.h" @@ -11,6 +19,11 @@ evas_buffer_outbuf_buf_free(Outbuf *buf) { if (buf->priv.back_buf) { +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + evas_cache2_image_close(&buf->priv.back_buf->cache_entry); + else +#endif evas_cache_image_drop(&buf->priv.back_buf->cache_entry); } free(buf); @@ -50,6 +63,14 @@ evas_buffer_outbuf_buf_setup_fb(int w, int h, Outbuf_Depth depth, void *dest, in (buf->dest) && (buf->dest_row_bytes == (buf->w * sizeof(DATA32)))) { memset(buf->dest, 0, h * buf->dest_row_bytes); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + buf->priv.back_buf = (RGBA_Image *) evas_cache2_image_data(evas_common_image_cache2_get(), + w, h, + buf->dest, + 1, EVAS_COLORSPACE_ARGB8888); + else +#endif buf->priv.back_buf = (RGBA_Image *) evas_cache_image_data(evas_common_image_cache_get(), w, h, buf->dest, @@ -58,6 +79,14 @@ evas_buffer_outbuf_buf_setup_fb(int w, int h, Outbuf_Depth depth, void *dest, in else if ((buf->depth == OUTBUF_DEPTH_RGB_32BPP_888_8888) && (buf->dest) && (buf->dest_row_bytes == (buf->w * sizeof(DATA32)))) { +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + buf->priv.back_buf = (RGBA_Image *) evas_cache2_image_data(evas_common_image_cache2_get(), + w, h, + buf->dest, + 0, EVAS_COLORSPACE_ARGB8888); + else +#endif buf->priv.back_buf = (RGBA_Image *) evas_cache_image_data(evas_common_image_cache_get(), w, h, buf->dest, @@ -80,6 +109,11 @@ evas_buffer_outbuf_buf_new_region_for_update(Outbuf *buf, int x, int y, int w, i else { *cx = 0; *cy = 0; *cw = w; *ch = h; +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + im = (RGBA_Image *)evas_cache2_image_empty(evas_common_image_cache2_get()); + else +#endif im = (RGBA_Image *) evas_cache_image_empty(evas_common_image_cache_get()); if (im) { @@ -87,6 +121,11 @@ evas_buffer_outbuf_buf_new_region_for_update(Outbuf *buf, int x, int y, int w, i ((buf->depth == OUTBUF_DEPTH_BGRA_32BPP_8888_8888))) { im->cache_entry.flags.alpha = 1; +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + evas_cache2_image_size_set(&im->cache_entry, w, h); + else +#endif im = (RGBA_Image *) evas_cache_image_size_set(&im->cache_entry, w, h); } } @@ -97,7 +136,15 @@ evas_buffer_outbuf_buf_new_region_for_update(Outbuf *buf, int x, int y, int w, i void evas_buffer_outbuf_buf_free_region_for_update(Outbuf *buf, RGBA_Image *update) { - if (update != buf->priv.back_buf) evas_cache_image_drop(&update->cache_entry); + if (update != buf->priv.back_buf) + { +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + evas_cache2_image_close(&update->cache_entry); + else +#endif + evas_cache_image_drop(&update->cache_entry); + } } void @@ -108,12 +155,26 @@ evas_buffer_outbuf_buf_switch_buffer(Outbuf *buf) buf->dest = buf->func.switch_buffer(buf->switch_data, buf->dest); if (buf->priv.back_buf) { - evas_cache_image_drop(&buf->priv.back_buf->cache_entry); - buf->priv.back_buf = (RGBA_Image *) evas_cache_image_data(evas_common_image_cache_get(), - buf->w, buf->h, - buf->dest, - buf->depth == OUTBUF_DEPTH_ARGB_32BPP_8888_8888 ? 1 : 0, - EVAS_COLORSPACE_ARGB8888); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + evas_cache2_image_close(&buf->priv.back_buf->cache_entry); + buf->priv.back_buf = (RGBA_Image *) evas_cache2_image_data(evas_common_image_cache_get(), + buf->w, buf->h, + buf->dest, + buf->depth == OUTBUF_DEPTH_ARGB_32BPP_8888_8888 ? 1 : 0, + EVAS_COLORSPACE_ARGB8888); + } + else +#endif + { + evas_cache_image_drop(&buf->priv.back_buf->cache_entry); + buf->priv.back_buf = (RGBA_Image *) evas_cache_image_data(evas_common_image_cache_get(), + buf->w, buf->h, + buf->dest, + buf->depth == OUTBUF_DEPTH_ARGB_32BPP_8888_8888 ? 1 : 0, + EVAS_COLORSPACE_ARGB8888); + } } } } diff --git a/src/modules/engines/software_generic/Makefile.am b/src/modules/engines/software_generic/Makefile.am index fa522f8..f31810e 100644 --- a/src/modules/engines/software_generic/Makefile.am +++ b/src/modules/engines/software_generic/Makefile.am @@ -5,6 +5,7 @@ AM_CPPFLAGS = \ -I. \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib/include \ +-I$(top_srcdir)/src/lib/cserve2 \ -I$(top_srcdir)/src/modules/engines \ @EINA_CFLAGS@ \ @FREETYPE_CFLAGS@ \ diff --git a/src/modules/engines/software_generic/evas_engine.c b/src/modules/engines/software_generic/evas_engine.c index cbc709d..96ed50c 100644 --- a/src/modules/engines/software_generic/evas_engine.c +++ b/src/modules/engines/software_generic/evas_engine.c @@ -1,5 +1,8 @@ #include "evas_common.h" /* Also includes international specific stuff */ #include "evas_private.h" +#ifdef EVAS_CSERVE2 +#include "evas_cs2_private.h" +#endif #ifdef HAVE_DLSYM # include /* dlopen,dlclose,etc */ @@ -655,24 +658,58 @@ static void * eng_image_load(void *data __UNUSED__, const char *file, const char *key, int *error, Evas_Image_Load_Opts *lo) { *error = EVAS_LOAD_ERROR_NONE; +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + Image_Entry *ie; + ie = evas_cache2_image_open(evas_common_image_cache2_get(), + file, key, lo, error); + if (ie) + evas_cache2_image_open_wait(ie); + + return ie; + } +#endif return evas_common_load_image_from_file(file, key, lo, error); } static void * eng_image_new_from_data(void *data __UNUSED__, int w, int h, DATA32 *image_data, int alpha, int cspace) { +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + Evas_Cache2 *cache = evas_common_image_cache2_get(); + return evas_cache2_image_data(cache, w, h, image_data, alpha, cspace); + } +#endif return evas_cache_image_data(evas_common_image_cache_get(), w, h, image_data, alpha, cspace); } static void * eng_image_new_from_copied_data(void *data __UNUSED__, int w, int h, DATA32 *image_data, int alpha, int cspace) { +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + Evas_Cache2 *cache = evas_common_image_cache2_get(); + return evas_cache2_image_copied_data(cache, w, h, image_data, alpha, + cspace); + } +#endif return evas_cache_image_copied_data(evas_common_image_cache_get(), w, h, image_data, alpha, cspace); } static void eng_image_free(void *data __UNUSED__, void *image) { +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + evas_cache2_image_close(image); + return; + } +#endif evas_cache_image_drop(image); } @@ -691,6 +728,10 @@ eng_image_size_set(void *data __UNUSED__, void *image, int w, int h) { Image_Entry *im = image; if (!im) return NULL; +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + return evas_cache2_image_size_set(im, w, h); +#endif return evas_cache_image_size_set(im, w, h); } @@ -699,6 +740,10 @@ eng_image_dirty_region(void *data __UNUSED__, void *image, int x, int y, int w, { Image_Entry *im = image; if (!im) return NULL; +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + return evas_cache2_image_dirty(im, x, y, w, h); +#endif return evas_cache_image_dirty(im, x, y, w, h); } @@ -706,7 +751,7 @@ static void * eng_image_data_get(void *data __UNUSED__, void *image, int to_write, DATA32 **image_data, int *err) { RGBA_Image *im; - int error; + int error = EVAS_LOAD_ERROR_NONE; if (!image) { @@ -714,6 +759,21 @@ eng_image_data_get(void *data __UNUSED__, void *image, int to_write, DATA32 **im return NULL; } im = image; + +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + error = evas_cache2_image_load_data(&im->cache_entry); + if (err) *err = error; + + if (to_write) + im = evas_cache2_image_writable(&im->cache_entry); + + *image_data = im->image.data; + return im; + } +#endif + error = evas_cache_image_load_data(&im->cache_entry); switch (im->cache_entry.space) { @@ -756,6 +816,14 @@ eng_image_data_put(void *data, void *image, DATA32 *image_data) im2 = eng_image_new_from_data(data, w, h, image_data, eng_image_alpha_get(data, image), eng_image_colorspace_get(data, image)); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + evas_cache2_image_close(&im->cache_entry); + im = im2; + break; + } +#endif evas_cache_image_drop(&im->cache_entry); im = im2; } @@ -786,8 +854,15 @@ static void eng_image_data_preload_request(void *data __UNUSED__, void *image, const void *target) { RGBA_Image *im = image; - if (!im) return ; + +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + evas_cache2_image_preload_data(&im->cache_entry, target); + return; + } +#endif evas_cache_image_preload_data(&im->cache_entry, target); } @@ -795,6 +870,10 @@ static void eng_image_data_preload_cancel(void *data __UNUSED__, void *image, const void *target) { RGBA_Image *im = image; +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + return; +#endif if (!im) return ; evas_cache_image_preload_cancel(&im->cache_entry, target); @@ -810,6 +889,10 @@ eng_image_draw(void *data __UNUSED__, void *context, void *surface, void *image, #ifdef BUILD_PIPE_RENDER if ((cpunum > 1)) { +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + evas_cache2_image_load_data(&im->cache_entry); +#endif evas_common_rgba_image_scalecache_prepare((Image_Entry *)(im), surface, context, smooth, src_x, src_y, src_w, src_h, @@ -822,9 +905,18 @@ eng_image_draw(void *data __UNUSED__, void *context, void *surface, void *image, else #endif { -// if (im->cache_entry.space == EVAS_COLORSPACE_ARGB8888) -// evas_cache_image_load_data(&im->cache_entry); -// evas_common_image_colorspace_normalize(im); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + evas_cache2_image_load_data(&im->cache_entry); + goto image_loaded; + } +#endif + if (im->cache_entry.space == EVAS_COLORSPACE_ARGB8888) + evas_cache_image_load_data(&im->cache_entry); + evas_common_image_colorspace_normalize(im); + +image_loaded: evas_common_rgba_image_scalecache_prepare(&im->cache_entry, surface, context, smooth, src_x, src_y, src_w, src_h, dst_x, dst_y, dst_w, dst_h); @@ -916,6 +1008,13 @@ eng_image_map_surface_new(void *data __UNUSED__, int w, int h, int alpha) static void eng_image_map_surface_free(void *data __UNUSED__, void *surface) { +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + evas_cache2_image_unload_data(surface); + return; + } +#endif evas_cache_image_drop(surface); } diff --git a/src/modules/engines/software_x11/Makefile.am b/src/modules/engines/software_x11/Makefile.am index 441a00a..1d5d17b 100644 --- a/src/modules/engines/software_x11/Makefile.am +++ b/src/modules/engines/software_x11/Makefile.am @@ -11,6 +11,7 @@ AM_CPPFLAGS = \ -I. \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib/include \ +-I$(top_srcdir)/src/lib/cserve2 \ -I$(top_srcdir)/src/modules/engines \ @FREETYPE_CFLAGS@ \ @PIXMAN_CFLAGS@ \ diff --git a/src/modules/engines/software_x11/evas_xlib_outbuf.c b/src/modules/engines/software_x11/evas_xlib_outbuf.c index 9662bb3..c788147 100644 --- a/src/modules/engines/software_x11/evas_xlib_outbuf.c +++ b/src/modules/engines/software_x11/evas_xlib_outbuf.c @@ -5,6 +5,9 @@ #include #include +#ifdef EVAS_CSERVE2 +#include "evas_cs2_private.h" +#endif #include "evas_common.h" #include "evas_macros.h" #include "evas_xlib_outbuf.h" @@ -157,7 +160,14 @@ evas_software_xlib_outbuf_free(Outbuf *buf) im = buf->priv.pending_writes->data; buf->priv.pending_writes = eina_list_remove_list(buf->priv.pending_writes, buf->priv.pending_writes); obr = im->extended_info; - evas_cache_image_drop(&im->cache_entry); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + evas_cache2_image_close(&im->cache_entry); + } + else +#endif + evas_cache_image_drop(&im->cache_entry); if (obr->xob) _unfind_xob(obr->xob, 0); if (obr->mxob) _unfind_xob(obr->mxob, 0); free(obr); @@ -411,10 +421,20 @@ evas_software_xlib_outbuf_new_region_for_update(Outbuf *buf, int x, int y, int w free(obr); return NULL; } - im = (RGBA_Image *)evas_cache_image_data(evas_common_image_cache_get(), - buf->w, buf->h, - (DATA32 *) evas_software_xlib_x_output_buffer_data(obr->xob, &bpl), - alpha, EVAS_COLORSPACE_ARGB8888); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + im = (RGBA_Image *)evas_cache2_image_data(evas_common_image_cache2_get(), + buf->w, buf->h, + (DATA32 *) evas_software_xlib_x_output_buffer_data(obr->xob, &bpl), + alpha, EVAS_COLORSPACE_ARGB8888); + } + else +#endif + im = (RGBA_Image *)evas_cache_image_data(evas_common_image_cache_get(), + buf->w, buf->h, + (DATA32 *) evas_software_xlib_x_output_buffer_data(obr->xob, &bpl), + alpha, EVAS_COLORSPACE_ARGB8888); if (!im) { evas_software_xlib_x_output_buffer_free(obr->xob, 0); @@ -432,14 +452,24 @@ evas_software_xlib_outbuf_new_region_for_update(Outbuf *buf, int x, int y, int w } else { - im = (RGBA_Image *)evas_cache_image_empty(evas_common_image_cache_get()); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + im = (RGBA_Image *)evas_cache2_image_empty(evas_common_image_cache2_get()); + else +#endif + im = (RGBA_Image *)evas_cache_image_empty(evas_common_image_cache_get()); if (!im) { free(obr); return NULL; } im->cache_entry.flags.alpha |= alpha ? 1 : 0; - evas_cache_image_surface_alloc(&im->cache_entry, buf->w, buf->h); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + evas_cache2_image_surface_alloc(&im->cache_entry, buf->w, buf->h); + else +#endif + evas_cache_image_surface_alloc(&im->cache_entry, buf->w, buf->h); im->extended_info = obr; if ((buf->rot == 0) || (buf->rot == 180)) { @@ -451,7 +481,14 @@ evas_software_xlib_outbuf_new_region_for_update(Outbuf *buf, int x, int y, int w NULL); if (!obr->xob) { - evas_cache_image_drop(&im->cache_entry); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + evas_cache2_image_close(&im->cache_entry); + } + else +#endif + evas_cache_image_drop(&im->cache_entry); free(obr); return NULL; } @@ -472,7 +509,14 @@ evas_software_xlib_outbuf_new_region_for_update(Outbuf *buf, int x, int y, int w NULL); if (!obr->xob) { - evas_cache_image_drop(&im->cache_entry); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + evas_cache2_image_close(&im->cache_entry); + } + else +#endif + evas_cache_image_drop(&im->cache_entry); free(obr); return NULL; } @@ -532,10 +576,18 @@ evas_software_xlib_outbuf_new_region_for_update(Outbuf *buf, int x, int y, int w free(obr); return NULL; } - im = (RGBA_Image *)evas_cache_image_data(evas_common_image_cache_get(), - w, h, - (DATA32 *) evas_software_xlib_x_output_buffer_data(obr->xob, &bpl), - alpha, EVAS_COLORSPACE_ARGB8888); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + im = (RGBA_Image *)evas_cache2_image_data(evas_common_image_cache2_get(), + w, h, + (DATA32 *) evas_software_xlib_x_output_buffer_data(obr->xob, &bpl), + alpha, EVAS_COLORSPACE_ARGB8888); + else +#endif + im = (RGBA_Image *)evas_cache_image_data(evas_common_image_cache_get(), + w, h, + (DATA32 *) evas_software_xlib_x_output_buffer_data(obr->xob, &bpl), + alpha, EVAS_COLORSPACE_ARGB8888); if (!im) { _unfind_xob(obr->xob, 0); @@ -552,7 +604,12 @@ evas_software_xlib_outbuf_new_region_for_update(Outbuf *buf, int x, int y, int w } else { - im = (RGBA_Image *)evas_cache_image_empty(evas_common_image_cache_get()); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + im = (RGBA_Image *)evas_cache2_image_empty(evas_common_image_cache2_get()); + else +#endif + im = (RGBA_Image *)evas_cache_image_empty(evas_common_image_cache_get()); if (!im) { free(obr); @@ -561,7 +618,12 @@ evas_software_xlib_outbuf_new_region_for_update(Outbuf *buf, int x, int y, int w im->cache_entry.w = w; im->cache_entry.h = h; im->cache_entry.flags.alpha |= alpha ? 1 : 0; - evas_cache_image_surface_alloc(&im->cache_entry, w, h); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + evas_cache2_image_surface_alloc(&im->cache_entry, w, h); + else +#endif + evas_cache_image_surface_alloc(&im->cache_entry, w, h); im->extended_info = obr; if ((buf->rot == 0) || (buf->rot == 180)) { @@ -573,7 +635,14 @@ evas_software_xlib_outbuf_new_region_for_update(Outbuf *buf, int x, int y, int w NULL); if (!obr->xob) { - evas_cache_image_drop(&im->cache_entry); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + evas_cache2_image_close(&im->cache_entry); + } + else +#endif + evas_cache_image_drop(&im->cache_entry); free(obr); return NULL; } @@ -594,7 +663,14 @@ evas_software_xlib_outbuf_new_region_for_update(Outbuf *buf, int x, int y, int w NULL); if (!obr->xob) { - evas_cache_image_drop(&im->cache_entry); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + evas_cache2_image_close(&im->cache_entry); + } + else +#endif + evas_cache_image_drop(&im->cache_entry); free(obr); return NULL; } @@ -723,7 +799,14 @@ evas_software_xlib_outbuf_flush(Outbuf *buf) eina_list_remove_list(buf->priv.prev_pending_writes, buf->priv.prev_pending_writes); obr = im->extended_info; - evas_cache_image_drop(&im->cache_entry); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + evas_cache2_image_close(&im->cache_entry); + } + else +#endif + evas_cache_image_drop(&im->cache_entry); if (obr->xob) _unfind_xob(obr->xob, 0); if (obr->mxob) _unfind_xob(obr->mxob, 0); free(obr); @@ -760,11 +843,25 @@ evas_software_xlib_outbuf_flush(Outbuf *buf) im = eina_list_data_get(buf->priv.pending_writes); buf->priv.pending_writes = eina_list_remove_list(buf->priv.pending_writes, buf->priv.pending_writes); obr = im->extended_info; - evas_cache_image_drop(&im->cache_entry); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + evas_cache2_image_close(&im->cache_entry); + } + else +#endif + evas_cache_image_drop(&im->cache_entry); if (obr->xob) _unfind_xob(obr->xob, 0); if (obr->mxob) _unfind_xob(obr->mxob, 0); free(obr); - evas_cache_image_drop(&im->cache_entry); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + evas_cache2_image_close(&im->cache_entry); + } + else +#endif + evas_cache_image_drop(&im->cache_entry); } #endif } @@ -785,7 +882,14 @@ evas_software_xlib_outbuf_idle_flush(Outbuf *buf) if (obr->xob) evas_software_xlib_x_output_buffer_free(obr->xob, 0); if (obr->mxob) evas_software_xlib_x_output_buffer_free(obr->mxob, 0); free(obr); - evas_cache_image_drop(&im->cache_entry); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + evas_cache2_image_close(&im->cache_entry); + } + else +#endif + evas_cache_image_drop(&im->cache_entry); } else { @@ -800,7 +904,14 @@ evas_software_xlib_outbuf_idle_flush(Outbuf *buf) eina_list_remove_list(buf->priv.prev_pending_writes, buf->priv.prev_pending_writes); obr = im->extended_info; - evas_cache_image_drop(&im->cache_entry); +#ifdef EVAS_CSERVE2 + if (evas_cserve2_use_get()) + { + evas_cache2_image_close(&im->cache_entry); + } + else +#endif + evas_cache_image_drop(&im->cache_entry); if (obr->xob) _unfind_xob(obr->xob, 0); if (obr->mxob) _unfind_xob(obr->mxob, 0); free(obr); -- 2.7.4