evas: add JPEG 2000 loader.
authorVincent Torri <vincent.torri@gmail.com>
Sat, 28 Sep 2013 12:28:41 +0000 (14:28 +0200)
committerCedric Bail <cedric.bail@samsung.com>
Tue, 1 Oct 2013 07:38:44 +0000 (16:38 +0900)
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
NEWS
configure.ac
m4/evas_check_loader.m4
src/Makefile_Evas.am
src/lib/evas/common/evas_image_load.c
src/lib/evas/file/evas_module.c
src/modules/evas/loaders/jp2k/evas_image_load_jp2k.c [new file with mode: 0644]

index 72873d0892bfdaec83cf997981ce46657cf03483..1206dcc659761452798e8ca7291a685e12ac3fc2 100644 (file)
--- 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 e40c53f056d0a44ce6576e0a215dce45386be402..b486810930a110eb057a18feca0c6574b2d3b731 100644 (file)
--- 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
index 056e4ec5c7bb3af31f80f7df1b703bcc823c4668..dd327bb2ba0be74c4aaaf3774f9878c24dac8a77 100644 (file)
@@ -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}])
index e10e33040d1e931141694a267c7da87820289344..b34c28fc05c52299b84a4602be9254ee57731f3e 100644 (file)
@@ -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],
index 924e0e159b0d57aa823521295b1039bd62edd68e..29bea4b6037d8af9e51346cbdd2ec22f6b2af6ca 100644 (file)
@@ -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
index afca101deb3969b5931da3154ba2bfcb0c39aabb..c945442be597c25f5a381ff03ab161833a18660a 100644 (file)
@@ -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"),
index 9f70e603697e4b07c2f69b544a852f80b536e29c..b3d0a87c9207790e605060eedeb7d61110813c8a 100644 (file)
@@ -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 (file)
index 0000000..28200ce
--- /dev/null
@@ -0,0 +1,409 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include <openjpeg.h>
+
+#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(&params);
+   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, &params);
+
+   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(&params);
+   info = opj_create_decompress(format);
+   opj_set_event_mgr((opj_common_ptr)info, NULL, NULL);
+   opj_setup_decoder(info, &params);
+   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
+