summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Hughes <richard@hughsie.com>2013-04-29 10:27:55 +0100
committerRichard Hughes <richard@hughsie.com>2013-04-29 10:27:58 +0100
commit934517d2c2954adc209ea546126c8d84e1dc2988 (patch)
tree41332d03e1e1f8fd646b3d1b9ae693845fcdd8aa
parent3d1bd6837cacc1372d83721d7deed273d574963f (diff)
downloadcolord-934517d2c2954adc209ea546126c8d84e1dc2988.tar.gz
Add a ICC transform object for simple RGB conversions
We don't want to suggest to applications to link to lcms2 and learn a whole new library just to convert small images and icons. Larger or more complex applications will want to use lcms2 for the additional control over the input and output buffer formats.
-rw-r--r--doc/api/colord-docs.xml1
-rw-r--r--lib/colord/Makefile.am2
-rw-r--r--lib/colord/cd-enum.c27
-rw-r--r--lib/colord/cd-enum.h16
-rw-r--r--lib/colord/cd-self-test.c57
-rw-r--r--lib/colord/cd-transform.c675
-rw-r--r--lib/colord/cd-transform.h115
7 files changed, 893 insertions, 0 deletions
diff --git a/doc/api/colord-docs.xml b/doc/api/colord-docs.xml
index ddee622..08e8bab 100644
--- a/doc/api/colord-docs.xml
+++ b/doc/api/colord-docs.xml
@@ -81,6 +81,7 @@
<xi:include href="xml/cd-color.xml"/>
<xi:include href="xml/cd-dom.xml"/>
<xi:include href="xml/cd-icc.xml"/>
+ <xi:include href="xml/cd-transform.xml"/>
<xi:include href="xml/cd-interp-akima.xml"/>
<xi:include href="xml/cd-interp-linear.xml"/>
<xi:include href="xml/cd-interp.xml"/>
diff --git a/lib/colord/Makefile.am b/lib/colord/Makefile.am
index 53af56c..743b308 100644
--- a/lib/colord/Makefile.am
+++ b/lib/colord/Makefile.am
@@ -46,6 +46,7 @@ libcolordbase_include_HEADERS = \
cd-profile-sync.h \
cd-sensor.h \
cd-sensor-sync.h \
+ cd-transform.h \
cd-version.h
libcolordprivate_la_SOURCES = \
@@ -59,6 +60,7 @@ libcolordprivate_la_SOURCES = \
cd-interp.c \
cd-it8.c \
cd-it8-utils.c \
+ cd-transform.c \
cd-math.c
libcolordprivate_la_LIBADD = \
$(LIBM) \
diff --git a/lib/colord/cd-enum.c b/lib/colord/cd-enum.c
index cc91e8d..a04b926 100644
--- a/lib/colord/cd-enum.c
+++ b/lib/colord/cd-enum.c
@@ -104,6 +104,15 @@ static const CdEnumMatch enum_rendering_intent[] = {
{0, NULL}
};
+static const CdEnumMatch enum_pixel_format[] = {
+ {CD_PIXEL_FORMAT_UNKNOWN, "unknown"}, /* fall though value */
+ {CD_PIXEL_FORMAT_RGB_8, "rgb-8"},
+ {CD_PIXEL_FORMAT_RGB_16, "rgb-16"},
+ {CD_PIXEL_FORMAT_RGBA_8, "rgba-8"},
+ {CD_PIXEL_FORMAT_RGBA_16, "rgba-16"},
+ {0, NULL}
+};
+
static const CdEnumMatch enum_colorspace[] = {
{CD_COLORSPACE_UNKNOWN, "unknown"}, /* fall though value */
{CD_COLORSPACE_CMY, "cmy"},
@@ -328,6 +337,24 @@ cd_rendering_intent_from_string (const gchar *rendering_intent)
}
/**
+ * cd_pixel_format_to_string:
+ **/
+const gchar *
+cd_pixel_format_to_string (CdPixelFormat pixel_format)
+{
+ return cd_enum_to_string (enum_pixel_format, pixel_format);
+}
+
+/**
+ * cd_pixel_format_from_string:
+ **/
+CdPixelFormat
+cd_pixel_format_from_string (const gchar *pixel_format)
+{
+ return cd_enum_from_string (enum_pixel_format, pixel_format);
+}
+
+/**
* cd_colorspace_to_string:
**/
const gchar *
diff --git a/lib/colord/cd-enum.h b/lib/colord/cd-enum.h
index 79dd8f2..9bfc1cd 100644
--- a/lib/colord/cd-enum.h
+++ b/lib/colord/cd-enum.h
@@ -95,6 +95,20 @@ typedef enum {
} CdRenderingIntent;
/**
+ * CdPixelFormat:
+ *
+ * The pixel format of an image.
+ **/
+typedef enum {
+ CD_PIXEL_FORMAT_UNKNOWN,
+ CD_PIXEL_FORMAT_RGB_8,
+ CD_PIXEL_FORMAT_RGB_16,
+ CD_PIXEL_FORMAT_RGBA_8,
+ CD_PIXEL_FORMAT_RGBA_16,
+ CD_PIXEL_FORMAT_LAST
+} CdPixelFormat;
+
+/**
* CdColorspace:
*
* The known colorspace.
@@ -498,6 +512,8 @@ const gchar *cd_profile_kind_to_string (CdProfileKind profile_kind);
CdProfileKind cd_profile_kind_from_string (const gchar *profile_kind);
CdRenderingIntent cd_rendering_intent_from_string (const gchar *rendering_intent);
const gchar *cd_rendering_intent_to_string (CdRenderingIntent rendering_intent);
+CdPixelFormat cd_pixel_format_from_string (const gchar *pixel_format);
+const gchar *cd_pixel_format_to_string (CdPixelFormat pixel_format);
const gchar *cd_colorspace_to_string (CdColorspace colorspace);
CdColorspace cd_colorspace_from_string (const gchar *colorspace);
const gchar *cd_device_mode_to_string (CdDeviceMode device_mode);
diff --git a/lib/colord/cd-self-test.c b/lib/colord/cd-self-test.c
index 5c9f007..d6f5bc1 100644
--- a/lib/colord/cd-self-test.c
+++ b/lib/colord/cd-self-test.c
@@ -52,6 +52,7 @@
#include "cd-profile-sync.h"
#include "cd-sensor.h"
#include "cd-sensor-sync.h"
+#include "cd-transform.h"
#include "cd-version.h"
static gboolean has_colord_process = FALSE;
@@ -3629,6 +3630,61 @@ colord_icc_localized_func (void)
g_object_unref (icc);
}
+static void
+colord_transform_func (void)
+{
+ CdTransform *transform;
+ gboolean ret;
+ GError *error = NULL;
+ guint8 data_in[3] = { 127, 32, 64 };
+ guint8 data_out[3];
+ CdIcc *icc;
+ gchar *filename;
+ GFile *file;
+
+ /* setup transform with 8 bit RGB */
+ transform = cd_transform_new ();
+ cd_transform_set_intent (transform, CD_RENDERING_INTENT_PERCEPTUAL);
+ g_assert_cmpint (cd_transform_get_intent (transform), ==, CD_RENDERING_INTENT_PERCEPTUAL);
+ cd_transform_set_format (transform, CD_PIXEL_FORMAT_RGB_8);
+ g_assert_cmpint (cd_transform_get_format (transform), ==, CD_PIXEL_FORMAT_RGB_8);
+
+ /* setup profiles */
+ cd_transform_set_input (transform, NULL);
+ cd_transform_set_abstract (transform, NULL);
+
+ filename = _g_test_realpath (TESTDATADIR "/ibm-t61.icc");
+ file = g_file_new_for_path (filename);
+ icc = cd_icc_new ();
+ ret = cd_icc_load_file (icc,
+ file,
+ CD_ICC_LOAD_FLAGS_NONE,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ cd_transform_set_output (transform, icc);
+ g_free (filename);
+ g_object_unref (file);
+ g_object_unref (icc);
+
+ /* run through profile */
+ ret = cd_transform_process (transform,
+ data_in,
+ data_out,
+ 1, 1, 1,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ g_assert_cmpint (data_out[0], ==, 144);
+ g_assert_cmpint (data_out[1], ==, 0);
+ g_assert_cmpint (data_out[2], ==, 69);
+
+ g_object_unref (transform);
+}
+
int
main (int argc, char **argv)
{
@@ -3639,6 +3695,7 @@ main (int argc, char **argv)
g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
/* tests go here */
+ g_test_add_func ("/colord/transform", colord_transform_func);
g_test_add_func ("/colord/icc", colord_icc_func);
g_test_add_func ("/colord/icc{localized}", colord_icc_localized_func);
g_test_add_func ("/colord/icc{edid}", colord_icc_edid_func);
diff --git a/lib/colord/cd-transform.c b/lib/colord/cd-transform.c
new file mode 100644
index 0000000..edfaf65
--- /dev/null
+++ b/lib/colord/cd-transform.c
@@ -0,0 +1,675 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * This library 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 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * SECTION:cd-transform
+ * @short_description: An ICC transform
+ *
+ * This object is a simple ICC transform that allows mapping of simple RGB
+ * spaces to other simple RGB spaces using one, two or three ICC profiles.
+ *
+ * This object is not supposed to re-implement LCMS, and if you need anything more
+ * complicated than simple RGB buffers (for instance, floating point, CMYK, BPC,
+ * etc.) then you are better off using lcms2 directly.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <lcms2.h>
+
+#include "cd-transform.h"
+
+static void cd_transform_class_init (CdTransformClass *klass);
+static void cd_transform_init (CdTransform *transform);
+static void cd_transform_finalize (GObject *object);
+
+#define CD_TRANSFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CD_TYPE_TRANSFORM, CdTransformPrivate))
+
+/**
+ * CdTransformPrivate:
+ *
+ * Private #CdTransform data
+ **/
+struct _CdTransformPrivate
+{
+ CdIcc *input;
+ CdIcc *output;
+ CdIcc *abstract;
+ CdPixelFormat pixel_format;
+ CdRenderingIntent rendering_intent;
+ cmsHPROFILE srgb;
+ cmsHTRANSFORM lcms_transform;
+};
+
+G_DEFINE_TYPE (CdTransform, cd_transform, G_TYPE_OBJECT)
+
+enum {
+ PROP_0,
+ PROP_RENDERING_INTENT,
+ PROP_PIXEL_FORMAT,
+ PROP_INPUT,
+ PROP_OUTPUT,
+ PROP_ABSTRACT,
+ PROP_LAST
+};
+
+/**
+ * cd_transform_error_quark:
+ *
+ * Return value: An error quark.
+ *
+ * Since: 0.1.34
+ **/
+GQuark
+cd_transform_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (!quark)
+ quark = g_quark_from_static_string ("cd_transform_error");
+ return quark;
+}
+
+/**
+ * cd_transform_invalidate:
+ **/
+static void
+cd_transform_invalidate (CdTransform *transform)
+{
+ if (transform->priv->lcms_transform != NULL)
+ cmsDeleteTransform (transform->priv->lcms_transform);
+ transform->priv->lcms_transform = NULL;
+}
+
+/**
+ * cd_transform_set_input:
+ * @transform: a #CdTransform instance.
+ * @icc: a #CdIcc instance or %NULL.
+ *
+ * Sets the input profile to use for the transform.
+ *
+ * Since: 0.1.34
+ **/
+void
+cd_transform_set_input (CdTransform *transform, CdIcc *icc)
+{
+ g_return_if_fail (CD_IS_TRANSFORM (transform));
+ g_return_if_fail (icc == NULL || CD_IS_ICC (icc));
+
+ if (transform->priv->input != NULL)
+ g_clear_object (&transform->priv->input);
+ if (icc != NULL)
+ transform->priv->input = g_object_ref (icc);
+ cd_transform_invalidate (transform);
+}
+
+/**
+ * cd_transform_get_input:
+ * @transform: a #CdTransform instance.
+ *
+ * Gets the input profile to use for the transform.
+ *
+ * Return value: (transfer none): The input profile
+ *
+ * Since: 0.1.34
+ **/
+CdIcc *
+cd_transform_get_input (CdTransform *transform)
+{
+ g_return_val_if_fail (CD_IS_TRANSFORM (transform), NULL);
+ return transform->priv->input;
+}
+
+/**
+ * cd_transform_set_output:
+ * @transform: a #CdTransform instance.
+ * @icc: a #CdIcc instance or %NULL.
+ *
+ * Sets the output profile to use for the transform.
+ *
+ * Since: 0.1.34
+ **/
+void
+cd_transform_set_output (CdTransform *transform, CdIcc *icc)
+{
+ g_return_if_fail (CD_IS_TRANSFORM (transform));
+ g_return_if_fail (icc == NULL || CD_IS_ICC (icc));
+
+ if (transform->priv->output != NULL)
+ g_clear_object (&transform->priv->output);
+ if (icc != NULL)
+ transform->priv->output = g_object_ref (icc);
+ cd_transform_invalidate (transform);
+}
+
+/**
+ * cd_transform_get_output:
+ * @transform: a #CdTransform instance.
+ *
+ * Gets the input profile to use for the transform.
+ *
+ * Return value: (transfer none): The output profile
+ *
+ * Since: 0.1.34
+ **/
+CdIcc *
+cd_transform_get_output (CdTransform *transform)
+{
+ g_return_val_if_fail (CD_IS_TRANSFORM (transform), NULL);
+ return transform->priv->output;
+}
+
+/**
+ * cd_transform_set_abstract:
+ * @transform: a #CdTransform instance.
+ * @icc: a #CdIcc instance or %NULL.
+ *
+ * Sets the abstract profile to use for the transform.
+ * This is typically only needed for soft-proofing.
+ *
+ * Since: 0.1.34
+ **/
+void
+cd_transform_set_abstract (CdTransform *transform, CdIcc *icc)
+{
+ g_return_if_fail (CD_IS_TRANSFORM (transform));
+ g_return_if_fail (icc == NULL || CD_IS_ICC (icc));
+
+ if (transform->priv->abstract != NULL)
+ g_clear_object (&transform->priv->abstract);
+ if (icc != NULL)
+ transform->priv->abstract = g_object_ref (icc);
+ cd_transform_invalidate (transform);
+}
+
+/**
+ * cd_transform_get_abstract:
+ * @transform: a #CdTransform instance.
+ *
+ * Gets the abstract profile to use for the transform.
+ *
+ * Return value: (transfer none): The abstract profile
+ *
+ * Since: 0.1.34
+ **/
+CdIcc *
+cd_transform_get_abstract (CdTransform *transform)
+{
+ g_return_val_if_fail (CD_IS_TRANSFORM (transform), NULL);
+ return transform->priv->abstract;
+}
+
+/**
+ * cd_transform_set_format:
+ * @transform: a #CdTransform instance.
+ * @pixel_format: The pixel format, e.g. %CD_PIXEL_FORMAT_RGBA_8
+ *
+ * Sets the pixel format to use for the transform.
+ *
+ * Since: 0.1.34
+ **/
+void
+cd_transform_set_format (CdTransform *transform, CdPixelFormat pixel_format)
+{
+ g_return_if_fail (CD_IS_TRANSFORM (transform));
+ g_return_if_fail (pixel_format != CD_PIXEL_FORMAT_UNKNOWN);
+
+ transform->priv->pixel_format = pixel_format;
+ cd_transform_invalidate (transform);
+}
+
+/**
+ * cd_transform_get_format:
+ * @transform: a #CdTransform instance.
+ *
+ * Gets the pixel format to use for the transform.
+ *
+ * Return value: the pixel format, e.g. %CD_PIXEL_FORMAT_RGBA_8
+ *
+ * Since: 0.1.34
+ **/
+CdPixelFormat
+cd_transform_get_format (CdTransform *transform)
+{
+ g_return_val_if_fail (CD_IS_TRANSFORM (transform), CD_PIXEL_FORMAT_UNKNOWN);
+ return transform->priv->pixel_format;
+}
+
+/**
+ * cd_transform_set_intent:
+ * @transform: a #CdTransform instance.
+ * @rendering_intent: the rendering intent, e.g. %CD_RENDERING_INTENT_PERCEPTUAL
+ *
+ * Sets the rendering intent to use for the transform.
+ *
+ * Since: 0.1.34
+ **/
+void
+cd_transform_set_intent (CdTransform *transform, CdRenderingIntent rendering_intent)
+{
+ g_return_if_fail (CD_IS_TRANSFORM (transform));
+ g_return_if_fail (rendering_intent != CD_RENDERING_INTENT_UNKNOWN);
+
+ transform->priv->rendering_intent = rendering_intent;
+ cd_transform_invalidate (transform);
+}
+
+/**
+ * cd_transform_get_intent:
+ * @transform: a #CdTransform instance.
+ *
+ * Gets the rendering intent to use for the transform.
+ *
+ * Return value: The rendering intent, e.g. %CD_RENDERING_INTENT_PERCEPTUAL
+ *
+ * Since: 0.1.34
+ **/
+CdRenderingIntent
+cd_transform_get_intent (CdTransform *transform)
+{
+ g_return_val_if_fail (CD_IS_TRANSFORM (transform), CD_RENDERING_INTENT_UNKNOWN);
+ return transform->priv->rendering_intent;
+}
+
+/* map lcms profile class to colord type */
+const struct {
+ guint32 lcms;
+ CdPixelFormat colord;
+} map_pixel_format[] = {
+ { TYPE_RGB_8, CD_PIXEL_FORMAT_RGB_8 },
+ { TYPE_RGBA_8, CD_PIXEL_FORMAT_RGBA_8 },
+ { TYPE_RGB_16, CD_PIXEL_FORMAT_RGB_16 },
+ { TYPE_RGBA_16, CD_PIXEL_FORMAT_RGBA_16 },
+ { 0, CD_PIXEL_FORMAT_LAST }
+};
+
+/* map lcms intent to colord type */
+const struct {
+ gint lcms;
+ CdRenderingIntent colord;
+} map_rendering_intent[] = {
+ { INTENT_PERCEPTUAL, CD_RENDERING_INTENT_PERCEPTUAL },
+ { INTENT_ABSOLUTE_COLORIMETRIC, CD_RENDERING_INTENT_ABSOLUTE_COLORIMETRIC },
+ { INTENT_RELATIVE_COLORIMETRIC, CD_RENDERING_INTENT_RELATIVE_COLORIMETRIC },
+ { INTENT_SATURATION, CD_RENDERING_INTENT_SATURATION },
+ { 0, CD_RENDERING_INTENT_LAST }
+};
+
+/**
+ * cd_transform_setup:
+ **/
+static gboolean
+cd_transform_setup (CdTransform *transform, GError **error)
+{
+ CdTransformPrivate *priv = transform->priv;
+ cmsHPROFILE profile_in;
+ cmsHPROFILE profile_out;
+ gboolean ret = TRUE;
+ gint lcms_intent = -1;
+ guint32 lcms_format = 0;
+ guint i;
+
+ /* find native rendering intent */
+ for (i = 0; map_rendering_intent[i].colord != CD_RENDERING_INTENT_LAST; i++) {
+ if (map_rendering_intent[i].colord == priv->rendering_intent) {
+ lcms_intent = map_rendering_intent[i].lcms;
+ break;
+ }
+ }
+ g_assert (lcms_intent != -1);
+
+ /* find native pixel format */
+ for (i = 0; map_pixel_format[i].colord != CD_PIXEL_FORMAT_LAST; i++) {
+ if (map_pixel_format[i].colord == priv->pixel_format) {
+ lcms_format = map_pixel_format[i].lcms;
+ break;
+ }
+ }
+ g_assert (lcms_format > 0);
+
+ /* get input profile */
+ if (priv->input != NULL) {
+ if (cd_icc_get_colorspace (priv->input) != CD_COLORSPACE_RGB) {
+ ret = FALSE;
+ g_set_error_literal (error,
+ CD_TRANSFORM_ERROR,
+ CD_TRANSFORM_ERROR_INVALID_COLORSPACE,
+ "input colorspace has to be RGB");
+ goto out;
+ }
+ g_debug ("using input profile of %s",
+ cd_icc_get_filename (priv->input));
+ profile_in = cd_icc_get_handle (priv->input);
+ } else {
+ g_debug ("no input profile, assume sRGB");
+ profile_in = priv->srgb;
+ }
+
+ /* get output profile */
+ if (priv->output != NULL) {
+ if (cd_icc_get_colorspace (priv->output) != CD_COLORSPACE_RGB) {
+ ret = FALSE;
+ g_set_error_literal (error,
+ CD_TRANSFORM_ERROR,
+ CD_TRANSFORM_ERROR_INVALID_COLORSPACE,
+ "output colorspace has to be RGB");
+ goto out;
+ }
+ g_debug ("using output profile of %s",
+ cd_icc_get_filename (priv->output));
+ profile_out = cd_icc_get_handle (priv->output);
+ } else {
+ g_debug ("no output profile, assume sRGB");
+ profile_out = priv->srgb;
+ }
+
+ /* get abstract profile */
+ if (priv->abstract != NULL) {
+ cmsHPROFILE profiles[3];
+
+ if (cd_icc_get_colorspace (priv->abstract) != CD_COLORSPACE_LAB) {
+ ret = FALSE;
+ g_set_error_literal (error,
+ CD_TRANSFORM_ERROR,
+ CD_TRANSFORM_ERROR_INVALID_COLORSPACE,
+ "abstract colorspace has to be Lab");
+ goto out;
+ }
+
+ /* generate a devicelink */
+ profiles[0] = profile_in;
+ profiles[1] = cd_icc_get_handle (priv->abstract);
+ profiles[2] = profile_out;
+ priv->lcms_transform = cmsCreateMultiprofileTransform (profiles,
+ 3,
+ lcms_format,
+ lcms_format,
+ lcms_intent,
+ 0);
+
+ } else {
+ /* create basic transform */
+ priv->lcms_transform = cmsCreateTransform (profile_in,
+ lcms_format,
+ profile_out,
+ lcms_format,
+ lcms_intent,
+ 0);
+ }
+
+ /* failed? */
+ if (priv->lcms_transform == NULL) {
+ ret = FALSE;
+ g_set_error_literal (error,
+ CD_TRANSFORM_ERROR,
+ CD_TRANSFORM_ERROR_FAILED_TO_SETUP_TRANSFORM,
+ "failed to setup transform, unspecified error");
+ goto out;
+ }
+out:
+ return ret;
+}
+
+/**
+ * cd_transform_process:
+ * @transform: a #CdTransform instance.
+ * @data_in: the data buffer to convert
+ * @data_out: the data buffer to return, which can be the same as @data_in
+ * @width: the width of @data_in
+ * @height: the height of @data_in
+ * @rowstride: the rowstride of @data_in, typically the same as @width
+ * @cancellable: A %GError, or %NULL
+ * @error: A %GCancellable, or %NULL
+ *
+ * Processes a block of data through the transform.
+ * Once the transform has been setup it is cached and only re-created if any
+ * of the formats, input, output or abstract profiles are changed.
+ *
+ * Return value: %TRUE if the pixels were successfully transformed.
+ *
+ * Since: 0.1.34
+ **/
+gboolean
+cd_transform_process (CdTransform *transform,
+ gpointer data_in,
+ gpointer data_out,
+ guint width,
+ guint height,
+ guint rowstride,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CdTransformPrivate *priv = transform->priv;
+ gboolean ret = TRUE;
+ guint i;
+ guint8 *p_in;
+ guint8 *p_out;
+
+ g_return_val_if_fail (CD_IS_TRANSFORM (transform), FALSE);
+ g_return_val_if_fail (data_in != NULL, FALSE);
+ g_return_val_if_fail (data_out != NULL, FALSE);
+ g_return_val_if_fail (width != 0, FALSE);
+ g_return_val_if_fail (height != 0, FALSE);
+ g_return_val_if_fail (rowstride != 0, FALSE);
+
+ /* check stuff that should have been set */
+ if (priv->rendering_intent == CD_RENDERING_INTENT_UNKNOWN) {
+ ret = FALSE;
+ g_set_error_literal (error,
+ CD_TRANSFORM_ERROR,
+ CD_TRANSFORM_ERROR_FAILED_TO_SETUP_TRANSFORM,
+ "rendering intent not set");
+ goto out;
+ }
+ if (priv->pixel_format == CD_PIXEL_FORMAT_UNKNOWN) {
+ ret = FALSE;
+ g_set_error_literal (error,
+ CD_TRANSFORM_ERROR,
+ CD_TRANSFORM_ERROR_FAILED_TO_SETUP_TRANSFORM,
+ "pixel format not set");
+ goto out;
+ }
+
+ /* setup the transform if required */
+ if (priv->lcms_transform == NULL) {
+ ret = cd_transform_setup (transform, error);
+ if (!ret)
+ goto out;
+ }
+
+ /* do conversion */
+ p_in = data_in;
+ p_out = data_out;
+ for (i = 0; i < height; i++) {
+ cmsDoTransform (priv->lcms_transform, p_in, p_out, width);
+ p_in += rowstride;
+ p_out += rowstride;
+ }
+out:
+ return ret;
+}
+
+/**
+ * cd_transform_get_property:
+ **/
+static void
+cd_transform_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ CdTransform *transform = CD_TRANSFORM (object);
+ CdTransformPrivate *priv = transform->priv;
+
+ switch (prop_id) {
+ case PROP_INPUT:
+ g_value_set_object (value, priv->input);
+ break;
+ case PROP_OUTPUT:
+ g_value_set_object (value, priv->output);
+ break;
+ case PROP_ABSTRACT:
+ g_value_set_object (value, priv->abstract);
+ break;
+ case PROP_RENDERING_INTENT:
+ g_value_set_uint (value, priv->rendering_intent);
+ break;
+ case PROP_PIXEL_FORMAT:
+ g_value_set_uint (value, priv->pixel_format);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * cd_transform_set_property:
+ **/
+static void
+cd_transform_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+ CdTransform *transform = CD_TRANSFORM (object);
+ switch (prop_id) {
+ case PROP_RENDERING_INTENT:
+ cd_transform_set_intent (transform, g_value_get_uint (value));
+ break;
+ case PROP_PIXEL_FORMAT:
+ cd_transform_set_format (transform, g_value_get_uint (value));
+ break;
+ case PROP_INPUT:
+ cd_transform_set_input (transform, g_value_get_object (value));
+ break;
+ case PROP_OUTPUT:
+ cd_transform_set_output (transform, g_value_get_object (value));
+ break;
+ case PROP_ABSTRACT:
+ cd_transform_set_abstract (transform, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * cd_transform_class_init:
+ */
+static void
+cd_transform_class_init (CdTransformClass *klass)
+{
+ GParamSpec *pspec;
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = cd_transform_finalize;
+ object_class->get_property = cd_transform_get_property;
+ object_class->set_property = cd_transform_set_property;
+
+ /**
+ * CdTransform: rendering-intent:
+ */
+ pspec = g_param_spec_uint ("rendering-intent", NULL, NULL,
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_RENDERING_INTENT, pspec);
+
+ /**
+ * CdTransform: pixel-format:
+ */
+ pspec = g_param_spec_uint ("pixel-format", NULL, NULL,
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_PIXEL_FORMAT, pspec);
+
+ /**
+ * CdTransform: input:
+ */
+ pspec = g_param_spec_object ("input", NULL, NULL,
+ CD_TYPE_ICC,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_INPUT, pspec);
+
+ /**
+ * CdTransform: output:
+ */
+ pspec = g_param_spec_object ("output", NULL, NULL,
+ CD_TYPE_ICC,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_OUTPUT, pspec);
+
+ /**
+ * CdTransform: abstract:
+ */
+ pspec = g_param_spec_object ("abstract", NULL, NULL,
+ CD_TYPE_ICC,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_ABSTRACT, pspec);
+
+ g_type_class_add_private (klass, sizeof (CdTransformPrivate));
+}
+
+/**
+ * cd_transform_init:
+ */
+static void
+cd_transform_init (CdTransform *transform)
+{
+ transform->priv = CD_TRANSFORM_GET_PRIVATE (transform);
+ transform->priv->rendering_intent = CD_RENDERING_INTENT_UNKNOWN;
+ transform->priv->pixel_format = CD_PIXEL_FORMAT_UNKNOWN;
+ transform->priv->srgb = cmsCreate_sRGBProfile ();
+}
+
+/**
+ * cd_transform_finalize:
+ */
+static void
+cd_transform_finalize (GObject *object)
+{
+ CdTransform *transform = CD_TRANSFORM (object);
+ CdTransformPrivate *priv = transform->priv;
+
+ cmsCloseProfile (transform->priv->srgb);
+ if (priv->input != NULL)
+ g_object_unref (priv->input);
+ if (priv->output != NULL)
+ g_object_unref (priv->output);
+ if (priv->abstract != NULL)
+ g_object_unref (priv->abstract);
+ if (priv->lcms_transform != NULL)
+ cmsDeleteTransform (priv->lcms_transform);
+
+ G_OBJECT_CLASS (cd_transform_parent_class)->finalize (object);
+}
+
+/**
+ * cd_transform_new:
+ *
+ * Creates a new #CdTransform object.
+ *
+ * Return value: a new CdTransform object.
+ *
+ * Since: 0.1.34
+ **/
+CdTransform *
+cd_transform_new (void)
+{
+ CdTransform *transform;
+ transform = g_object_new (CD_TYPE_TRANSFORM, NULL);
+ return CD_TRANSFORM (transform);
+}
diff --git a/lib/colord/cd-transform.h b/lib/colord/cd-transform.h
new file mode 100644
index 0000000..9d8d430
--- /dev/null
+++ b/lib/colord/cd-transform.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * This library 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 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if !defined (__COLORD_H_INSIDE__) && !defined (CD_COMPILATION)
+#error "Only <colord.h> can be included directly."
+#endif
+
+#ifndef __CD_TRANSFORM_H
+#define __CD_TRANSFORM_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "cd-enum.h"
+#include "cd-icc.h"
+
+G_BEGIN_DECLS
+
+#define CD_TYPE_TRANSFORM (cd_transform_get_type ())
+#define CD_TRANSFORM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CD_TYPE_TRANSFORM, CdTransform))
+#define CD_TRANSFORM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CD_TYPE_TRANSFORM, CdTransformClass))
+#define CD_IS_TRANSFORM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CD_TYPE_TRANSFORM))
+#define CD_IS_TRANSFORM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CD_TYPE_TRANSFORM))
+#define CD_TRANSFORM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CD_TYPE_TRANSFORM, CdTransformClass))
+#define CD_TRANSFORM_ERROR (cd_transform_error_quark ())
+#define CD_TRANSFORM_TYPE_ERROR (cd_transform_error_get_type ())
+
+typedef struct _CdTransformPrivate CdTransformPrivate;
+
+typedef struct
+{
+ GObject parent;
+ CdTransformPrivate *priv;
+} CdTransform;
+
+/**
+ * CdTransformError:
+ * @CD_TRANSFORM_ERROR_FAILED_TO_SETUP_TRANSFORM: Failed to setup transform
+ * @CD_TRANSFORM_ERROR_INVALID_COLORSPACE: Invalid colorspace
+ *
+ * The transform error code.
+ *
+ * Since: 0.1.34
+ **/
+typedef enum {
+ CD_TRANSFORM_ERROR_FAILED_TO_SETUP_TRANSFORM,
+ CD_TRANSFORM_ERROR_INVALID_COLORSPACE,
+ CD_TRANSFORM_ERROR_LAST
+} CdTransformError;
+
+typedef struct
+{
+ GObjectClass parent_class;
+ /*< private >*/
+ /* Padding for future expansion */
+ void (*_cd_transform_reserved1) (void);
+ void (*_cd_transform_reserved2) (void);
+ void (*_cd_transform_reserved3) (void);
+ void (*_cd_transform_reserved4) (void);
+ void (*_cd_transform_reserved5) (void);
+ void (*_cd_transform_reserved6) (void);
+ void (*_cd_transform_reserved7) (void);
+ void (*_cd_transform_reserved8) (void);
+} CdTransformClass;
+
+GType cd_transform_get_type (void);
+GQuark cd_transform_error_quark (void);
+CdTransform *cd_transform_new (void);
+
+void cd_transform_set_input (CdTransform *transform,
+ CdIcc *icc);
+CdIcc *cd_transform_get_input (CdTransform *transform);
+void cd_transform_set_output (CdTransform *transform,
+ CdIcc *icc);
+CdIcc *cd_transform_get_output (CdTransform *transform);
+void cd_transform_set_abstract (CdTransform *transform,
+ CdIcc *icc);
+CdIcc *cd_transform_get_abstract (CdTransform *transform);
+void cd_transform_set_format (CdTransform *transform,
+ CdPixelFormat pixel_format);
+CdPixelFormat cd_transform_get_format (CdTransform *transform);
+void cd_transform_set_intent (CdTransform *transform,
+ CdRenderingIntent rendering_intent);
+CdRenderingIntent cd_transform_get_intent (CdTransform *transform);
+gboolean cd_transform_process (CdTransform *transform,
+ gpointer data_in,
+ gpointer data_out,
+ guint width,
+ guint height,
+ guint rowstride,
+ GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __CD_TRANSFORM_H */
+