[gegl] operations: Add gegl:lcms-from-profile



commit 8641c1708b8d2efedfa2346d15fad423e6ee0b93
Author: Michael Henning <drawoc darkrefraction com>
Date:   Sun Jun 23 11:29:05 2013 -0400

    operations: Add gegl:lcms-from-profile

 configure.ac                            |   35 ++---
 operations/external/Makefile.am         |    7 +
 operations/external/lcms-from-profile.c |  237 +++++++++++++++++++++++++++++++
 3 files changed, 258 insertions(+), 21 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 474e151..ad0934e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -44,6 +44,7 @@ m4_define([introspection_required_version], [1.32.0])
 m4_define([glib_required_version], [2.32.3])
 m4_define([graphviz_required_version], [0.0.0])
 m4_define([jasper_required_version], [1.900.1])
+m4_define([lcms_required_version], [2.2])
 m4_define([lensfun_required_version], [0.2.5])
 m4_define([librsvg_required_version], [2.14.0])
 m4_define([lua_required_version], [5.1.0])
@@ -911,31 +912,23 @@ fi
 
 AM_CONDITIONAL(HAVE_V4L, test "$have_v4l" = "yes")
 
-
-
 ################
 # Check for lcms
 ################
- 
-#AC_ARG_WITH(liblcms, [  --without-lcms          build without CMS support])
-#
-#have_lcms="no"
-#if test x$with_liblcms != xno; then
-#  AC_CHECK_LIB(lcms, cmsCreateProofingTransform, [
-#    AC_CHECK_HEADER(lcms.h,
-#      have_lcms=yes
-#      LCMS_LIBS="-llcms", [
-#      AC_CHECK_HEADER(lcms/lcms.h,
-#      have_lcms=yes
-#        AC_DEFINE(HAVE_LCMS_LCMS_H, 1,
-#          [Define to 1 if the lcms header must be included as lcms/lcms.h])
-#        LCMS_LIBS="-llcms")])
-#  ])
-#fi
-# 
-#AM_CONDITIONAL(HAVE_LCMS, test "x$have_lcms" = "xyes")
-#AC_SUBST(LCMS_LIBS)
 
+AC_ARG_WITH(lcms, [  --without-lcms          build without lcms support])
+
+have_lcms="no (lcms support disabled)"
+if test "x$with_lcms" != xno; then
+  have_lcms=yes
+  PKG_CHECK_MODULES(LCMS, lcms2 >= lcms_required_version,
+    AC_DEFINE(HAVE_LCMS, 1, [Define to 1 if lcms is available])
+    LCMS='lcms$(EXEEXT)',
+    have_lcms="no (lcms not found or unusable)")
+fi
+
+AC_SUBST(LCMS)
+AM_CONDITIONAL(HAVE_LCMS, test "x$have_lcms" = xyes)
 
 ####################
 # Check for libspiro
diff --git a/operations/external/Makefile.am b/operations/external/Makefile.am
index b29df12..b9e7ba2 100644
--- a/operations/external/Makefile.am
+++ b/operations/external/Makefile.am
@@ -118,6 +118,13 @@ matting_levin_la_LIBADD  = $(op_libs) $(UMFPACK_LIBS)
 matting_levin_la_CFLAGS  = $(AM_CFLAGS)
 endif
 
+if HAVE_LCMS
+ops += lcms-from-profile.la
+lcms_from_profile_la_SOURCES = lcms-from-profile.c
+lcms_from_profile_la_LIBADD  = $(op_libs) $(LCMS_LIBS)
+lcms_from_profile_la_CFLAGS  = $(AM_CFLAGS)
+endif
+
 # No dependencies
 ops += ppm-load.la ppm-save.la
 ppm_load_la_SOURCES = ppm-load.c
diff --git a/operations/external/lcms-from-profile.c b/operations/external/lcms-from-profile.c
new file mode 100644
index 0000000..f52909d
--- /dev/null
+++ b/operations/external/lcms-from-profile.c
@@ -0,0 +1,237 @@
+/* This file is an image processing operation for GEGL
+ *
+ * GEGL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Michael Henning <drawoc darkrefraction com>
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#ifdef GEGL_CHANT_PROPERTIES
+
+gegl_chant_pointer (src_profile, _("Source Profile"),
+                    _("The cmsHPROFILE corresponding to the icc profile for "
+                      "the input data."))
+
+/* These are positioned so their values match up with the LCMS enum */
+gegl_chant_register_enum (gegl_rendering_intent)
+  enum_value (GEGL_RENDERING_INTENT_PERCEPTUAL,            "Perceptual")
+  enum_value (GEGL_RENDERING_INTENT_RELATIVE_COLORIMETRIC, "Relative Colorimetric")
+  enum_value (GEGL_RENDERING_INTENT_SATURATION,            "Saturation")
+  enum_value (GEGL_RENDERING_INTENT_ABSOLUTE_COLORIMETRIC, "Absolute Colorimetric")
+/* TODO: Add the K_ONLY and K_PLANE intents */
+gegl_chant_register_enum_end (GeglRenderingIntent)
+
+gegl_chant_enum (intent, _("Rendering Intent"),
+                 GeglRenderingIntent, gegl_rendering_intent,
+                 GEGL_RENDERING_INTENT_PERCEPTUAL,
+                 _("The rendering intent to use in the conversion."))
+
+gegl_chant_boolean (black_point_compensation, _("Black Point Compensation"),
+                    FALSE, _("Convert using black point compensation."))
+
+#else
+
+#define GEGL_CHANT_TYPE_FILTER
+#define GEGL_CHANT_C_FILE      "lcms-from-profile.c"
+
+#include "gegl-chant.h"
+#include <lcms2.h>
+
+static void prepare (GeglOperation *operation)
+{
+  /* TODO: move input format detection code here */
+  gegl_operation_set_format (operation, "output", babl_format ("RGBA float"));
+}
+
+/* This profile corresponds to babl_model ("RGB") */
+static cmsHPROFILE
+create_lcms_linear_rgb_profile (void)
+{
+  cmsHPROFILE ret;
+
+  /* white point is D65 */
+  cmsCIExyY whitepoint = {0.312713, 0.329016, 1.0};
+
+  /* primaries are ITU‐R BT.709‐5 (xYY) */
+  cmsCIExyYTRIPLE primaries = {
+    /*R*/ {0.6400, 0.3300, 1.0},
+    /*G*/ {0.3000, 0.6000, 1.0},
+    /*B*/ {0.1500, 0.0600, 1.0}
+  };
+
+  /* linear light */
+  cmsToneCurve* linear[3];
+  linear[0] = linear[1] = linear[2] = cmsBuildGamma (NULL, 1.0); 
+
+  /* create the profile, cleanup, and return */
+  ret = cmsCreateRGBProfile (&whitepoint, &primaries, linear);
+  cmsFreeToneCurve (linear[0]);
+  return ret;
+}
+
+static cmsUInt32Number
+determine_lcms_format (const Babl *babl, cmsHPROFILE profile)
+{
+  cmsUInt32Number format = COLORSPACE_SH (PT_ANY);
+  gint channels, bpc, alpha;
+  const Babl *type;
+
+  channels = cmsChannelsOf (cmsGetColorSpace (profile));
+  alpha = babl_format_get_n_components (babl) - channels;
+  bpc = babl_format_get_bytes_per_pixel (babl)
+          / babl_format_get_n_components (babl);
+
+  type = babl_format_get_type (babl, 0);
+  if (type == babl_type ("half") ||
+      type == babl_type ("float") ||
+      type == babl_type ("double"))
+    format |= FLOAT_SH (1);
+
+  /* bpc == 8 overflows the bitfield otherwise */
+  bpc &= 0x07;
+
+  /*
+   * This is needed so the alpha component lines up with RGBA float
+   * for our memcpy hack later on.
+   */
+  if (alpha > 1 || (alpha && channels != 3))
+    return 0;
+
+  format |= EXTRA_SH (alpha) | CHANNELS_SH (channels) | BYTES_SH (bpc);
+  return format;
+}
+
+static gboolean
+process (GeglOperation       *operation,
+         GeglBuffer          *input,
+         GeglBuffer          *output,
+         const GeglRectangle *result,
+         gint                 level)
+{
+  GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+
+  cmsHTRANSFORM transform;
+  const Babl *in_format, *out_format;
+
+  gboolean alpha;
+  gint bpp;
+
+  in_format = babl_format_n (babl_type ("float"),
+                 babl_format_get_n_components (gegl_buffer_get_format (input)));
+
+  bpp = babl_format_get_bytes_per_pixel (in_format);
+
+  /* create the transformation */
+  {
+    cmsHPROFILE in_profile, out_profile;
+    cmsUInt32Number format;
+
+    in_profile = o->src_profile;
+
+    format = determine_lcms_format (in_format, in_profile);
+
+    if (format == 0)
+      return FALSE;
+
+    if (format & EXTRA_SH (1))
+      alpha = TRUE;
+    else
+      alpha = FALSE;
+
+    out_profile = create_lcms_linear_rgb_profile ();
+
+    transform = cmsCreateTransform (in_profile, format,
+                                    out_profile, alpha ? TYPE_RGBA_FLT : TYPE_RGB_FLT,
+                                    o->intent, o->black_point_compensation ? cmsFLAGS_BLACKPOINTCOMPENSATION 
: 0);
+
+    cmsCloseProfile (out_profile);
+  }
+
+  out_format = alpha ? babl_format ("RGBA float") : babl_format ("RGB float");
+
+  /* iterate over the pixels */
+  {
+    GeglBufferIterator *gi;
+
+    gi = gegl_buffer_iterator_new (input, result, 0, in_format,
+                                   GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
+
+    gegl_buffer_iterator_add (gi, output, result, 0, out_format,
+                              GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE);
+
+    while (gegl_buffer_iterator_next (gi))
+      {
+        if (alpha)
+          memcpy (gi->data[1], gi->data[0], bpp * gi->length);
+
+        cmsDoTransform (transform, gi->data[0], gi->data[1], gi->length);
+      }
+  }
+
+  cmsDeleteTransform (transform);
+
+  return TRUE;
+}
+
+static gboolean
+operation_process (GeglOperation        *operation,
+                   GeglOperationContext *context,
+                   const gchar          *output_prop,
+                   const GeglRectangle  *result,
+                   gint                  level)
+{
+  GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+  GeglOperationClass  *operation_class;
+
+  operation_class = GEGL_OPERATION_CLASS (gegl_chant_parent_class);
+
+  /* If the profile is NULL, simply become a nop */
+  if (!o->src_profile)
+    {
+      gpointer input = gegl_operation_context_get_object (context, "input");
+      gegl_operation_context_take_object (context, "output",
+                                          g_object_ref (input));
+      return TRUE;
+    }
+
+  return operation_class->process (operation, context,
+                                   output_prop, result, level);
+}
+
+static void
+gegl_chant_class_init (GeglChantClass *klass)
+{
+  GeglOperationClass       *operation_class;
+  GeglOperationFilterClass *filter_class;
+
+  operation_class = GEGL_OPERATION_CLASS (klass);
+  filter_class    = GEGL_OPERATION_FILTER_CLASS (klass);
+
+  operation_class->prepare = prepare;
+  operation_class->process = operation_process;
+
+  filter_class->process = process;
+
+  gegl_operation_class_set_keys (operation_class,
+    "name",        "gegl:lcms-from-profile",
+    "categories",  "color",
+    "description",
+       _("Converts the input from an ICC color profile to "
+         "a well defined babl format. The input buffer should "
+         "be a babl_format_n type."),
+    NULL);
+}
+#endif


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]