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 +