From 3b8b2ac66c10be49a85ec2dad2d54082fe47742f Mon Sep 17 00:00:00 2001 From: Vincent Torri Date: Sat, 28 Sep 2013 14:28:41 +0200 Subject: [PATCH] evas: add JPEG 2000 loader. This add finally support for JPEG 2000, but be aware that libopenjpeg is very badly managed. There is currently only version 1.5.x that does provide the right files, is usable by a third party and portable. You can seriously forget any other version. --- ChangeLog | 4 + NEWS | 1 + configure.ac | 2 + m4/evas_check_loader.m4 | 62 +++ src/Makefile_Evas.am | 27 ++ src/lib/evas/common/evas_image_load.c | 4 + src/lib/evas/file/evas_module.c | 4 + .../evas/loaders/jp2k/evas_image_load_jp2k.c | 409 ++++++++++++++++++ 8 files changed, 513 insertions(+) create mode 100644 src/modules/evas/loaders/jp2k/evas_image_load_jp2k.c diff --git a/ChangeLog b/ChangeLog index 72873d0892..1206dcc659 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2013-10-01 Vincent Torri + + * Evas: add JPEG 2000 support. + 2013-09-25 Tom Hacohen * Evas font: Make the evas_font_path_* functions apply to fontconfig searches. diff --git a/NEWS b/NEWS index e40c53f056..b486810930 100644 --- a/NEWS +++ b/NEWS @@ -68,6 +68,7 @@ Additions: - Add interceptor for focus_set. - Evas font: Use our own fontconfig configuration so we don't get affected by changes made to the default fontconfig configuration. - Evas font: Make the evas_font_path_* functions apply to fontconfig searches. + - Add JPEG 2000 loader. * Ecore_X: - Add window profile support. ECORE_X_ATOM_E_WINDOW_PROFILE_SUPPORTED diff --git a/configure.ac b/configure.ac index 056e4ec5c7..dd327bb2ba 100644 --- a/configure.ac +++ b/configure.ac @@ -1241,6 +1241,7 @@ ARG_ENABLE_EVAS_IMAGE_LOADER(Generic, static) ARG_ENABLE_EVAS_IMAGE_LOADER(Gif, yes) ARG_ENABLE_EVAS_IMAGE_LOADER(ICO, static) ARG_ENABLE_EVAS_IMAGE_LOADER(JPEG, static) +ARG_ENABLE_EVAS_IMAGE_LOADER(JP2K, auto) ARG_ENABLE_EVAS_IMAGE_LOADER(PMAPS, static) ARG_ENABLE_EVAS_IMAGE_LOADER(PNG, static) ARG_ENABLE_EVAS_IMAGE_LOADER(PSD, static) @@ -1522,6 +1523,7 @@ EVAS_CHECK_IMAGE_LOADER([Generic], [${want_evas_image_loader_generic}]) EVAS_CHECK_IMAGE_LOADER([Gif], [${want_evas_image_loader_gif}]) EVAS_CHECK_IMAGE_LOADER([ICO], [${want_evas_image_loader_ico}]) EVAS_CHECK_IMAGE_LOADER([JPEG], [${want_evas_image_loader_jpeg}]) +EVAS_CHECK_IMAGE_LOADER([JP2K], [${want_evas_image_loader_jp2k}]) EVAS_CHECK_IMAGE_LOADER([PMAPS], [${want_evas_image_loader_pmaps}]) EVAS_CHECK_IMAGE_LOADER([PNG], [${want_evas_image_loader_png}]) EVAS_CHECK_IMAGE_LOADER([PSD], [${want_evas_image_loader_psd}]) diff --git a/m4/evas_check_loader.m4 b/m4/evas_check_loader.m4 index e10e33040d..b34c28fc05 100644 --- a/m4/evas_check_loader.m4 +++ b/m4/evas_check_loader.m4 @@ -184,6 +184,68 @@ AS_IF([test "x${have_dep}" = "xyes"], [$3], [$4]) ]) +dnl use: EVAS_CHECK_LOADER_DEP_JP2K(loader, want_static[, ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) + +AC_DEFUN([EVAS_CHECK_LOADER_DEP_JP2K], +[ + +have_dep="no" +have_dep_pc="no" +evas_image_loader_[]$1[]_cflags="" +evas_image_loader_[]$1[]_libs="" + +AC_CHECK_HEADER([openjpeg.h], [have_dep="yes"]) + +if test "x${have_dep}" = "xyes" ; then + AC_CHECK_LIB([openjp2], + [opj_set_default_decoder_parameters], + [ + evas_image_loader_[]$1[]_libs="-lopenjp2" + have_dep="yes" + ], + [have_dep="no"]) +fi + +if test "x${have_dep}" = "xno" ; then + PKG_CHECK_EXISTS([libopenjpeg1 >= 1.5], + [ + have_dep="yes" + have_dep_pc="yes" + requirement="libopenjpeg1 >= 1.5" + ], + [have_dep="no"]) +fi + +if test "x${have_dep}" = "xno" ; then + PKG_CHECK_EXISTS([libopenjpeg >= 1.5], + [ + have_dep="yes" + have_dep_pc="yes" + requirement="libopenjpeg >= 1.5" + ], + [have_dep="no"]) +fi + +if test "x${have_dep}" = "xyes" ; then + if test "x$2" = "xstatic" ; then + requirements_pc_evas="${requirement} ${requirements_pc_evas}" + requirements_pc_deps_evas="${requirement} ${requirements_pc_deps_evas}" + requirements_libs_evas="${evas_image_loader_[]$1[]_libs} ${requirements_libs_evas}" + fi + if test "x${have_dep_pc}" = "xyes" ; then + PKG_CHECK_MODULES([JP2K], [${requirement}]) + evas_image_loader_[]$1[]_cflags="${JP2K_CFLAGS}" + evas_image_loader_[]$1[]_libs="${JP2K_LIBS}" + fi +fi + +AC_SUBST([evas_image_loader_$1_cflags]) +AC_SUBST([evas_image_loader_$1_libs]) + +AS_IF([test "x${have_dep}" = "xyes"], [$3], [$4]) + +]) + dnl use: EVAS_CHECK_LOADER_DEP_PMAPS(loader, want_static[[, ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) AC_DEFUN([EVAS_CHECK_LOADER_DEP_PMAPS], diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am index 924e0e159b..29bea4b603 100644 --- a/src/Makefile_Evas.am +++ b/src/Makefile_Evas.am @@ -1275,6 +1275,33 @@ endif endif endif +if BUILD_LOADER_JP2K +if EVAS_STATIC_BUILD_JP2K +lib_evas_libevas_la_SOURCES += modules/evas/loaders/jp2k/evas_image_load_jp2k.c +lib_evas_libevas_la_CPPFLAGS += @evas_image_loader_jp2k_cflags@ +lib_evas_libevas_la_LIBADD += @evas_image_loader_jp2k_libs@ +if EVAS_CSERVE2 +bin_evas_evas_cserve2_slave_SOURCES += modules/evas/loaders/jp2k/evas_image_load_jp2k.c +bin_evas_evas_cserve2_slave_CPPFLAGS += @evas_image_loader_jp2k_cflags@ +bin_evas_evas_cserve2_slave_LDADD += @evas_image_loader_jp2k_libs@ +endif +else +loaderjp2kpkgdir = $(libdir)/evas/modules/loaders/jp2k/$(MODULE_ARCH) +loaderjp2kpkg_LTLIBRARIES = modules/evas/loaders/jp2k/module.la +modules_evas_loaders_jp2k_module_la_SOURCES = modules/evas/loaders/jp2k/evas_image_load_jp2k.c +modules_evas_loaders_jp2k_module_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ +-I$(top_srcdir)/src/lib/evas/include \ +@EVAS_CFLAGS@ \ +@evas_image_loader_jp2k_cflags@ +modules_evas_loaders_jp2k_module_la_LIBADD = \ +@USE_EVAS_LIBS@ \ +@evas_image_loader_jp2k_libs@ +modules_evas_loaders_jp2k_module_la_DEPENDENCIES = @USE_EVAS_INTERNAL_LIBS@ +modules_evas_loaders_jp2k_module_la_LDFLAGS = -module @EFL_LTMODULE_FLAGS@ +modules_evas_loaders_jp2k_module_la_LIBTOOLFLAGS = --tag=disable-static +endif +endif + if BUILD_LOADER_PMAPS if EVAS_STATIC_BUILD_PMAPS lib_evas_libevas_la_SOURCES += modules/evas/loaders/pmaps/evas_image_load_pmaps.c diff --git a/src/lib/evas/common/evas_image_load.c b/src/lib/evas/common/evas_image_load.c index afca101deb..c945442be5 100644 --- a/src/lib/evas/common/evas_image_load.c +++ b/src/lib/evas/common/evas_image_load.c @@ -29,6 +29,10 @@ static const struct ext_loader_s loaders[] = MATCHING(".jpg", "jpeg"), MATCHING(".jpeg", "jpeg"), MATCHING(".jfif", "jpeg"), + MATCHING(".j2k", "jp2k"), + MATCHING(".jp2", "jp2k"), + MATCHING(".jpx", "jp2k"), + MATCHING(".jpf", "jp2k"), MATCHING(".eet", "eet"), MATCHING(".edj", "eet"), MATCHING(".eap", "eet"), diff --git a/src/lib/evas/file/evas_module.c b/src/lib/evas/file/evas_module.c index 9f70e60369..b3d0a87c92 100644 --- a/src/lib/evas/file/evas_module.c +++ b/src/lib/evas/file/evas_module.c @@ -136,6 +136,7 @@ EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, generic); EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, gif); EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, ico); EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, jpeg); +EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, jp2k); EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, pmaps); EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, png); EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, psd); @@ -207,6 +208,9 @@ static const struct { #ifdef EVAS_STATIC_BUILD_JPEG EVAS_EINA_STATIC_MODULE_USE(image_loader, jpeg), #endif +#ifdef EVAS_STATIC_BUILD_JP2K + EVAS_EINA_STATIC_MODULE_USE(image_loader, jp2k), +#endif #ifdef EVAS_STATIC_BUILD_PMAPS EVAS_EINA_STATIC_MODULE_USE(image_loader, pmaps), #endif diff --git a/src/modules/evas/loaders/jp2k/evas_image_load_jp2k.c b/src/modules/evas/loaders/jp2k/evas_image_load_jp2k.c new file mode 100644 index 0000000000..28200cee0a --- /dev/null +++ b/src/modules/evas/loaders/jp2k/evas_image_load_jp2k.c @@ -0,0 +1,409 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#ifdef HAVE_EVIL +# include +#endif + +#include + +#include "Evas_Loader.h" + +static int _evas_loader_jp2k_log_dom = -1; + +#ifdef ERR +# undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_evas_loader_jp2k_log_dom, __VA_ARGS__) + +#ifdef WRN +# undef WRN +#endif +#define WRN(...) EINA_LOG_DOM_WARN(_evas_loader_jp2k_log_dom, __VA_ARGS__) + +#ifdef INF +# undef INF +#endif +#define INF(...) EINA_LOG_DOM_INFO(_evas_loader_jp2k_log_dom, __VA_ARGS__) + +typedef struct _Evas_Loader_Internal Evas_Loader_Internal; +struct _Evas_Loader_Internal +{ + Eina_File *f; + Evas_Image_Load_Opts *opts; +}; + +static void +_jp2k_error_cb(const char *msg, void *data EINA_UNUSED) +{ + ERR("OpenJPEG internal error: '%s'.", msg); +} + +static void +_jp2k_warning_cb(const char *msg, void *data EINA_UNUSED) +{ + WRN("OpenJPEG internal warning: '%s'.", msg); +} + +static void +_jp2k_info_cb(const char *msg, void *data EINA_UNUSED) +{ + INF("OpenJPEG internal information: '%s'.", msg); +} + +static Eina_Bool +evas_image_load_file_head_jp2k_internal(unsigned int *w, unsigned int *h, + unsigned char *alpha, + Evas_Image_Load_Opts *opts EINA_UNUSED, + void *map, size_t length, + int *error) +{ + opj_event_mgr_t event_mgr; + opj_dparameters_t params; + opj_dinfo_t *info; + opj_cio_t *cio; + opj_image_t *image; + int format; + int k; + + if (length < 2) + { + *error = EVAS_LOAD_ERROR_GENERIC; + return EINA_FALSE; + } + + if (((unsigned char *)map)[0] == 0xFF && ((unsigned char *)map)[1] == 0x4F) + format = CODEC_J2K; + else + format = CODEC_JP2; + + memset(&event_mgr, 0, sizeof(event_mgr)); + event_mgr.error_handler = _jp2k_error_cb; + event_mgr.warning_handler = _jp2k_warning_cb; + event_mgr.info_handler = _jp2k_info_cb; + + opj_set_default_decoder_parameters(¶ms); + info = opj_create_decompress(format); + if (!info) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + opj_set_event_mgr((opj_common_ptr)info, &event_mgr, NULL); + opj_setup_decoder(info, ¶ms); + + cio = opj_cio_open((opj_common_ptr)info, map, length); + if (!cio) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + + image = opj_decode(info, cio); + if (!image) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + + for (k = 1; k < image->numcomps; k++) + { + if (image->comps[k].w != image->comps[0].w) + goto free_image; + if (image->comps[k].h != image->comps[0].h) + goto free_image; + if (image->comps[k].prec > 8) + goto free_image; + } + + *w = image->comps[0].w; + *h = image->comps[0].h; + *alpha = ((image->numcomps == 4) || (image->numcomps == 2)) ? 1 : 0; + *error = EVAS_LOAD_ERROR_NONE; + + opj_image_destroy(image); + opj_cio_close(cio); + opj_destroy_decompress(info); + + return EINA_TRUE; + + free_image: + *error = EVAS_LOAD_ERROR_GENERIC; + opj_image_destroy(image); + opj_cio_close(cio); + opj_destroy_decompress(info); + + return EINA_FALSE; +} + +static Eina_Bool +evas_image_load_file_data_jp2k_internal(Evas_Image_Load_Opts *opts EINA_UNUSED, + Evas_Image_Property *prop EINA_UNUSED, + void *pixels, + void *map, size_t length, + int *error) +{ + opj_dparameters_t params; + opj_dinfo_t *info; + opj_cio_t *cio; + opj_image_t *image; + unsigned int *iter; + int format; + int idx; + + if (((unsigned char *)map)[0] == 0xFF && ((unsigned char *)map)[1] == 0x4F) + format = CODEC_J2K; + else + format = CODEC_JP2; + + opj_set_default_decoder_parameters(¶ms); + info = opj_create_decompress(format); + opj_set_event_mgr((opj_common_ptr)info, NULL, NULL); + opj_setup_decoder(info, ¶ms); + cio = opj_cio_open((opj_common_ptr)info, map, length); + image = opj_decode(info, cio); + + iter = pixels; + idx = 0; + + /* + * FIXME: + * image->numcomps == 4, image->color_space == CLRSPC_SYCC : YUV + */ + /* BGR(A) */ + if ((image->numcomps >= 3) && + (image->comps[0].dx == image->comps[1].dx) && + (image->comps[1].dx == image->comps[2].dx) && + (image->comps[0].dy == image->comps[1].dy) && + (image->comps[1].dy == image->comps[2].dy)) + { + int a; + int r; + int g; + int b; + int i; + int j; + + for (j = 0; j < image->comps[0].h; j++) + { + for (i = 0; i < image->comps[0].w; i++, idx++, iter++) + { + r = image->comps[0].data[idx]; + r+= (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); + if (r > 255) r = 255; + if (r < 0) r = 0; + + g = image->comps[1].data[idx]; + g+= (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); + if (g > 255) g = 255; + if (g < 0) g = 0; + + b = image->comps[2].data[idx]; + b+= (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0); + if (b > 255) b = 255; + if (b < 0) b = 0; + + if (image->numcomps == 4) + { + a = image->comps[3].data[idx]; + a+= (image->comps[3].sgnd ? 1 << (image->comps[3].prec - 1) : 0); + if (a > 255) a = 255; + if (a < 0) a = 0; + } + else + a = 255; + + *iter = a << 24 | r << 16 | g << 8 | b; + } + } + } + /* *GRAY(A) */ + else if (((image->numcomps == 1) || (image->numcomps == 2)) && + (image->comps[0].dx == image->comps[1].dx) && + (image->comps[1].dx == image->comps[2].dx) && + (image->comps[0].dy == image->comps[1].dy) && + (image->comps[1].dy == image->comps[2].dy)) + { + int a; + int g; + int i; + int j; + + for (j = 0; j < image->comps[0].h; j++) + { + for (i = 0; i < image->comps[0].w; i++, idx++, iter++) + { + g = image->comps[0].data[idx]; + g+= (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); + if (g > 255) g = 255; + if (g < 0) g = 0; + + if (image->numcomps == 2) + { + a = image->comps[1].data[idx]; + a+= (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); + if (a > 255) a = 255; + if (a < 0) a = 0; + } + else + a = 255; + + *iter = a << 24 | g << 16 | g << 8 | g; + } + } + } + + opj_image_destroy(image); + opj_cio_close(cio); + opj_destroy_decompress(info); + + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; +} + +static void * +evas_image_load_file_open_jp2k(Eina_File *f, Eina_Stringshare *key EINA_UNUSED, + Evas_Image_Load_Opts *opts, + Evas_Image_Animated *animated EINA_UNUSED, + int *error) +{ + Evas_Loader_Internal *loader; + + loader = calloc(1, sizeof (Evas_Loader_Internal)); + if (!loader) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return NULL; + } + + loader->f = f; + loader->opts = opts; + + return loader; +} + +static void +evas_image_load_file_close_jp2k(void *loader_data) +{ + free(loader_data); +} + +static Eina_Bool +evas_image_load_file_head_jp2k(void *loader_data, + Evas_Image_Property *prop, + int *error) +{ + Evas_Loader_Internal *loader = loader_data; + Evas_Image_Load_Opts *opts; + Eina_File *f; + void *map; + Eina_Bool val; + + opts = loader->opts; + f = loader->f; + + map = eina_file_map_all(f, EINA_FILE_RANDOM); + if (!map) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return EINA_FALSE; + } + + val = evas_image_load_file_head_jp2k_internal(&prop->w, &prop->h, + &prop->alpha, + opts, + map, eina_file_size_get(f), + error); + + eina_file_map_free(f, map); + + return val; +} + +static Eina_Bool +evas_image_load_file_data_jp2k(void *loader_data, + Evas_Image_Property *prop, + void *pixels, + int *error) +{ + Evas_Loader_Internal *loader = loader_data; + Evas_Image_Load_Opts *opts; + Eina_File *f; + void *map; + Eina_Bool val = EINA_FALSE; + + f = loader->f; + opts = loader->opts; + + map = eina_file_map_all(f, EINA_FILE_WILLNEED); + if (!map) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + goto on_error; + } + + val = evas_image_load_file_data_jp2k_internal(opts, prop, pixels, + map, eina_file_size_get(f), + error); + + eina_file_map_free(f, map); + + on_error: + return val; +} + +static Evas_Image_Load_Func evas_image_load_jp2k_func = +{ + evas_image_load_file_open_jp2k, + evas_image_load_file_close_jp2k, + evas_image_load_file_head_jp2k, + evas_image_load_file_data_jp2k, + NULL, + EINA_TRUE, + EINA_TRUE +}; + +static int +module_open(Evas_Module *em) +{ + if (!em) return 0; + + _evas_loader_jp2k_log_dom = eina_log_domain_register("evas-jp2k", EINA_COLOR_BLUE); + if (_evas_loader_jp2k_log_dom < 0) + { + EINA_LOG_ERR("Can not create a module log domain."); + return 0; + } + + em->functions = (void *)(&evas_image_load_jp2k_func); + + return 1; +} + +static void +module_close(Evas_Module *em EINA_UNUSED) +{ + eina_log_domain_unregister(_evas_loader_jp2k_log_dom); + _evas_loader_jp2k_log_dom = -1; +} + +static Evas_Module_Api evas_modapi = +{ + EVAS_MODULE_API_VERSION, + "jp2k", + "none", + { + module_open, + module_close + } +}; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, jp2k); + +#ifndef EVAS_STATIC_BUILD_JP2K +EVAS_EINA_MODULE_DEFINE(image_loader, jp2k); +#endif +