[gnome-color-manager: 2/3] Split out the lcms1 functionality into a derived class to allow us to add lcms2 at either compile ti



commit 57e3ca7feffab0c929b29d6971e336e6003eebcc
Author: Richard Hughes <richard hughsie com>
Date:   Tue Dec 22 22:22:48 2009 +0000

    Split out the lcms1 functionality into a derived class to allow us to add lcms2 at either compile time or at run time

 src/Makefile.am         |    3 +
 src/gcm-cie-widget.c    |    2 +-
 src/gcm-dbus.c          |    4 +-
 src/gcm-dump-profile.c  |    2 +-
 src/gcm-import.c        |    2 +-
 src/gcm-inspect.c       |    2 +-
 src/gcm-prefs.c         |    6 +-
 src/gcm-profile-lcms1.c | 1010 ++++++++++++++++++++++++++++++++++++++++
 src/gcm-profile-lcms1.h |   66 +++
 src/gcm-profile.c       | 1189 ++++++-----------------------------------------
 src/gcm-profile.h       |   12 +-
 src/gcm-trc-widget.c    |    2 +-
 src/gcm-utils.c         |    2 +-
 13 files changed, 1245 insertions(+), 1057 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index b7ea6a7..554874d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -45,6 +45,8 @@ libgcmshared_a_SOURCES =				\
 	gcm-trc-widget.h				\
 	gcm-gamma-widget.c				\
 	gcm-gamma-widget.h				\
+	gcm-profile-lcms1.c				\
+	gcm-profile-lcms1.h				\
 	gcm-profile.c					\
 	gcm-profile.h
 
@@ -204,6 +206,7 @@ gcm_self_test_SOURCES =					\
 	gcm-utils.c					\
 	gcm-device.c					\
 	gcm-profile.c					\
+	gcm-profile-lcms1.c				\
 	gcm-brightness.c				\
 	gcm-clut.c					\
 	gcm-dmi.c					\
diff --git a/src/gcm-cie-widget.c b/src/gcm-cie-widget.c
index 91a1589..2373a98 100644
--- a/src/gcm-cie-widget.c
+++ b/src/gcm-cie-widget.c
@@ -1215,7 +1215,7 @@ gcm_cie_widget_test (EggTest *test)
 	filename_profile = egg_test_get_data_file ("bluish.icc");
 	egg_test_assert (test, (filename_profile != NULL));
 
-	profile = gcm_profile_new ();
+	profile = gcm_profile_default_new ();
 	gcm_profile_parse (profile, filename_profile, NULL);
 	g_object_get (profile,
 		      "white-point", &white,
diff --git a/src/gcm-dbus.c b/src/gcm-dbus.c
index 04015de..3929c50 100644
--- a/src/gcm-dbus.c
+++ b/src/gcm-dbus.c
@@ -188,7 +188,7 @@ gcm_dbus_get_profiles_for_device_internal (GcmDbus *dbus, const gchar *sysfs_pat
 				      NULL);
 
 			/* open and parse filename */
-			profile = gcm_profile_new ();
+			profile = gcm_profile_default_new ();
 			ret = gcm_profile_parse (profile, filename, &error);
 			if (!ret) {
 				egg_warning ("failed to parse %s: %s", filename, error->message);
@@ -237,7 +237,7 @@ gcm_dbus_get_profiles_for_type_internal (GcmDbus *dbus, GcmDeviceType type)
 		filename = g_ptr_array_index (array_devices, i);
 
 		/* open and parse filename */
-		profile = gcm_profile_new ();
+		profile = gcm_profile_default_new ();
 		ret = gcm_profile_parse (profile, filename, &error);
 		if (!ret) {
 			egg_warning ("failed to parse %s: %s", filename, error->message);
diff --git a/src/gcm-dump-profile.c b/src/gcm-dump-profile.c
index e6bf989..39bf4b2 100644
--- a/src/gcm-dump-profile.c
+++ b/src/gcm-dump-profile.c
@@ -48,7 +48,7 @@ gcm_dump_profile_filename (const gchar *filename)
 	gchar *datetime = NULL;
 
 	/* parse profile */
-	profile = gcm_profile_new ();
+	profile = gcm_profile_default_new ();
 	ret = gcm_profile_parse (profile, filename, &error);
 	if (!ret) {
 		egg_warning ("failed to parse: %s", error->message);
diff --git a/src/gcm-import.c b/src/gcm-import.c
index 3ec9c2e..5bd469e 100644
--- a/src/gcm-import.c
+++ b/src/gcm-import.c
@@ -91,7 +91,7 @@ main (int argc, char **argv)
 	}
 
 	/* load profile */
-	profile = gcm_profile_new ();
+	profile = gcm_profile_default_new ();
 	ret = gcm_profile_parse (profile, files[0], &error);
 	if (!ret) {
 		/* TRANSLATORS: could not read file */
diff --git a/src/gcm-inspect.c b/src/gcm-inspect.c
index 183ccbb..4619afc 100644
--- a/src/gcm-inspect.c
+++ b/src/gcm-inspect.c
@@ -46,7 +46,7 @@ gcm_inspect_print_data_info (const gchar *title, const guint8 *data, gsize lengt
 	gboolean ret;
 
 	/* parse the data */
-	profile = gcm_profile_new ();
+	profile = gcm_profile_default_new ();
 	ret = gcm_profile_parse_data (profile, data, length, &error);
 	if (!ret) {
 		egg_warning ("failed to parse data: %s", error->message);
diff --git a/src/gcm-prefs.c b/src/gcm-prefs.c
index dbb047a..1b7fd9b 100644
--- a/src/gcm-prefs.c
+++ b/src/gcm-prefs.c
@@ -694,7 +694,7 @@ gcm_prefs_profile_import_cb (GtkWidget *widget, gpointer data)
 	}
 
 	/* add new profile */
-	profile = gcm_profile_new ();
+	profile = gcm_profile_default_new ();
 
 	/* parse the new file */
 	ret = gcm_profile_parse (profile, destination, &error);
@@ -871,7 +871,7 @@ gcm_prefs_calibrate_cb (GtkWidget *widget, gpointer data)
 		}
 
 		/* create a new instance */
-		profile = gcm_profile_new ();
+		profile = gcm_profile_default_new ();
 
 		/* parse the new file */
 		ret = gcm_profile_parse (profile, destination, &error);
@@ -1695,7 +1695,7 @@ gcm_prefs_add_profiles (GtkWidget *widget)
 		filename = g_ptr_array_index (filename_array, i);
 
 		/* parse the profile name */
-		profile = gcm_profile_new ();
+		profile = gcm_profile_default_new ();
 		ret = gcm_profile_parse (profile, filename, &error);
 		if (!ret) {
 			egg_warning ("failed to add profile: %s", error->message);
diff --git a/src/gcm-profile-lcms1.c b/src/gcm-profile-lcms1.c
new file mode 100644
index 0000000..4f94794
--- /dev/null
+++ b/src/gcm-profile-lcms1.c
@@ -0,0 +1,1010 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more profile_lcms1.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:gcm-profile-lcms1
+ * @short_description: A parser object that understands the ICC profile data format.
+ *
+ * This object is a simple parser for the ICC binary profile data. If only understands
+ * a subset of the ICC profile_lcms1, just enought to get some metadata and the LUT.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+#include <glib/gi18n.h>
+#include <math.h>
+#include <lcms.h>
+
+#include "egg-debug.h"
+
+#include "gcm-profile-lcms1.h"
+#include "gcm-utils.h"
+#include "gcm-xyz.h"
+
+static void     gcm_profile_lcms1_finalize	(GObject     *object);
+
+#define GCM_PROFILE_LCMS1_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GCM_TYPE_PROFILE_LCMS1, GcmProfileLcms1Private))
+
+#define GCM_NUMTAGS			0x80
+#define GCM_BODY			0x84
+
+#define GCM_TAG_ID			0x00
+#define GCM_TAG_OFFSET			0x04
+#define GCM_TAG_SIZE			0x08
+#define GCM_TAG_WIDTH			0x0c
+
+#define icSigVideoCartGammaTableTag	0x76636774
+#define icSigMachineLookUpTableTag	0x6d4c5554
+
+#define GCM_MLUT_RED			0x000
+#define GCM_MLUT_GREEN			0x200
+#define GCM_MLUT_BLUE			0x400
+
+#define GCM_DESC_RECORD_SIZE		0x08
+#define GCM_DESC_RECORD_TEXT		0x0c
+#define GCM_TEXT_RECORD_TEXT		0x08
+
+#define GCM_VCGT_ID			0x00
+#define GCM_VCGT_DUMMY			0x04
+#define GCM_VCGT_GAMMA_TYPE		0x08
+#define GCM_VCGT_GAMMA_DATA		0x0c
+
+#define GCM_VCGT_FORMULA_GAMMA_RED	0x00
+#define GCM_VCGT_FORMULA_MIN_RED	0x04
+#define GCM_VCGT_FORMULA_MAX_RED	0x08
+#define GCM_VCGT_FORMULA_GAMMA_GREEN	0x0c
+#define GCM_VCGT_FORMULA_MIN_GREEN	0x10
+#define GCM_VCGT_FORMULA_MAX_GREEN	0x14
+#define GCM_VCGT_FORMULA_GAMMA_BLUE	0x18
+#define GCM_VCGT_FORMULA_MIN_BLUE	0x1c
+#define GCM_VCGT_FORMULA_MAX_BLUE	0x20
+
+#define GCM_VCGT_TABLE_NUM_CHANNELS	0x00
+#define GCM_VCGT_TABLE_NUM_ENTRIES	0x02
+#define GCM_VCGT_TABLE_NUM_SIZE		0x04
+#define GCM_VCGT_TABLE_NUM_DATA		0x06
+
+/**
+ * GcmProfileLcms1Private:
+ *
+ * Private #GcmProfileLcms1 data
+ **/
+struct _GcmProfileLcms1Private
+{
+	gboolean			 loaded;
+	gboolean			 has_mlut;
+	gboolean			 has_vcgt_formula;
+	gboolean			 has_vcgt_table;
+	cmsHPROFILE			 lcms_profile;
+	GcmClutData			*vcgt_data;
+	guint				 vcgt_data_size;
+	GcmClutData			*mlut_data;
+	guint				 mlut_data_size;
+	gboolean			 adobe_gamma_workaround;
+};
+
+G_DEFINE_TYPE (GcmProfileLcms1, gcm_profile_lcms1, GCM_TYPE_PROFILE)
+
+/**
+ * gcm_parser_decode_32:
+ **/
+static guint
+gcm_parser_decode_32 (const guint8 *data)
+{
+	guint retval;
+	retval = (*(data + 0) << 0) + (*(data + 1) << 8) + (*(data + 2) << 16) + (*(data + 3) << 24);
+	return GUINT32_FROM_BE (retval);
+}
+
+/**
+ * gcm_parser_decode_16:
+ **/
+static guint
+gcm_parser_decode_16 (const guint8 *data)
+{
+	guint retval;
+	retval = (*(data + 0) << 0) + (*(data + 1) << 8);
+	return GUINT16_FROM_BE (retval);
+}
+
+/**
+ * gcm_parser_decode_8:
+ **/
+static guint
+gcm_parser_decode_8 (const guint8 *data)
+{
+	guint retval;
+	retval = (*data << 0);
+	return GUINT16_FROM_BE (retval);
+}
+
+/**
+ * gcm_parser_load_icc_mlut:
+ **/
+static gboolean
+gcm_parser_load_icc_mlut (GcmProfileLcms1 *profile_lcms1, const guint8 *data, guint size)
+{
+	gboolean ret = TRUE;
+	guint i;
+	GcmClutData *mlut_data;
+
+	/* just load in data into a fixed size LUT */
+	profile_lcms1->priv->mlut_data = g_new0 (GcmClutData, 256);
+	mlut_data = profile_lcms1->priv->mlut_data;
+
+	for (i=0; i<256; i++)
+		mlut_data[i].red = gcm_parser_decode_16 (data + GCM_MLUT_RED + i*2);
+	for (i=0; i<256; i++)
+		mlut_data[i].green = gcm_parser_decode_16 (data + GCM_MLUT_GREEN + i*2);
+	for (i=0; i<256; i++)
+		mlut_data[i].blue = gcm_parser_decode_16 (data + GCM_MLUT_BLUE + i*2);
+
+	/* save datatype */
+	profile_lcms1->priv->has_mlut = TRUE;
+	return ret;
+}
+
+/**
+ * gcm_parser_load_icc_vcgt_formula:
+ **/
+static gboolean
+gcm_parser_load_icc_vcgt_formula (GcmProfileLcms1 *profile_lcms1, const guint8 *data, guint size)
+{
+	gboolean ret = FALSE;
+	GcmClutData *vcgt_data;
+
+	egg_debug ("loading a formula encoded gamma table");
+
+	/* just load in data into a temporary array */
+	profile_lcms1->priv->vcgt_data = g_new0 (GcmClutData, 4);
+	vcgt_data = profile_lcms1->priv->vcgt_data;
+
+	/* read in block of data */
+	vcgt_data[0].red = gcm_parser_decode_32 (data + GCM_VCGT_FORMULA_GAMMA_RED);
+	vcgt_data[0].green = gcm_parser_decode_32 (data + GCM_VCGT_FORMULA_GAMMA_GREEN);
+	vcgt_data[0].blue = gcm_parser_decode_32 (data + GCM_VCGT_FORMULA_GAMMA_BLUE);
+
+	vcgt_data[1].red = gcm_parser_decode_32 (data + GCM_VCGT_FORMULA_MIN_RED);
+	vcgt_data[1].green = gcm_parser_decode_32 (data + GCM_VCGT_FORMULA_MIN_GREEN);
+	vcgt_data[1].blue = gcm_parser_decode_32 (data + GCM_VCGT_FORMULA_MIN_BLUE);
+
+	vcgt_data[2].red = gcm_parser_decode_32 (data + GCM_VCGT_FORMULA_MAX_RED);
+	vcgt_data[2].green = gcm_parser_decode_32 (data + GCM_VCGT_FORMULA_MAX_GREEN);
+	vcgt_data[2].blue = gcm_parser_decode_32 (data + GCM_VCGT_FORMULA_MAX_BLUE);
+
+	/* check if valid */
+	if (vcgt_data[0].red / 65536.0 > 5.0 || vcgt_data[0].green / 65536.0 > 5.0 || vcgt_data[0].blue / 65536.0 > 5.0) {
+		egg_warning ("Gamma values out of range: [R:%u G:%u B:%u]", vcgt_data[0].red, vcgt_data[0].green, vcgt_data[0].blue);
+		goto out;
+	}
+	if (vcgt_data[1].red / 65536.0 >= 1.0 || vcgt_data[1].green / 65536.0 >= 1.0 || vcgt_data[1].blue / 65536.0 >= 1.0) {
+		egg_warning ("Gamma min limit out of range: [R:%u G:%u B:%u]", vcgt_data[1].red, vcgt_data[1].green, vcgt_data[1].blue);
+		goto out;
+	}
+	if (vcgt_data[2].red / 65536.0 > 1.0 || vcgt_data[2].green / 65536.0 > 1.0 || vcgt_data[2].blue / 65536.0 > 1.0) {
+		egg_warning ("Gamma max limit out of range: [R:%u G:%u B:%u]", vcgt_data[2].red, vcgt_data[2].green, vcgt_data[2].blue);
+		goto out;
+	}
+
+	/* save datatype */
+	profile_lcms1->priv->has_vcgt_formula = TRUE;
+	profile_lcms1->priv->vcgt_data_size = 3;
+	ret = TRUE;
+out:
+	return ret;
+}
+
+/**
+ * gcm_parser_load_icc_vcgt_table:
+ **/
+static gboolean
+gcm_parser_load_icc_vcgt_table (GcmProfileLcms1 *profile_lcms1, const guint8 *data, guint size)
+{
+	gboolean ret = TRUE;
+	guint num_channels = 0;
+	guint num_entries = 0;
+	guint entry_size = 0;
+	guint i;
+	GcmClutData *vcgt_data;
+
+	egg_debug ("loading a table encoded gamma table");
+
+	num_channels = gcm_parser_decode_16 (data + GCM_VCGT_TABLE_NUM_CHANNELS);
+	num_entries = gcm_parser_decode_16 (data + GCM_VCGT_TABLE_NUM_ENTRIES);
+	entry_size = gcm_parser_decode_16 (data + GCM_VCGT_TABLE_NUM_SIZE);
+
+	/* work-around for AdobeGamma-ProfileLcms1s (taken from xcalib) */
+	if (profile_lcms1->priv->adobe_gamma_workaround) {
+		egg_debug ("Working around AdobeGamma profile_lcms1");
+		entry_size = 2;
+		num_entries = 256;
+		num_channels = 3;
+	}
+
+	egg_debug ("channels: %u", num_channels);
+	egg_debug ("entry size: %ubits", entry_size * 8);
+	egg_debug ("entries/channel: %u", num_entries);
+
+	/* only able to parse RGB data */
+	if (num_channels != 3) {
+		egg_warning ("cannot parse non RGB entries");
+		ret = FALSE;
+		goto out;
+	}
+
+	/* bigger than will fit in 16 bits? */
+	if (entry_size > 2) {
+		egg_warning ("cannot parse large entries");
+		ret = FALSE;
+		goto out;
+	}
+
+	/* allocate ramp, plus one entry for extrapolation */
+	profile_lcms1->priv->vcgt_data = g_new0 (GcmClutData, num_entries + 1);
+	vcgt_data = profile_lcms1->priv->vcgt_data;
+
+	if (entry_size == 1) {
+		for (i=0; i<num_entries; i++)
+			vcgt_data[i].red = gcm_parser_decode_8 (data + GCM_VCGT_TABLE_NUM_DATA + (num_entries * 0) + i);
+		for (i=0; i<num_entries; i++)
+			vcgt_data[i].green = gcm_parser_decode_8 (data + GCM_VCGT_TABLE_NUM_DATA + (num_entries * 1) + i);
+		for (i=0; i<num_entries; i++)
+			vcgt_data[i].blue = gcm_parser_decode_8 (data + GCM_VCGT_TABLE_NUM_DATA + (num_entries * 2) + i);
+	} else {
+		for (i=0; i<num_entries; i++)
+			vcgt_data[i].red = gcm_parser_decode_16 (data + GCM_VCGT_TABLE_NUM_DATA + (num_entries * 0) + (i*2));
+		for (i=0; i<num_entries; i++)
+			vcgt_data[i].green = gcm_parser_decode_16 (data + GCM_VCGT_TABLE_NUM_DATA + (num_entries * 2) + (i*2));
+		for (i=0; i<num_entries; i++)
+			vcgt_data[i].blue = gcm_parser_decode_16 (data + GCM_VCGT_TABLE_NUM_DATA + (num_entries * 4) + (i*2));
+	}
+
+	/* save datatype */
+	profile_lcms1->priv->has_vcgt_table = TRUE;
+	profile_lcms1->priv->vcgt_data_size = num_entries;
+out:
+	return ret;
+}
+
+/**
+ * gcm_parser_load_icc_vcgt:
+ **/
+static gboolean
+gcm_parser_load_icc_vcgt (GcmProfileLcms1 *profile_lcms1, const guint8 *data, guint size)
+{
+	gboolean ret = FALSE;
+	guint tag_id;
+	guint gamma_type;
+
+	/* check we have a VCGT block */
+	tag_id = gcm_parser_decode_32 (data);
+	if (tag_id != icSigVideoCartGammaTableTag) {
+		egg_warning ("invalid content of table vcgt, starting with %x", tag_id);
+		goto out;
+	}
+
+	/* check what type of gamma encoding we have */
+	gamma_type = gcm_parser_decode_32 (data + GCM_VCGT_GAMMA_TYPE);
+	if (gamma_type == 0) {
+		ret = gcm_parser_load_icc_vcgt_table (profile_lcms1, data + GCM_VCGT_GAMMA_DATA, size);
+		goto out;
+	}
+	if (gamma_type == 1) {
+		ret = gcm_parser_load_icc_vcgt_formula (profile_lcms1, data + GCM_VCGT_GAMMA_DATA, size);
+		goto out;
+	}
+
+	/* we didn't understand the encoding */
+	egg_warning ("gamma type encoding not recognised");
+out:
+	return ret;
+}
+
+/**
+ * gcm_profile_lcms1_utf16be_to_locale:
+ *
+ * Convert ICC encoded UTF-16BE into a string the user can understand
+ **/
+static gchar *
+gcm_profile_lcms1_utf16be_to_locale (const guint8 *text, guint size)
+{
+	gsize items_written;
+	gchar *text_utf8 = NULL;
+	gchar *text_locale = NULL;
+	GError *error = NULL;
+
+	/* convert from ICC text encoding to UTF-8 */
+	text_utf8 = g_convert ((const gchar*)text, size, "UTF-8", "UTF-16BE", NULL, &items_written, &error);
+	if (text_utf8 == NULL) {
+		egg_warning ("failed to convert to UTF-8: %s", error->message);
+		g_error_free (error);
+		goto out;
+	}
+
+	/* convert from UTF-8 to the users locale*/
+	text_locale = g_locale_from_utf8 (text_utf8, items_written, NULL, NULL, &error);
+	if (text_locale == NULL) {
+		egg_warning ("failed to convert to locale: %s", error->message);
+		g_error_free (error);
+		goto out;
+	}
+out:
+	g_free (text_utf8);
+	return text_locale;
+}
+
+/**
+ * gcm_profile_lcms1_parse_multi_localized_unicode:
+ **/
+static gchar *
+gcm_profile_lcms1_parse_multi_localized_unicode (GcmProfileLcms1 *profile_lcms1, const guint8 *data, guint size)
+{
+	guint i;
+	gchar *text = NULL;
+	guint record_size;
+	guint names_size;
+	guint len;
+	guint offset_name;
+	guint32 type;
+
+	/* get type */
+	type = gcm_parser_decode_32 (data);
+
+	/* check we are not a localized tag */
+	if (type == icSigTextDescriptionType) {
+		record_size = gcm_parser_decode_32 (data + GCM_DESC_RECORD_SIZE);
+		text = g_strndup ((const gchar*)&data[GCM_DESC_RECORD_TEXT], record_size);
+		goto out;
+	}
+
+	/* check we are not a localized tag */
+	if (type == icSigTextType) {
+		text = g_strdup ((const gchar*)&data[GCM_TEXT_RECORD_TEXT]);
+		goto out;
+	}
+
+	/* check we are not a localized tag */
+	if (type == icSigMultiLocalizedUnicodeType) {
+		names_size = gcm_parser_decode_32 (data + 8);
+		if (names_size != 1) {
+			/* there is more than one language encoded */
+			egg_warning ("more than one item of data in MLUC (names size: %i), using first one", names_size);
+		}
+		record_size = gcm_parser_decode_32 (data + 12);
+		len = gcm_parser_decode_32 (data + 20);
+		offset_name = gcm_parser_decode_32 (data + 24);
+		text = gcm_profile_lcms1_utf16be_to_locale (data + offset_name, len);
+		goto out;
+	}
+
+	/* an unrecognised tag */
+	for (i=0x0; i<0x1c; i++) {
+		egg_warning ("unrecognised text tag");
+		if (data[i] >= 'A' && data[i] <= 'z')
+			egg_debug ("%i\t%c (%i)", i, data[i], data[i]);
+		else
+			egg_debug ("%i\t  (%i)", i, data[i]);
+	}
+out:
+	return text;
+}
+
+/**
+ * gcm_profile_lcms1_parse_data:
+ **/
+static gboolean
+gcm_profile_lcms1_parse_data (GcmProfile *profile, const guint8 *data, gsize length, GError **error)
+{
+	gboolean ret = FALSE;
+	guint num_tags;
+	guint i;
+	guint tag_id;
+	guint offset;
+	guint tag_size;
+	guint tag_offset;
+	icProfileClassSignature profile_class;
+	icColorSpaceSignature color_space;
+	GcmProfileColorspace colorspace;
+	GcmProfileType profile_type;
+	cmsCIEXYZ cie_xyz;
+	cmsCIEXYZTRIPLE cie_illum;
+	struct tm created;
+	cmsHPROFILE xyz_profile_lcms1;
+	cmsHTRANSFORM transform;
+	gchar *text;
+	GcmXyz *xyz;
+	GcmProfileLcms1 *profile_lcms1 = GCM_PROFILE_LCMS1 (profile);
+	GcmProfileLcms1Private *priv = profile_lcms1->priv;
+
+	g_return_val_if_fail (GCM_IS_PROFILE_LCMS1 (profile_lcms1), FALSE);
+	g_return_val_if_fail (data != NULL, FALSE);
+	g_return_val_if_fail (priv->loaded == FALSE, FALSE);
+
+	priv->loaded = TRUE;
+
+	/* load profile into lcms */
+	priv->lcms_profile = cmsOpenProfileFromMem ((LPVOID)data, length);
+	if (priv->lcms_profile == NULL) {
+		*error = g_error_new (1, 0, "failed to load: not an ICC profile_lcms1");
+		goto out;
+	}
+
+	/* get white point */
+	ret = cmsTakeMediaWhitePoint (&cie_xyz, priv->lcms_profile);
+	if (ret) {
+		xyz = gcm_xyz_new ();
+		g_object_set (xyz,
+			      "cie-x", cie_xyz.X,
+			      "cie-y", cie_xyz.Y,
+			      "cie-z", cie_xyz.Z,
+			      NULL);
+		g_object_set (profile,
+			      "white-point", xyz,
+			      NULL);
+		g_object_unref (xyz);
+	} else {
+		egg_warning ("failed to get white point");
+	}
+
+	/* get black point */
+	ret = cmsTakeMediaBlackPoint (&cie_xyz, priv->lcms_profile);
+	if (ret) {
+		xyz = gcm_xyz_new ();
+		g_object_set (xyz,
+			      "cie-x", cie_xyz.X,
+			      "cie-y", cie_xyz.Y,
+			      "cie-z", cie_xyz.Z,
+			      NULL);
+		g_object_set (profile,
+			      "black-point", xyz,
+			      NULL);
+		g_object_unref (xyz);
+	} else {
+		egg_warning ("failed to get black point");
+	}
+
+	/* get the profile_lcms1 type */
+	profile_class = cmsGetDeviceClass (priv->lcms_profile);
+	switch (profile_class) {
+	case icSigInputClass:
+		profile_type = GCM_PROFILE_TYPE_INPUT_DEVICE;
+		break;
+	case icSigDisplayClass:
+		profile_type = GCM_PROFILE_TYPE_DISPLAY_DEVICE;
+		break;
+	case icSigOutputClass:
+		profile_type = GCM_PROFILE_TYPE_OUTPUT_DEVICE;
+		break;
+	case icSigLinkClass:
+		profile_type = GCM_PROFILE_TYPE_DEVICELINK;
+		break;
+	case icSigColorSpaceClass:
+		profile_type = GCM_PROFILE_TYPE_COLORSPACE_CONVERSION;
+		break;
+	case icSigAbstractClass:
+		profile_type = GCM_PROFILE_TYPE_ABSTRACT;
+		break;
+	case icSigNamedColorClass:
+		profile_type = GCM_PROFILE_TYPE_NAMED_COLOR;
+		break;
+	default:
+		profile_type = GCM_PROFILE_TYPE_UNKNOWN;
+	}
+	g_object_set (profile,
+		      "type", profile_type,
+		      NULL);
+
+	/* get colorspace */
+	color_space = cmsGetColorSpace (priv->lcms_profile);
+	switch (color_space) {
+	case icSigXYZData:
+		colorspace = GCM_PROFILE_COLORSPACE_XYZ;
+		break;
+	case icSigLabData:
+		colorspace = GCM_PROFILE_COLORSPACE_LAB;
+		break;
+	case icSigLuvData:
+		colorspace = GCM_PROFILE_COLORSPACE_LUV;
+		break;
+	case icSigYCbCrData:
+		colorspace = GCM_PROFILE_COLORSPACE_YCBCR;
+		break;
+	case icSigYxyData:
+		colorspace = GCM_PROFILE_COLORSPACE_YXY;
+		break;
+	case icSigRgbData:
+		colorspace = GCM_PROFILE_COLORSPACE_RGB;
+		break;
+	case icSigGrayData:
+		colorspace = GCM_PROFILE_COLORSPACE_GRAY;
+		break;
+	case icSigHsvData:
+		colorspace = GCM_PROFILE_COLORSPACE_HSV;
+		break;
+	case icSigCmykData:
+		colorspace = GCM_PROFILE_COLORSPACE_CMYK;
+		break;
+	case icSigCmyData:
+		colorspace = GCM_PROFILE_COLORSPACE_CMY;
+		break;
+	default:
+		colorspace = GCM_PROFILE_COLORSPACE_UNKNOWN;
+	}
+	g_object_set (profile,
+		      "colorspace", colorspace,
+		      NULL);
+
+	/* get primary illuminants */
+	ret = cmsTakeColorants (&cie_illum, priv->lcms_profile);
+
+	/* geting the illuminants failed, try running it through the profile_lcms1 */
+	if (!ret && color_space == icSigRgbData) {
+		gdouble rgb_values[3];
+
+		/* create a transform from profile_lcms1 to XYZ */
+		xyz_profile_lcms1 = cmsCreateXYZProfile ();
+		transform = cmsCreateTransform (priv->lcms_profile, TYPE_RGB_DBL, xyz_profile_lcms1, TYPE_XYZ_DBL, INTENT_PERCEPTUAL, 0);
+		if (transform != NULL) {
+
+			/* red */
+			rgb_values[0] = 1.0;
+			rgb_values[1] = 0.0;
+			rgb_values[2] = 0.0;
+			cmsDoTransform (transform, rgb_values, &cie_illum.Red, 1);
+
+			/* green */
+			rgb_values[0] = 0.0;
+			rgb_values[1] = 1.0;
+			rgb_values[2] = 0.0;
+			cmsDoTransform (transform, rgb_values, &cie_illum.Green, 1);
+
+			/* blue */
+			rgb_values[0] = 0.0;
+			rgb_values[1] = 0.0;
+			rgb_values[2] = 1.0;
+			cmsDoTransform (transform, rgb_values, &cie_illum.Blue, 1);
+
+			/* we're done */
+			cmsDeleteTransform (transform);
+			ret = TRUE;
+		}
+
+		/* no more need for the output profile_lcms1 */
+		cmsCloseProfile (xyz_profile_lcms1);
+	}
+
+	/* we've got valid values */
+	if (ret) {
+		/* red */
+		xyz = gcm_xyz_new ();
+		g_object_set (xyz,
+			      "cie-x", cie_illum.Red.X,
+			      "cie-y", cie_illum.Red.Y,
+			      "cie-z", cie_illum.Red.Z,
+			      NULL);
+		g_object_set (profile,
+			      "luminance_red", xyz,
+			      NULL);
+		g_object_unref (xyz);
+
+		/* green */
+		xyz = gcm_xyz_new ();
+		g_object_set (xyz,
+			      "cie-x", cie_illum.Green.X,
+			      "cie-y", cie_illum.Green.Y,
+			      "cie-z", cie_illum.Green.Z,
+			      NULL);
+		g_object_set (profile,
+			      "luminance-green", xyz,
+			      NULL);
+		g_object_unref (xyz);
+
+		/* blue */
+		xyz = gcm_xyz_new ();
+		g_object_set (xyz,
+			      "cie-x", cie_illum.Blue.X,
+			      "cie-y", cie_illum.Blue.Y,
+			      "cie-z", cie_illum.Blue.Z,
+			      NULL);
+		g_object_set (profile,
+			      "luminance-blue", xyz,
+			      NULL);
+		g_object_unref (xyz);
+	} else {
+		egg_debug ("failed to get luminance values");
+	}
+
+	/* get the profile_lcms1 created time and date */
+	ret = cmsTakeCreationDateTime (&created, priv->lcms_profile);
+	if (ret) {
+		text = gcm_utils_format_date_time (&created);
+		g_object_set (profile,
+			      "datetime", text,
+			      NULL);
+		g_free (text);
+	}
+
+	/* get the number of tags in the file */
+	num_tags = gcm_parser_decode_32 (data + GCM_NUMTAGS);
+	egg_debug ("number of tags: %i", num_tags);
+
+	for (i=0; i<num_tags; i++) {
+		offset = GCM_TAG_WIDTH * i;
+		tag_id = gcm_parser_decode_32 (data + GCM_BODY + offset + GCM_TAG_ID);
+		tag_offset = gcm_parser_decode_32 (data + GCM_BODY + offset + GCM_TAG_OFFSET);
+		tag_size = gcm_parser_decode_32 (data + GCM_BODY + offset + GCM_TAG_SIZE);
+
+		/* print tag */
+		egg_debug ("tag %x is present at 0x%x with size %u", tag_id, tag_offset, tag_size);
+
+		if (tag_id == icSigProfileDescriptionTag) {
+			text = gcm_profile_lcms1_parse_multi_localized_unicode (profile_lcms1, data + tag_offset, tag_size);
+			g_object_set (profile, "description", text, NULL);
+			g_free (text);
+		}
+		if (tag_id == icSigCopyrightTag) {
+			text = gcm_profile_lcms1_parse_multi_localized_unicode (profile_lcms1, data + tag_offset, tag_size);
+			g_object_set (profile, "copyright", text, NULL);
+			g_free (text);
+		}
+		if (tag_id == icSigDeviceMfgDescTag) {
+			text = gcm_profile_lcms1_parse_multi_localized_unicode (profile_lcms1, data + tag_offset, tag_size);
+			g_object_set (profile, "manufacturer", text, NULL);
+			g_free (text);
+		}
+		if (tag_id == icSigDeviceModelDescTag) {
+			text = gcm_profile_lcms1_parse_multi_localized_unicode (profile_lcms1, data + tag_offset, tag_size);
+			g_object_set (profile, "model", text, NULL);
+			g_free (text);
+		}
+		if (tag_id == icSigMachineLookUpTableTag) {
+			egg_debug ("found MLUT which is a fixed size block");
+			ret = gcm_parser_load_icc_mlut (profile_lcms1, data + tag_offset, tag_size);
+			if (!ret) {
+				*error = g_error_new (1, 0, "failed to load mlut");
+				goto out;
+			}
+		}
+		if (tag_id == icSigVideoCartGammaTableTag) {
+			egg_debug ("found VCGT");
+			if (tag_size == 1584)
+				priv->adobe_gamma_workaround = TRUE;
+			ret = gcm_parser_load_icc_vcgt (profile_lcms1, data + tag_offset, tag_size);
+			if (!ret) {
+				*error = g_error_new (1, 0, "failed to load vcgt");
+				goto out;
+			}
+		}
+	}
+
+	/* success */
+	ret = TRUE;
+
+	egg_debug ("Has MLUT:         %s", priv->has_mlut ? "YES" : "NO");
+	egg_debug ("Has VCGT formula: %s", priv->has_vcgt_formula ? "YES" : "NO");
+	egg_debug ("Has VCGT table:   %s", priv->has_vcgt_table ? "YES" : "NO");
+out:
+	return ret;
+}
+
+/**
+ * gcm_profile_lcms1_generate_vcgt:
+ *
+ * Free with g_object_unref();
+ **/
+static GcmClut *
+gcm_profile_lcms1_generate_vcgt (GcmProfile *profile, guint size)
+{
+	guint i;
+	guint ratio;
+	GcmClutData *tmp;
+	GcmClutData *vcgt_data;
+	GcmClutData *mlut_data;
+	gfloat gamma_red, min_red, max_red;
+	gfloat gamma_green, min_green, max_green;
+	gfloat gamma_blue, min_blue, max_blue;
+	guint num_entries;
+	GcmClut *clut = NULL;
+	GPtrArray *array = NULL;
+	gfloat inverse_ratio;
+	guint idx;
+	gfloat frac;
+	GcmProfileLcms1 *profile_lcms1 = GCM_PROFILE_LCMS1 (profile);
+
+	g_return_val_if_fail (GCM_IS_PROFILE_LCMS1 (profile_lcms1), NULL);
+	g_return_val_if_fail (size != 0, FALSE);
+
+	/* reduce dereferences */
+	vcgt_data = profile_lcms1->priv->vcgt_data;
+	mlut_data = profile_lcms1->priv->mlut_data;
+
+	if (profile_lcms1->priv->has_vcgt_table) {
+
+		/* create array */
+		array = g_ptr_array_new_with_free_func (g_free);
+
+		/* simply subsample if the LUT is smaller than the number of entries in the file */
+		num_entries = profile_lcms1->priv->vcgt_data_size;
+		if (num_entries >= size) {
+			ratio = (guint) (num_entries / size);
+			for (i=0; i<size; i++) {
+				/* add a point */
+				tmp = g_new0 (GcmClutData, 1);
+				tmp->red = vcgt_data[ratio*i].red;
+				tmp->green = vcgt_data[ratio*i].green;
+				tmp->blue = vcgt_data[ratio*i].blue;
+				g_ptr_array_add (array, tmp);
+			}
+			goto out;
+		}
+
+		/* LUT is bigger than number of entries, so interpolate */
+		inverse_ratio = (gfloat) num_entries / size;
+		vcgt_data[num_entries].red = 0xffff;
+		vcgt_data[num_entries].green = 0xffff;
+		vcgt_data[num_entries].blue = 0xffff;
+
+		/* interpolate */
+		for (i=0; i<size; i++) {
+			idx = floor(i*inverse_ratio);
+			frac = (i*inverse_ratio) - idx;
+			tmp = g_new0 (GcmClutData, 1);
+			tmp->red = vcgt_data[idx].red * (1.0f-frac) + vcgt_data[idx + 1].red * frac;
+			tmp->green = vcgt_data[idx].green * (1.0f-frac) + vcgt_data[idx + 1].green * frac;
+			tmp->blue = vcgt_data[idx].blue * (1.0f-frac) + vcgt_data[idx + 1].blue * frac;
+			g_ptr_array_add (array, tmp);
+		}
+		goto out;
+	}
+
+	if (profile_lcms1->priv->has_vcgt_formula) {
+
+		/* create array */
+		array = g_ptr_array_new_with_free_func (g_free);
+
+		gamma_red = (gfloat) vcgt_data[0].red / 65536.0;
+		gamma_green = (gfloat) vcgt_data[0].green / 65536.0;
+		gamma_blue = (gfloat) vcgt_data[0].blue / 65536.0;
+		min_red = (gfloat) vcgt_data[1].red / 65536.0;
+		min_green = (gfloat) vcgt_data[1].green / 65536.0;
+		min_blue = (gfloat) vcgt_data[1].blue / 65536.0;
+		max_red = (gfloat) vcgt_data[2].red / 65536.0;
+		max_green = (gfloat) vcgt_data[2].green / 65536.0;
+		max_blue = (gfloat) vcgt_data[2].blue / 65536.0;
+
+		/* create mapping of desired size */
+		for (i=0; i<size; i++) {
+			/* add a point */
+			tmp = g_new0 (GcmClutData, 1);
+			tmp->red = 65536.0 * ((gdouble) pow ((gdouble) i / (gdouble) size, gamma_red) * (max_red - min_red) + min_red);
+			tmp->green = 65536.0 * ((gdouble) pow ((gdouble) i / (gdouble) size, gamma_green) * (max_green - min_green) + min_green);
+			tmp->blue = 65536.0 * ((gdouble) pow ((gdouble) i / (gdouble) size, gamma_blue) * (max_blue - min_blue) + min_blue);
+			g_ptr_array_add (array, tmp);
+		}
+		goto out;
+	}
+
+	if (profile_lcms1->priv->has_mlut) {
+
+		/* create array */
+		array = g_ptr_array_new_with_free_func (g_free);
+
+		/* roughly interpolate table */
+		ratio = (guint) (256 / (size));
+		for (i=0; i<size; i++) {
+			/* add a point */
+			tmp = g_new0 (GcmClutData, 1);
+			tmp->red = mlut_data[ratio*i].red;
+			tmp->green = mlut_data[ratio*i].green;
+			tmp->blue = mlut_data[ratio*i].blue;
+			g_ptr_array_add (array, tmp);
+		}
+		goto out;
+	}
+
+	/* bugger */
+	egg_debug ("no LUT to generate");
+out:
+	if (array != NULL) {
+		/* create new output array */
+		clut = gcm_clut_new ();
+		gcm_clut_set_source_array (clut, array);
+		g_ptr_array_unref (array);
+	}
+	return clut;
+}
+
+/**
+ * gcm_profile_lcms1_generate_curve:
+ *
+ * Free with g_object_unref();
+ **/
+static GcmClut *
+gcm_profile_lcms1_generate_curve (GcmProfile *profile, guint size)
+{
+	GcmClut *clut = NULL;
+	gdouble *values_in = NULL;
+	gdouble *values_out = NULL;
+	guint i;
+	GcmClutData *data;
+	GPtrArray *array = NULL;
+	gfloat divamount;
+	gfloat divadd;
+	guint component_width;
+	cmsHPROFILE srgb_profile_lcms1 = NULL;
+	cmsHTRANSFORM transform = NULL;
+	guint type;
+	GcmProfileColorspace colorspace;
+	GcmProfileLcms1 *profile_lcms1 = GCM_PROFILE_LCMS1 (profile);
+	GcmProfileLcms1Private *priv = profile_lcms1->priv;
+
+	/* get data */
+	g_object_get (profile,
+		      "colorspace", &colorspace,
+		      NULL);
+
+	/* run through the profile_lcms1 */
+	if (colorspace == GCM_PROFILE_COLORSPACE_RGB) {
+
+		/* RGB */
+		component_width = 3;
+		type = TYPE_RGB_DBL;
+
+		/* create input array */
+		values_in = g_new0 (gdouble, size * 3 * component_width);
+		divamount = 1.0f / (gfloat) (size - 1);
+		for (i=0; i<size; i++) {
+			divadd = divamount * (gfloat) i;
+
+			/* red component */
+			values_in[(i * 3 * component_width)+0] = divadd;
+			values_in[(i * 3 * component_width)+1] = 0.0f;
+			values_in[(i * 3 * component_width)+2] = 0.0f;
+
+			/* green component */
+			values_in[(i * 3 * component_width)+3] = 0.0f;
+			values_in[(i * 3 * component_width)+4] = divadd;
+			values_in[(i * 3 * component_width)+5] = 0.0f;
+
+			/* blue component */
+			values_in[(i * 3 * component_width)+6] = 0.0f;
+			values_in[(i * 3 * component_width)+7] = 0.0f;
+			values_in[(i * 3 * component_width)+8] = divadd;
+		}
+	}
+
+	/* do each transform */
+	if (values_in != NULL) {
+		/* create output array */
+		values_out = g_new0 (gdouble, size * 3 * component_width);
+
+		/* create a transform from profile_lcms1 to sRGB */
+		srgb_profile_lcms1 = cmsCreate_sRGBProfile ();
+		transform = cmsCreateTransform (priv->lcms_profile, type, srgb_profile_lcms1, TYPE_RGB_DBL, INTENT_PERCEPTUAL, 0);
+		if (transform == NULL)
+			goto out;
+
+		/* do transform */
+		cmsDoTransform (transform, values_in, values_out, size * 3);
+
+		/* create output array */
+		array = g_ptr_array_new_with_free_func (g_free);
+
+		for (i=0; i<size; i++) {
+			data = g_new0 (GcmClutData, 1);
+
+			data->red = values_out[(i * 3 * component_width)+0] * (gfloat) 0xffff;
+			data->green = values_out[(i * 3 * component_width)+4] * (gfloat) 0xffff;
+			data->blue = values_out[(i * 3 * component_width)+8] * (gfloat) 0xffff;
+			g_ptr_array_add (array, data);
+		}
+		clut = gcm_clut_new ();
+		gcm_clut_set_source_array (clut, array);
+	}
+
+out:
+	g_free (values_in);
+	g_free (values_out);
+	if (array != NULL)
+		g_ptr_array_unref (array);
+	if (transform != NULL)
+		cmsDeleteTransform (transform);
+	if (srgb_profile_lcms1 != NULL)
+		cmsCloseProfile (srgb_profile_lcms1);
+	return clut;
+}
+
+/**
+ * gcm_profile_lcms1_lcms_error_cb:
+ **/
+static int
+gcm_profile_lcms1_lcms_error_cb (int ErrorCode, const char *ErrorText)
+{
+	egg_warning ("LCMS error %i: %s", ErrorCode, ErrorText);
+	return LCMS_ERRC_WARNING;
+}
+
+/**
+ * gcm_profile_lcms1_class_init:
+ **/
+static void
+gcm_profile_lcms1_class_init (GcmProfileLcms1Class *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	GcmProfileClass *parent_class = GCM_PROFILE_CLASS (klass);
+	object_class->finalize = gcm_profile_lcms1_finalize;
+
+	parent_class->parse_data = gcm_profile_lcms1_parse_data;
+	parent_class->generate_vcgt = gcm_profile_lcms1_generate_vcgt;
+	parent_class->generate_curve = gcm_profile_lcms1_generate_curve;
+
+	g_type_class_add_private (klass, sizeof (GcmProfileLcms1Private));
+}
+
+/**
+ * gcm_profile_lcms1_init:
+ **/
+static void
+gcm_profile_lcms1_init (GcmProfileLcms1 *profile_lcms1)
+{
+	profile_lcms1->priv = GCM_PROFILE_LCMS1_GET_PRIVATE (profile_lcms1);
+	profile_lcms1->priv->vcgt_data = NULL;
+	profile_lcms1->priv->mlut_data = NULL;
+	profile_lcms1->priv->adobe_gamma_workaround = FALSE;
+
+	/* setup LCMS */
+	cmsSetErrorHandler (gcm_profile_lcms1_lcms_error_cb);
+	cmsErrorAction (LCMS_ERROR_SHOW);
+	cmsSetLanguage ("en", "US");
+}
+
+/**
+ * gcm_profile_lcms1_finalize:
+ **/
+static void
+gcm_profile_lcms1_finalize (GObject *object)
+{
+	GcmProfileLcms1 *profile_lcms1 = GCM_PROFILE_LCMS1 (object);
+	GcmProfileLcms1Private *priv = profile_lcms1->priv;
+
+	if (priv->lcms_profile != NULL)
+		cmsCloseProfile (priv->lcms_profile);
+
+	g_free (priv->vcgt_data);
+	g_free (priv->mlut_data);
+
+	G_OBJECT_CLASS (gcm_profile_lcms1_parent_class)->finalize (object);
+}
+
+/**
+ * gcm_profile_lcms1_new:
+ *
+ * Return value: a new GcmProfileLcms1 object.
+ **/
+GcmProfileLcms1 *
+gcm_profile_lcms1_new (void)
+{
+	GcmProfileLcms1 *profile_lcms1;
+	profile_lcms1 = g_object_new (GCM_TYPE_PROFILE_LCMS1, NULL);
+	return GCM_PROFILE_LCMS1 (profile_lcms1);
+}
+
diff --git a/src/gcm-profile-lcms1.h b/src/gcm-profile-lcms1.h
new file mode 100644
index 0000000..78cefe9
--- /dev/null
+++ b/src/gcm-profile-lcms1.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GCM_PROFILE_LCMS1_H
+#define __GCM_PROFILE_LCMS1_H
+
+#include <glib-object.h>
+
+#include "gcm-clut.h"
+#include "gcm-profile.h"
+
+G_BEGIN_DECLS
+
+#define GCM_TYPE_PROFILE_LCMS1		(gcm_profile_lcms1_get_type ())
+#define GCM_PROFILE_LCMS1(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), GCM_TYPE_PROFILE_LCMS1, GcmProfileLcms1))
+#define GCM_PROFILE_LCMS1_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), GCM_TYPE_PROFILE_LCMS1, GcmProfileLcms1Class))
+#define GCM_IS_PROFILE_LCMS1(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), GCM_TYPE_PROFILE_LCMS1))
+#define GCM_IS_PROFILE_LCMS1_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), GCM_TYPE_PROFILE_LCMS1))
+#define GCM_PROFILE_LCMS1_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), GCM_TYPE_PROFILE_LCMS1, GcmProfileLcms1Class))
+
+typedef struct _GcmProfileLcms1Private	GcmProfileLcms1Private;
+typedef struct _GcmProfileLcms1		GcmProfileLcms1;
+typedef struct _GcmProfileLcms1Class	GcmProfileLcms1Class;
+
+struct _GcmProfileLcms1
+{
+	 GcmProfile		 parent;
+	 GcmProfileLcms1Private	*priv;
+};
+
+struct _GcmProfileLcms1Class
+{
+	GcmProfileClass		parent_class;
+	/* padding for future expansion */
+	void (*_gcm_reserved1) (void);
+	void (*_gcm_reserved2) (void);
+	void (*_gcm_reserved3) (void);
+	void (*_gcm_reserved4) (void);
+	void (*_gcm_reserved5) (void);
+};
+
+GType		 gcm_profile_lcms1_get_type		(void);
+GcmProfileLcms1	*gcm_profile_lcms1_new			(void);
+
+G_END_DECLS
+
+#endif /* __GCM_PROFILE_LCMS1_H */
+
diff --git a/src/gcm-profile.c b/src/gcm-profile.c
index 87bb2ca..a1836d8 100644
--- a/src/gcm-profile.c
+++ b/src/gcm-profile.c
@@ -31,58 +31,18 @@
 
 #include <glib-object.h>
 #include <glib/gi18n.h>
-#include <math.h>
-#include <lcms.h>
 
 #include "egg-debug.h"
 
 #include "gcm-profile.h"
 #include "gcm-utils.h"
 #include "gcm-xyz.h"
+#include "gcm-profile-lcms1.h"
 
 static void     gcm_profile_finalize	(GObject     *object);
 
 #define GCM_PROFILE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GCM_TYPE_PROFILE, GcmProfilePrivate))
 
-#define GCM_NUMTAGS			0x80
-#define GCM_BODY			0x84
-
-#define GCM_TAG_ID			0x00
-#define GCM_TAG_OFFSET			0x04
-#define GCM_TAG_SIZE			0x08
-#define GCM_TAG_WIDTH			0x0c
-
-#define icSigVideoCartGammaTableTag		0x76636774
-#define icSigMachineLookUpTableTag		0x6d4c5554
-
-#define GCM_MLUT_RED			0x000
-#define GCM_MLUT_GREEN			0x200
-#define GCM_MLUT_BLUE			0x400
-
-#define GCM_DESC_RECORD_SIZE		0x08
-#define GCM_DESC_RECORD_TEXT		0x0c
-#define GCM_TEXT_RECORD_TEXT		0x08
-
-#define GCM_VCGT_ID			0x00
-#define GCM_VCGT_DUMMY			0x04
-#define GCM_VCGT_GAMMA_TYPE		0x08
-#define GCM_VCGT_GAMMA_DATA		0x0c
-
-#define GCM_VCGT_FORMULA_GAMMA_RED	0x00
-#define GCM_VCGT_FORMULA_MIN_RED	0x04
-#define GCM_VCGT_FORMULA_MAX_RED	0x08
-#define GCM_VCGT_FORMULA_GAMMA_GREEN	0x0c
-#define GCM_VCGT_FORMULA_MIN_GREEN	0x10
-#define GCM_VCGT_FORMULA_MAX_GREEN	0x14
-#define GCM_VCGT_FORMULA_GAMMA_BLUE	0x18
-#define GCM_VCGT_FORMULA_MIN_BLUE	0x1c
-#define GCM_VCGT_FORMULA_MAX_BLUE	0x20
-
-#define GCM_VCGT_TABLE_NUM_CHANNELS	0x00
-#define GCM_VCGT_TABLE_NUM_ENTRIES	0x02
-#define GCM_VCGT_TABLE_NUM_SIZE		0x04
-#define GCM_VCGT_TABLE_NUM_DATA		0x06
-
 /**
  * GcmProfilePrivate:
  *
@@ -90,7 +50,6 @@ static void     gcm_profile_finalize	(GObject     *object);
  **/
 struct _GcmProfilePrivate
 {
-	gboolean			 loaded;
 	guint				 profile_type;
 	guint				 colorspace;
 	guint				 size;
@@ -100,15 +59,6 @@ struct _GcmProfilePrivate
 	gchar				*manufacturer;
 	gchar				*model;
 	gchar				*datetime;
-	gboolean			 has_mlut;
-	gboolean			 has_vcgt_formula;
-	gboolean			 has_vcgt_table;
-	cmsHPROFILE			 lcms_profile;
-	GcmClutData			*vcgt_data;
-	guint				 vcgt_data_size;
-	GcmClutData			*mlut_data;
-	guint				 mlut_data_size;
-	gboolean			 adobe_gamma_workaround;
 	GcmXyz				*white_point;
 	GcmXyz				*black_point;
 	GcmXyz				*luminance_red;
@@ -128,6 +78,7 @@ enum {
 	PROP_COLORSPACE,
 	PROP_SIZE,
 	PROP_WHITE_POINT,
+	PROP_BLACK_POINT,
 	PROP_LUMINANCE_RED,
 	PROP_LUMINANCE_GREEN,
 	PROP_LUMINANCE_BLUE,
@@ -137,714 +88,27 @@ enum {
 G_DEFINE_TYPE (GcmProfile, gcm_profile, G_TYPE_OBJECT)
 
 /**
- * gcm_parser_decode_32:
- **/
-static guint
-gcm_parser_decode_32 (const guint8 *data)
-{
-	guint retval;
-	retval = (*(data + 0) << 0) + (*(data + 1) << 8) + (*(data + 2) << 16) + (*(data + 3) << 24);
-	return GUINT32_FROM_BE (retval);
-}
-
-/**
- * gcm_parser_decode_16:
- **/
-static guint
-gcm_parser_decode_16 (const guint8 *data)
-{
-	guint retval;
-	retval = (*(data + 0) << 0) + (*(data + 1) << 8);
-	return GUINT16_FROM_BE (retval);
-}
-
-/**
- * gcm_parser_decode_8:
- **/
-static guint
-gcm_parser_decode_8 (const guint8 *data)
-{
-	guint retval;
-	retval = (*data << 0);
-	return GUINT16_FROM_BE (retval);
-}
-
-/**
- * gcm_prefs_get_tag_description:
- **/
-static const gchar *
-gcm_prefs_get_tag_description (guint tag)
-{
-	if (tag == icSigProfileDescriptionTag)
-		return "profileDescription";
-	if (tag == icSigVideoCartGammaTableTag)
-		return "videoCartGammaTable";
-	if (tag == icSigMachineLookUpTableTag)
-		return "massiveLookUpTable";
-	if (tag == icSigDeviceMfgDescTag)
-		return "deviceMfgDesc";
-	if (tag == icSigDeviceModelDescTag)
-		return "deviceModelDesc";
-	if (tag == icSigViewingCondDescTag)
-		return "viewingCondDesc";
-	if (tag == icSigViewingConditionsTag)
-		return "viewingConditions";
-	if (tag == icSigLuminanceTag)
-		return "luminance";
-	if (tag == icSigMeasurementTag)
-		return "measurement";
-	if (tag == icSigRedColorantTag)
-		return "redMatrixColumn";
-	if (tag == icSigGreenColorantTag)
-		return "greenMatrixColumn";
-	if (tag == icSigBlueColorantTag)
-		return "blueMatrixColumn";
-	if (tag == icSigRedTRCTag)
-		return "redTRC";
-	if (tag == icSigGreenTRCTag)
-		return "greenTRC";
-	if (tag == icSigBlueTRCTag)
-		return "blueTRC";
-	if (tag == icSigMediaWhitePointTag)
-		return "mediaWhitePoint";
-	if (tag == icSigMediaBlackPointTag)
-		return "mediaBlackPoint";
-	if (tag == icSigTechnologyTag)
-		return "technology";
-	if (tag == icSigCopyrightTag)
-		return "copyright";
-	if (tag == icSigCalibrationDateTimeTag)
-		return "calibrationDateTime";
-	if (tag == icSigColorantTableTag)
-		return "colorantTable";
-	if (tag == icSigBToA0Tag)
-		return "BToA0";
-	if (tag == icSigBToA1Tag)
-		return "BToA1";
-	if (tag == icSigBToA2Tag)
-		return "BToA2";
-	if (tag == icSigAToB0Tag)
-		return "AToB0";
-	if (tag == icSigAToB1Tag)
-		return "AToB1";
-	if (tag == icSigAToB2Tag)
-		return "AToB2";
-	return NULL;
-}
-
-/**
- * gcm_parser_load_icc_mlut:
- **/
-static gboolean
-gcm_parser_load_icc_mlut (GcmProfile *profile, const guint8 *data, guint size)
-{
-	gboolean ret = TRUE;
-	guint i;
-	GcmClutData *mlut_data;
-
-	/* just load in data into a fixed size LUT */
-	profile->priv->mlut_data = g_new0 (GcmClutData, 256);
-	mlut_data = profile->priv->mlut_data;
-
-	for (i=0; i<256; i++)
-		mlut_data[i].red = gcm_parser_decode_16 (data + GCM_MLUT_RED + i*2);
-	for (i=0; i<256; i++)
-		mlut_data[i].green = gcm_parser_decode_16 (data + GCM_MLUT_GREEN + i*2);
-	for (i=0; i<256; i++)
-		mlut_data[i].blue = gcm_parser_decode_16 (data + GCM_MLUT_BLUE + i*2);
-
-	/* save datatype */
-	profile->priv->has_mlut = TRUE;
-	return ret;
-}
-
-/**
- * gcm_parser_load_icc_vcgt_formula:
- **/
-static gboolean
-gcm_parser_load_icc_vcgt_formula (GcmProfile *profile, const guint8 *data, guint size)
-{
-	gboolean ret = FALSE;
-	GcmClutData *vcgt_data;
-
-	egg_debug ("loading a formula encoded gamma table");
-
-	/* just load in data into a temporary array */
-	profile->priv->vcgt_data = g_new0 (GcmClutData, 4);
-	vcgt_data = profile->priv->vcgt_data;
-
-	/* read in block of data */
-	vcgt_data[0].red = gcm_parser_decode_32 (data + GCM_VCGT_FORMULA_GAMMA_RED);
-	vcgt_data[0].green = gcm_parser_decode_32 (data + GCM_VCGT_FORMULA_GAMMA_GREEN);
-	vcgt_data[0].blue = gcm_parser_decode_32 (data + GCM_VCGT_FORMULA_GAMMA_BLUE);
-
-	vcgt_data[1].red = gcm_parser_decode_32 (data + GCM_VCGT_FORMULA_MIN_RED);
-	vcgt_data[1].green = gcm_parser_decode_32 (data + GCM_VCGT_FORMULA_MIN_GREEN);
-	vcgt_data[1].blue = gcm_parser_decode_32 (data + GCM_VCGT_FORMULA_MIN_BLUE);
-
-	vcgt_data[2].red = gcm_parser_decode_32 (data + GCM_VCGT_FORMULA_MAX_RED);
-	vcgt_data[2].green = gcm_parser_decode_32 (data + GCM_VCGT_FORMULA_MAX_GREEN);
-	vcgt_data[2].blue = gcm_parser_decode_32 (data + GCM_VCGT_FORMULA_MAX_BLUE);
-
-	/* check if valid */
-	if (vcgt_data[0].red / 65536.0 > 5.0 || vcgt_data[0].green / 65536.0 > 5.0 || vcgt_data[0].blue / 65536.0 > 5.0) {
-		egg_warning ("Gamma values out of range: [R:%u G:%u B:%u]", vcgt_data[0].red, vcgt_data[0].green, vcgt_data[0].blue);
-		goto out;
-	}
-	if (vcgt_data[1].red / 65536.0 >= 1.0 || vcgt_data[1].green / 65536.0 >= 1.0 || vcgt_data[1].blue / 65536.0 >= 1.0) {
-		egg_warning ("Gamma min limit out of range: [R:%u G:%u B:%u]", vcgt_data[1].red, vcgt_data[1].green, vcgt_data[1].blue);
-		goto out;
-	}
-	if (vcgt_data[2].red / 65536.0 > 1.0 || vcgt_data[2].green / 65536.0 > 1.0 || vcgt_data[2].blue / 65536.0 > 1.0) {
-		egg_warning ("Gamma max limit out of range: [R:%u G:%u B:%u]", vcgt_data[2].red, vcgt_data[2].green, vcgt_data[2].blue);
-		goto out;
-	}
-
-	/* save datatype */
-	profile->priv->has_vcgt_formula = TRUE;
-	profile->priv->vcgt_data_size = 3;
-	ret = TRUE;
-out:
-	return ret;
-}
-
-/**
- * gcm_parser_load_icc_vcgt_table:
- **/
-static gboolean
-gcm_parser_load_icc_vcgt_table (GcmProfile *profile, const guint8 *data, guint size)
-{
-	gboolean ret = TRUE;
-	guint num_channels = 0;
-	guint num_entries = 0;
-	guint entry_size = 0;
-	guint i;
-	GcmClutData *vcgt_data;
-
-	egg_debug ("loading a table encoded gamma table");
-
-	num_channels = gcm_parser_decode_16 (data + GCM_VCGT_TABLE_NUM_CHANNELS);
-	num_entries = gcm_parser_decode_16 (data + GCM_VCGT_TABLE_NUM_ENTRIES);
-	entry_size = gcm_parser_decode_16 (data + GCM_VCGT_TABLE_NUM_SIZE);
-
-	/* work-around for AdobeGamma-Profiles (taken from xcalib) */
-	if (profile->priv->adobe_gamma_workaround) {
-		egg_debug ("Working around AdobeGamma profile");
-		entry_size = 2;
-		num_entries = 256;
-		num_channels = 3;
-	}
-
-	egg_debug ("channels: %u", num_channels);
-	egg_debug ("entry size: %ubits", entry_size * 8);
-	egg_debug ("entries/channel: %u", num_entries);
-
-	/* only able to parse RGB data */
-	if (num_channels != 3) {
-		egg_warning ("cannot parse non RGB entries");
-		ret = FALSE;
-		goto out;
-	}
-
-	/* bigger than will fit in 16 bits? */
-	if (entry_size > 2) {
-		egg_warning ("cannot parse large entries");
-		ret = FALSE;
-		goto out;
-	}
-
-	/* allocate ramp, plus one entry for extrapolation */
-	profile->priv->vcgt_data = g_new0 (GcmClutData, num_entries + 1);
-	vcgt_data = profile->priv->vcgt_data;
-
-	if (entry_size == 1) {
-		for (i=0; i<num_entries; i++)
-			vcgt_data[i].red = gcm_parser_decode_8 (data + GCM_VCGT_TABLE_NUM_DATA + (num_entries * 0) + i);
-		for (i=0; i<num_entries; i++)
-			vcgt_data[i].green = gcm_parser_decode_8 (data + GCM_VCGT_TABLE_NUM_DATA + (num_entries * 1) + i);
-		for (i=0; i<num_entries; i++)
-			vcgt_data[i].blue = gcm_parser_decode_8 (data + GCM_VCGT_TABLE_NUM_DATA + (num_entries * 2) + i);
-	} else {
-		for (i=0; i<num_entries; i++)
-			vcgt_data[i].red = gcm_parser_decode_16 (data + GCM_VCGT_TABLE_NUM_DATA + (num_entries * 0) + (i*2));
-		for (i=0; i<num_entries; i++)
-			vcgt_data[i].green = gcm_parser_decode_16 (data + GCM_VCGT_TABLE_NUM_DATA + (num_entries * 2) + (i*2));
-		for (i=0; i<num_entries; i++)
-			vcgt_data[i].blue = gcm_parser_decode_16 (data + GCM_VCGT_TABLE_NUM_DATA + (num_entries * 4) + (i*2));
-	}
-
-	/* save datatype */
-	profile->priv->has_vcgt_table = TRUE;
-	profile->priv->vcgt_data_size = num_entries;
-out:
-	return ret;
-}
-
-/**
- * gcm_parser_load_icc_vcgt:
- **/
-static gboolean
-gcm_parser_load_icc_vcgt (GcmProfile *profile, const guint8 *data, guint size)
-{
-	gboolean ret = FALSE;
-	guint tag_id;
-	guint gamma_type;
-
-	/* check we have a VCGT block */
-	tag_id = gcm_parser_decode_32 (data);
-	if (tag_id != icSigVideoCartGammaTableTag) {
-		egg_warning ("invalid content of table vcgt, starting with %x", tag_id);
-		goto out;
-	}
-
-	/* check what type of gamma encoding we have */
-	gamma_type = gcm_parser_decode_32 (data + GCM_VCGT_GAMMA_TYPE);
-	if (gamma_type == 0) {
-		ret = gcm_parser_load_icc_vcgt_table (profile, data + GCM_VCGT_GAMMA_DATA, size);
-		goto out;
-	}
-	if (gamma_type == 1) {
-		ret = gcm_parser_load_icc_vcgt_formula (profile, data + GCM_VCGT_GAMMA_DATA, size);
-		goto out;
-	}
-
-	/* we didn't understand the encoding */
-	egg_warning ("gamma type encoding not recognised");
-out:
-	return ret;
-}
-
-/**
- * gcm_profile_ensure_printable:
- **/
-static void
-gcm_profile_ensure_printable (gchar *data)
-{
-	guint i;
-	guint idx = 0;
-
-	g_return_if_fail (data != NULL);
-
-	for (i=0; data[i] != '\0'; i++) {
-		if (g_ascii_isalnum (data[i]) ||
-		    g_ascii_ispunct (data[i]) ||
-		    data[i] == ' ')
-			data[idx++] = data[i];
-	}
-	data[idx] = '\0';
-
-	/* broken profiles have _ instead of spaces */
-	g_strdelimit (data, "_", ' ');
-}
-
-/**
- * gcm_profile_utf16be_to_locale:
- *
- * Convert ICC encoded UTF-16BE into a string the user can understand
- **/
-static gchar *
-gcm_profile_utf16be_to_locale (const guint8 *text, guint size)
-{
-	gsize items_written;
-	gchar *text_utf8 = NULL;
-	gchar *text_locale = NULL;
-	GError *error = NULL;
-
-	/* convert from ICC text encoding to UTF-8 */
-	text_utf8 = g_convert ((const gchar*)text, size, "UTF-8", "UTF-16BE", NULL, &items_written, &error);
-	if (text_utf8 == NULL) {
-		egg_warning ("failed to convert to UTF-8: %s", error->message);
-		g_error_free (error);
-		goto out;
-	}
-
-	/* convert from UTF-8 to the users locale*/
-	text_locale = g_locale_from_utf8 (text_utf8, items_written, NULL, NULL, &error);
-	if (text_locale == NULL) {
-		egg_warning ("failed to convert to locale: %s", error->message);
-		g_error_free (error);
-		goto out;
-	}
-out:
-	g_free (text_utf8);
-	return text_locale;
-}
-
-/**
- * gcm_profile_parse_multi_localized_unicode:
- **/
-static gchar *
-gcm_profile_parse_multi_localized_unicode (GcmProfile *profile, const guint8 *data, guint size)
-{
-	guint i;
-	gchar *text = NULL;
-	guint record_size;
-	guint names_size;
-	guint len;
-	guint offset_name;
-	guint32 type;
-
-	/* get type */
-	type = gcm_parser_decode_32 (data);
-
-	/* check we are not a localized tag */
-	if (type == icSigTextDescriptionType) {
-		record_size = gcm_parser_decode_32 (data + GCM_DESC_RECORD_SIZE);
-		text = g_strndup ((const gchar*)&data[GCM_DESC_RECORD_TEXT], record_size);
-		goto out;
-	}
-
-	/* check we are not a localized tag */
-	if (type == icSigTextType) {
-		text = g_strdup ((const gchar*)&data[GCM_TEXT_RECORD_TEXT]);
-		goto out;
-	}
-
-	/* check we are not a localized tag */
-	if (type == icSigMultiLocalizedUnicodeType) {
-		names_size = gcm_parser_decode_32 (data + 8);
-		if (names_size != 1) {
-			/* there is more than one language encoded */
-			egg_warning ("more than one item of data in MLUC (names size: %i), using first one", names_size);
-		}
-		record_size = gcm_parser_decode_32 (data + 12);
-		len = gcm_parser_decode_32 (data + 20);
-		offset_name = gcm_parser_decode_32 (data + 24);
-		text = gcm_profile_utf16be_to_locale (data + offset_name, len);
-		goto out;
-	}
-
-	/* an unrecognised tag */
-	for (i=0x0; i<0x1c; i++) {
-		egg_warning ("unrecognised text tag");
-		if (data[i] >= 'A' && data[i] <= 'z')
-			egg_debug ("%i\t%c (%i)", i, data[i], data[i]);
-		else
-			egg_debug ("%i\t  (%i)", i, data[i]);
-	}
-out:
-	return text;
-}
-
-/**
- * gcm_profile_ensure_sane_length:
- **/
-static void
-gcm_profile_ensure_sane_length (gchar *text, guint max_length)
-{
-	guint i;
-	guint len;
-
-	/* get length */
-	len = strlen (text);
-
-	/* check we have room for ellipsis */
-	if (len <= max_length - 4)
-		return;
-
-	/* already correct len */
-	if (len == max_length)
-		return;
-
-	/* truncate, finding prior word break */
-	for (i=max_length-1; i>0; i--) {
-		if (text[i] == ' ')
-			break;
-	}
-
-	/* one long string with no spaces */
-	if (i == 0)
-		i = max_length - 3;
-
-	/* ellipsis */
-	text[i+0] = '.';
-	text[i+1] = '.';
-	text[i+2] = '.';
-	text[i+3] = '\0';
-}
-
-/**
  * gcm_profile_parse_data:
  **/
 gboolean
 gcm_profile_parse_data (GcmProfile *profile, const guint8 *data, gsize length, GError **error)
 {
 	gboolean ret = FALSE;
-	guint num_tags;
-	guint i;
-	guint tag_id;
-	guint offset;
-	guint tag_size;
-	guint tag_offset;
-	icProfileClassSignature profile_class;
-	icColorSpaceSignature color_space;
 	GcmProfilePrivate *priv = profile->priv;
-	cmsCIEXYZ cie_xyz;
-	cmsCIEXYZTRIPLE cie_illum;
-	struct tm created;
-	cmsHPROFILE xyz_profile;
-	cmsHTRANSFORM transform;
-
-	g_return_val_if_fail (GCM_IS_PROFILE (profile), FALSE);
-	g_return_val_if_fail (data != NULL, FALSE);
-	g_return_val_if_fail (priv->loaded == FALSE, FALSE);
-
-	priv->loaded = TRUE;
-
-	/* load profile into lcms */
-	priv->lcms_profile = cmsOpenProfileFromMem ((LPVOID)data, length);
-	if (priv->lcms_profile == NULL) {
-		*error = g_error_new (1, 0, "failed to load: not an ICC profile");
-		goto out;
-	}
-
-	/* get white point */
-	ret = cmsTakeMediaWhitePoint (&cie_xyz, priv->lcms_profile);
-	if (ret) {
-		g_object_set (priv->white_point,
-			      "cie-x", cie_xyz.X,
-			      "cie-y", cie_xyz.Y,
-			      "cie-z", cie_xyz.Z,
-			      NULL);
-	} else {
-		gcm_xyz_clear (priv->white_point);
-		egg_warning ("failed to get white point");
-	}
-
-	/* get black point */
-	ret = cmsTakeMediaBlackPoint (&cie_xyz, priv->lcms_profile);
-	if (ret) {
-		g_object_set (priv->black_point,
-			      "cie-x", cie_xyz.X,
-			      "cie-y", cie_xyz.Y,
-			      "cie-z", cie_xyz.Z,
-			      NULL);
-	} else {
-		gcm_xyz_clear (priv->black_point);
-		egg_warning ("failed to get black point");
-	}
-
-	/* get the profile type */
-	profile_class = cmsGetDeviceClass (priv->lcms_profile);
-	switch (profile_class) {
-	case icSigInputClass:
-		priv->profile_type = GCM_PROFILE_TYPE_INPUT_DEVICE;
-		break;
-	case icSigDisplayClass:
-		priv->profile_type = GCM_PROFILE_TYPE_DISPLAY_DEVICE;
-		break;
-	case icSigOutputClass:
-		priv->profile_type = GCM_PROFILE_TYPE_OUTPUT_DEVICE;
-		break;
-	case icSigLinkClass:
-		priv->profile_type = GCM_PROFILE_TYPE_DEVICELINK;
-		break;
-	case icSigColorSpaceClass:
-		priv->profile_type = GCM_PROFILE_TYPE_COLORSPACE_CONVERSION;
-		break;
-	case icSigAbstractClass:
-		priv->profile_type = GCM_PROFILE_TYPE_ABSTRACT;
-		break;
-	case icSigNamedColorClass:
-		priv->profile_type = GCM_PROFILE_TYPE_NAMED_COLOR;
-		break;
-	default:
-		priv->profile_type = GCM_PROFILE_TYPE_UNKNOWN;
-	}
-
-	/* get colorspace */
-	color_space = cmsGetColorSpace (priv->lcms_profile);
-	switch (color_space) {
-	case icSigXYZData:
-		priv->colorspace = GCM_PROFILE_COLORSPACE_XYZ;
-		break;
-	case icSigLabData:
-		priv->colorspace = GCM_PROFILE_COLORSPACE_LAB;
-		break;
-	case icSigLuvData:
-		priv->colorspace = GCM_PROFILE_COLORSPACE_LUV;
-		break;
-	case icSigYCbCrData:
-		priv->colorspace = GCM_PROFILE_COLORSPACE_YCBCR;
-		break;
-	case icSigYxyData:
-		priv->colorspace = GCM_PROFILE_COLORSPACE_YXY;
-		break;
-	case icSigRgbData:
-		priv->colorspace = GCM_PROFILE_COLORSPACE_RGB;
-		break;
-	case icSigGrayData:
-		priv->colorspace = GCM_PROFILE_COLORSPACE_GRAY;
-		break;
-	case icSigHsvData:
-		priv->colorspace = GCM_PROFILE_COLORSPACE_HSV;
-		break;
-	case icSigCmykData:
-		priv->colorspace = GCM_PROFILE_COLORSPACE_CMYK;
-		break;
-	case icSigCmyData:
-		priv->colorspace = GCM_PROFILE_COLORSPACE_CMY;
-		break;
-	default:
-		priv->colorspace = GCM_PROFILE_COLORSPACE_UNKNOWN;
-	}
-
-	/* get primary illuminants */
-	ret = cmsTakeColorants (&cie_illum, priv->lcms_profile);
-
-	/* geting the illuminants failed, try running it through the profile */
-	if (!ret && color_space == icSigRgbData) {
-		gdouble rgb_values[3];
-
-		/* create a transform from profile to XYZ */
-		xyz_profile = cmsCreateXYZProfile ();
-		transform = cmsCreateTransform (priv->lcms_profile, TYPE_RGB_DBL, xyz_profile, TYPE_XYZ_DBL, INTENT_PERCEPTUAL, 0);
-		if (transform != NULL) {
-
-			/* red */
-			rgb_values[0] = 1.0;
-			rgb_values[1] = 0.0;
-			rgb_values[2] = 0.0;
-			cmsDoTransform (transform, rgb_values, &cie_illum.Red, 1);
-
-			/* green */
-			rgb_values[0] = 0.0;
-			rgb_values[1] = 1.0;
-			rgb_values[2] = 0.0;
-			cmsDoTransform (transform, rgb_values, &cie_illum.Green, 1);
-
-			/* blue */
-			rgb_values[0] = 0.0;
-			rgb_values[1] = 0.0;
-			rgb_values[2] = 1.0;
-			cmsDoTransform (transform, rgb_values, &cie_illum.Blue, 1);
-
-			/* we're done */
-			cmsDeleteTransform (transform);
-			ret = TRUE;
-		}
-
-		/* no more need for the output profile */
-		cmsCloseProfile (xyz_profile);
-	}
-
-	/* we've got valid values */
-	if (ret) {
-		g_object_set (priv->luminance_red,
-			      "cie-x", cie_illum.Red.X,
-			      "cie-y", cie_illum.Red.Y,
-			      "cie-z", cie_illum.Red.Z,
-			      NULL);
-		g_object_set (priv->luminance_green,
-			      "cie-x", cie_illum.Green.X,
-			      "cie-y", cie_illum.Green.Y,
-			      "cie-z", cie_illum.Green.Z,
-			      NULL);
-		g_object_set (priv->luminance_blue,
-			      "cie-x", cie_illum.Blue.X,
-			      "cie-y", cie_illum.Blue.Y,
-			      "cie-z", cie_illum.Blue.Z,
-			      NULL);
-	} else {
-		gcm_xyz_clear (priv->luminance_red);
-		gcm_xyz_clear (priv->luminance_green);
-		gcm_xyz_clear (priv->luminance_blue);
-		egg_debug ("failed to get luminance values");
-	}
-
-	/* get the profile created time and date */
-	ret = cmsTakeCreationDateTime (&created, priv->lcms_profile);
-	if (ret)
-		priv->datetime = gcm_utils_format_date_time (&created);
-
-	/* get the number of tags in the file */
-	num_tags = gcm_parser_decode_32 (data + GCM_NUMTAGS);
-	egg_debug ("number of tags: %i", num_tags);
-
-	for (i=0; i<num_tags; i++) {
-		const gchar *tag_description;
-		offset = GCM_TAG_WIDTH * i;
-		tag_id = gcm_parser_decode_32 (data + GCM_BODY + offset + GCM_TAG_ID);
-		tag_offset = gcm_parser_decode_32 (data + GCM_BODY + offset + GCM_TAG_OFFSET);
-		tag_size = gcm_parser_decode_32 (data + GCM_BODY + offset + GCM_TAG_SIZE);
-
-		/* print description */
-		tag_description = gcm_prefs_get_tag_description (tag_id);
-		if (tag_description == NULL)
-			egg_debug ("unknown tag %x is present at 0x%x with size %u", tag_id, tag_offset, tag_size);
-		else
-			egg_debug ("named tag %x [%s] is present at 0x%x with size %u", tag_id, tag_description, tag_offset, tag_size);
-
-		if (tag_id == icSigProfileDescriptionTag) {
-			priv->description = gcm_profile_parse_multi_localized_unicode (profile, data + tag_offset, tag_size);
-			egg_debug ("found DESC: %s", priv->description);
-		}
-		if (tag_id == icSigCopyrightTag) {
-			priv->copyright = gcm_profile_parse_multi_localized_unicode (profile, data + tag_offset, tag_size);
-			egg_debug ("found COPYRIGHT: %s", priv->copyright);
-		}
-		if (tag_id == icSigDeviceMfgDescTag) {
-			priv->manufacturer = gcm_profile_parse_multi_localized_unicode (profile, data + tag_offset, tag_size);
-			egg_debug ("found MANUFACTURER: %s", priv->manufacturer);
-		}
-		if (tag_id == icSigDeviceModelDescTag) {
-			priv->model = gcm_profile_parse_multi_localized_unicode (profile, data + tag_offset, tag_size);
-			egg_debug ("found MODEL: %s", priv->model);
-		}
-		if (tag_id == icSigMachineLookUpTableTag) {
-			egg_debug ("found MLUT which is a fixed size block");
-			ret = gcm_parser_load_icc_mlut (profile, data + tag_offset, tag_size);
-			if (!ret) {
-				*error = g_error_new (1, 0, "failed to load mlut");
-				goto out;
-			}
-		}
-		if (tag_id == icSigVideoCartGammaTableTag) {
-			egg_debug ("found VCGT");
-			if (tag_size == 1584)
-				priv->adobe_gamma_workaround = TRUE;
-			ret = gcm_parser_load_icc_vcgt (profile, data + tag_offset, tag_size);
-			if (!ret) {
-				*error = g_error_new (1, 0, "failed to load vcgt");
-				goto out;
-			}
-		}
-	}
-
-	/* there's nothing sensible to display */
-	if (priv->description == NULL || priv->description[0] == '\0') {
-		g_free (priv->description);
-		if (priv->filename != NULL) {
-			priv->description = g_path_get_basename (priv->filename);
-		} else {
-			/* TRANSLATORS: this is where the ICC profile has no description */
-			priv->description = _("Missing description");
-		}
-	}
-
-	/* Ensure this is displayable */
-	if (priv->description != NULL)
-		gcm_profile_ensure_printable (priv->description);
-	if (priv->copyright != NULL)
-		gcm_profile_ensure_printable (priv->copyright);
-	if (priv->manufacturer != NULL)
-		gcm_profile_ensure_printable (priv->manufacturer);
-	if (priv->model != NULL)
-		gcm_profile_ensure_printable (priv->model);
-
-	/* some profiles have _really_ long titles - Microsoft, I'm looking at you... */
-	if (priv->description != NULL)
-		gcm_profile_ensure_sane_length (priv->description, 80);
+	GcmProfileClass *klass = GCM_PROFILE_GET_CLASS (profile);
 
 	/* save the length */
 	priv->size = length;
 
-	/* success */
-	ret = TRUE;
+	/* do we have support */
+	if (klass->parse_data == NULL) {
+		if (error != NULL)
+			*error = g_error_new (1, 0, "no support");
+		goto out;
+	}
 
-	egg_debug ("Has MLUT:         %s", priv->has_mlut ? "YES" : "NO");
-	egg_debug ("Has VCGT formula: %s", priv->has_vcgt_formula ? "YES" : "NO");
-	egg_debug ("Has VCGT table:   %s", priv->has_vcgt_table ? "YES" : "NO");
+	/* proxy */
+	ret = klass->parse_data (profile, data, length, error);
 out:
 	return ret;
 }
@@ -863,7 +127,6 @@ gcm_profile_parse (GcmProfile *profile, const gchar *filename, GError **error)
 
 	g_return_val_if_fail (GCM_IS_PROFILE (profile), FALSE);
 	g_return_val_if_fail (filename != NULL, FALSE);
-	g_return_val_if_fail (priv->loaded == FALSE, FALSE);
 
 	egg_debug ("loading '%s'", filename);
 
@@ -897,121 +160,16 @@ out:
 GcmClut *
 gcm_profile_generate_vcgt (GcmProfile *profile, guint size)
 {
-	guint i;
-	guint ratio;
-	GcmClutData *tmp;
-	GcmClutData *vcgt_data;
-	GcmClutData *mlut_data;
-	gfloat gamma_red, min_red, max_red;
-	gfloat gamma_green, min_green, max_green;
-	gfloat gamma_blue, min_blue, max_blue;
-	guint num_entries;
 	GcmClut *clut = NULL;
-	GPtrArray *array = NULL;
-	gfloat inverse_ratio;
-	guint idx;
-	gfloat frac;
-
-	g_return_val_if_fail (GCM_IS_PROFILE (profile), NULL);
-	g_return_val_if_fail (size != 0, FALSE);
-
-	/* reduce dereferences */
-	vcgt_data = profile->priv->vcgt_data;
-	mlut_data = profile->priv->mlut_data;
-
-	if (profile->priv->has_vcgt_table) {
-
-		/* create array */
-		array = g_ptr_array_new_with_free_func (g_free);
-
-		/* simply subsample if the LUT is smaller than the number of entries in the file */
-		num_entries = profile->priv->vcgt_data_size;
-		if (num_entries >= size) {
-			ratio = (guint) (num_entries / size);
-			for (i=0; i<size; i++) {
-				/* add a point */
-				tmp = g_new0 (GcmClutData, 1);
-				tmp->red = vcgt_data[ratio*i].red;
-				tmp->green = vcgt_data[ratio*i].green;
-				tmp->blue = vcgt_data[ratio*i].blue;
-				g_ptr_array_add (array, tmp);
-			}
-			goto out;
-		}
+	GcmProfileClass *klass = GCM_PROFILE_GET_CLASS (profile);
 
-		/* LUT is bigger than number of entries, so interpolate */
-		inverse_ratio = (gfloat) num_entries / size;
-		vcgt_data[num_entries].red = 0xffff;
-		vcgt_data[num_entries].green = 0xffff;
-		vcgt_data[num_entries].blue = 0xffff;
-
-		/* interpolate */
-		for (i=0; i<size; i++) {
-			idx = floor(i*inverse_ratio);
-			frac = (i*inverse_ratio) - idx;
-			tmp = g_new0 (GcmClutData, 1);
-			tmp->red = vcgt_data[idx].red * (1.0f-frac) + vcgt_data[idx + 1].red * frac;
-			tmp->green = vcgt_data[idx].green * (1.0f-frac) + vcgt_data[idx + 1].green * frac;
-			tmp->blue = vcgt_data[idx].blue * (1.0f-frac) + vcgt_data[idx + 1].blue * frac;
-			g_ptr_array_add (array, tmp);
-		}
-		goto out;
-	}
-
-	if (profile->priv->has_vcgt_formula) {
-
-		/* create array */
-		array = g_ptr_array_new_with_free_func (g_free);
-
-		gamma_red = (gfloat) vcgt_data[0].red / 65536.0;
-		gamma_green = (gfloat) vcgt_data[0].green / 65536.0;
-		gamma_blue = (gfloat) vcgt_data[0].blue / 65536.0;
-		min_red = (gfloat) vcgt_data[1].red / 65536.0;
-		min_green = (gfloat) vcgt_data[1].green / 65536.0;
-		min_blue = (gfloat) vcgt_data[1].blue / 65536.0;
-		max_red = (gfloat) vcgt_data[2].red / 65536.0;
-		max_green = (gfloat) vcgt_data[2].green / 65536.0;
-		max_blue = (gfloat) vcgt_data[2].blue / 65536.0;
-
-		/* create mapping of desired size */
-		for (i=0; i<size; i++) {
-			/* add a point */
-			tmp = g_new0 (GcmClutData, 1);
-			tmp->red = 65536.0 * ((gdouble) pow ((gdouble) i / (gdouble) size, gamma_red) * (max_red - min_red) + min_red);
-			tmp->green = 65536.0 * ((gdouble) pow ((gdouble) i / (gdouble) size, gamma_green) * (max_green - min_green) + min_green);
-			tmp->blue = 65536.0 * ((gdouble) pow ((gdouble) i / (gdouble) size, gamma_blue) * (max_blue - min_blue) + min_blue);
-			g_ptr_array_add (array, tmp);
-		}
-		goto out;
-	}
-
-	if (profile->priv->has_mlut) {
-
-		/* create array */
-		array = g_ptr_array_new_with_free_func (g_free);
-
-		/* roughly interpolate table */
-		ratio = (guint) (256 / (size));
-		for (i=0; i<size; i++) {
-			/* add a point */
-			tmp = g_new0 (GcmClutData, 1);
-			tmp->red = mlut_data[ratio*i].red;
-			tmp->green = mlut_data[ratio*i].green;
-			tmp->blue = mlut_data[ratio*i].blue;
-			g_ptr_array_add (array, tmp);
-		}
+	/* do we have support */
+	if (klass->generate_vcgt == NULL)
 		goto out;
-	}
 
-	/* bugger */
-	egg_debug ("no LUT to generate");
+	/* proxy */
+	clut = klass->generate_vcgt (profile, size);
 out:
-	if (array != NULL) {
-		/* create new output array */
-		clut = gcm_clut_new ();
-		gcm_clut_set_source_array (clut, array);
-		g_ptr_array_unref (array);
-	}
 	return clut;
 }
 
@@ -1024,87 +182,15 @@ GcmClut *
 gcm_profile_generate_curve (GcmProfile *profile, guint size)
 {
 	GcmClut *clut = NULL;
-	gdouble *values_in = NULL;
-	gdouble *values_out = NULL;
-	guint i;
-	GcmClutData *data;
-	GPtrArray *array = NULL;
-	gfloat divamount;
-	gfloat divadd;
-	guint component_width;
-	cmsHPROFILE srgb_profile = NULL;
-	cmsHTRANSFORM transform = NULL;
-	GcmProfilePrivate *priv = profile->priv;
-	guint type;
-
-	/* run through the profile */
-	if (priv->colorspace == GCM_PROFILE_COLORSPACE_RGB) {
-
-		/* RGB */
-		component_width = 3;
-		type = TYPE_RGB_DBL;
-
-		/* create input array */
-		values_in = g_new0 (gdouble, size * 3 * component_width);
-		divamount = 1.0f / (gfloat) (size - 1);
-		for (i=0; i<size; i++) {
-			divadd = divamount * (gfloat) i;
-
-			/* red component */
-			values_in[(i * 3 * component_width)+0] = divadd;
-			values_in[(i * 3 * component_width)+1] = 0.0f;
-			values_in[(i * 3 * component_width)+2] = 0.0f;
-
-			/* green component */
-			values_in[(i * 3 * component_width)+3] = 0.0f;
-			values_in[(i * 3 * component_width)+4] = divadd;
-			values_in[(i * 3 * component_width)+5] = 0.0f;
-
-			/* blue component */
-			values_in[(i * 3 * component_width)+6] = 0.0f;
-			values_in[(i * 3 * component_width)+7] = 0.0f;
-			values_in[(i * 3 * component_width)+8] = divadd;
-		}
-	}
-
-	/* do each transform */
-	if (values_in != NULL) {
-		/* create output array */
-		values_out = g_new0 (gdouble, size * 3 * component_width);
-
-		/* create a transform from profile to sRGB */
-		srgb_profile = cmsCreate_sRGBProfile ();
-		transform = cmsCreateTransform (priv->lcms_profile, type, srgb_profile, TYPE_RGB_DBL, INTENT_PERCEPTUAL, 0);
-		if (transform == NULL)
-			goto out;
-
-		/* do transform */
-		cmsDoTransform (transform, values_in, values_out, size * 3);
+	GcmProfileClass *klass = GCM_PROFILE_GET_CLASS (profile);
 
-		/* create output array */
-		array = g_ptr_array_new_with_free_func (g_free);
-
-		for (i=0; i<size; i++) {
-			data = g_new0 (GcmClutData, 1);
-
-			data->red = values_out[(i * 3 * component_width)+0] * (gfloat) 0xffff;
-			data->green = values_out[(i * 3 * component_width)+4] * (gfloat) 0xffff;
-			data->blue = values_out[(i * 3 * component_width)+8] * (gfloat) 0xffff;
-			g_ptr_array_add (array, data);
-		}
-		clut = gcm_clut_new ();
-		gcm_clut_set_source_array (clut, array);
-	}
+	/* do we have support */
+	if (klass->generate_curve == NULL)
+		goto out;
 
+	/* proxy */
+	clut = klass->generate_curve (profile, size);
 out:
-	g_free (values_in);
-	g_free (values_out);
-	if (array != NULL)
-		g_ptr_array_unref (array);
-	if (transform != NULL)
-		cmsDeleteTransform (transform);
-	if (srgb_profile != NULL)
-		cmsCloseProfile (srgb_profile);
 	return clut;
 }
 
@@ -1161,16 +247,6 @@ gcm_profile_colorspace_to_text (GcmProfileColorspace type)
 }
 
 /**
- * gcm_profile_lcms_error_cb:
- **/
-static int
-gcm_profile_lcms_error_cb (int ErrorCode, const char *ErrorText)
-{
-	egg_warning ("LCMS error %i: %s", ErrorCode, ErrorText);
-	return LCMS_ERRC_WARNING;
-}
-
-/**
  * gcm_profile_get_property:
  **/
 static void
@@ -1210,6 +286,9 @@ gcm_profile_get_property (GObject *object, guint prop_id, GValue *value, GParamS
 	case PROP_WHITE_POINT:
 		g_value_set_object (value, priv->white_point);
 		break;
+	case PROP_BLACK_POINT:
+		g_value_set_object (value, priv->black_point);
+		break;
 	case PROP_LUMINANCE_RED:
 		g_value_set_object (value, priv->luminance_red);
 		break;
@@ -1231,7 +310,81 @@ gcm_profile_get_property (GObject *object, guint prop_id, GValue *value, GParamS
 static void
 gcm_profile_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
 {
+	GcmProfile *profile = GCM_PROFILE (object);
+	GcmProfilePrivate *priv = profile->priv;
+
 	switch (prop_id) {
+	case PROP_COPYRIGHT:
+		g_free (priv->copyright);
+		priv->copyright = g_strdup (g_value_get_string (value));
+		if (priv->copyright != NULL)
+			gcm_utils_ensure_printable (priv->copyright);
+		break;
+	case PROP_MANUFACTURER:
+		g_free (priv->manufacturer);
+		priv->manufacturer = g_strdup (g_value_get_string (value));
+		if (priv->manufacturer != NULL)
+			gcm_utils_ensure_printable (priv->manufacturer);
+		break;
+	case PROP_MODEL:
+		g_free (priv->model);
+		priv->model = g_strdup (g_value_get_string (value));
+		if (priv->model != NULL)
+			gcm_utils_ensure_printable (priv->model);
+		break;
+	case PROP_DATETIME:
+		g_free (priv->datetime);
+		priv->datetime = g_strdup (g_value_get_string (value));
+		break;
+	case PROP_DESCRIPTION:
+		g_free (priv->description);
+		priv->description = g_strdup (g_value_get_string (value));
+		if (priv->description != NULL)
+			gcm_utils_ensure_printable (priv->description);
+
+		/* some profile_lcms1s have _really_ long titles - Microsoft, I'm looking at you... */
+		if (priv->description != NULL)
+			gcm_utils_ensure_sane_length (priv->description, 80);
+
+		/* there's nothing sensible to display */
+		if (priv->description == NULL || priv->description[0] == '\0') {
+			g_free (priv->description);
+			if (priv->filename != NULL) {
+				priv->description = g_path_get_basename (priv->filename);
+			} else {
+				/* TRANSLATORS: this is where the ICC profile_lcms1 has no description */
+				priv->description = _("Missing description");
+			}
+		}
+		break;
+	case PROP_FILENAME:
+		g_free (priv->filename);
+		priv->filename = g_strdup (g_value_get_string (value));
+		break;
+	case PROP_TYPE:
+		priv->profile_type = g_value_get_uint (value);
+		break;
+	case PROP_COLORSPACE:
+		priv->colorspace = g_value_get_uint (value);
+		break;
+	case PROP_SIZE:
+		priv->size = g_value_get_uint (value);
+		break;
+	case PROP_WHITE_POINT:
+		priv->white_point = g_value_dup_object (value);
+		break;
+	case PROP_BLACK_POINT:
+		priv->black_point = g_value_dup_object (value);
+		break;
+	case PROP_LUMINANCE_RED:
+		priv->luminance_red = g_value_dup_object (value);
+		break;
+	case PROP_LUMINANCE_GREEN:
+		priv->luminance_green = g_value_dup_object (value);
+		break;
+	case PROP_LUMINANCE_BLUE:
+		priv->luminance_blue = g_value_dup_object (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1255,7 +408,7 @@ gcm_profile_class_init (GcmProfileClass *klass)
 	 */
 	pspec = g_param_spec_string ("copyright", NULL, NULL,
 				     NULL,
-				     G_PARAM_READABLE);
+				     G_PARAM_READWRITE);
 	g_object_class_install_property (object_class, PROP_COPYRIGHT, pspec);
 
 	/**
@@ -1263,7 +416,7 @@ gcm_profile_class_init (GcmProfileClass *klass)
 	 */
 	pspec = g_param_spec_string ("manufacturer", NULL, NULL,
 				     NULL,
-				     G_PARAM_READABLE);
+				     G_PARAM_READWRITE);
 	g_object_class_install_property (object_class, PROP_MANUFACTURER, pspec);
 
 	/**
@@ -1271,7 +424,7 @@ gcm_profile_class_init (GcmProfileClass *klass)
 	 */
 	pspec = g_param_spec_string ("model", NULL, NULL,
 				     NULL,
-				     G_PARAM_READABLE);
+				     G_PARAM_READWRITE);
 	g_object_class_install_property (object_class, PROP_MODEL, pspec);
 
 	/**
@@ -1279,7 +432,7 @@ gcm_profile_class_init (GcmProfileClass *klass)
 	 */
 	pspec = g_param_spec_string ("datetime", NULL, NULL,
 				     NULL,
-				     G_PARAM_READABLE);
+				     G_PARAM_READWRITE);
 	g_object_class_install_property (object_class, PROP_DATETIME, pspec);
 
 	/**
@@ -1287,7 +440,7 @@ gcm_profile_class_init (GcmProfileClass *klass)
 	 */
 	pspec = g_param_spec_string ("description", NULL, NULL,
 				     NULL,
-				     G_PARAM_READABLE);
+				     G_PARAM_READWRITE);
 	g_object_class_install_property (object_class, PROP_DESCRIPTION, pspec);
 
 	/**
@@ -1295,7 +448,7 @@ gcm_profile_class_init (GcmProfileClass *klass)
 	 */
 	pspec = g_param_spec_string ("filename", NULL, NULL,
 				     NULL,
-				     G_PARAM_READABLE);
+				     G_PARAM_READWRITE);
 	g_object_class_install_property (object_class, PROP_FILENAME, pspec);
 
 	/**
@@ -1303,7 +456,7 @@ gcm_profile_class_init (GcmProfileClass *klass)
 	 */
 	pspec = g_param_spec_uint ("type", NULL, NULL,
 				   0, G_MAXUINT, 0,
-				   G_PARAM_READABLE);
+				   G_PARAM_READWRITE);
 	g_object_class_install_property (object_class, PROP_TYPE, pspec);
 
 	/**
@@ -1311,7 +464,7 @@ gcm_profile_class_init (GcmProfileClass *klass)
 	 */
 	pspec = g_param_spec_uint ("colorspace", NULL, NULL,
 				   0, G_MAXUINT, 0,
-				   G_PARAM_READABLE);
+				   G_PARAM_READWRITE);
 	g_object_class_install_property (object_class, PROP_COLORSPACE, pspec);
 
 	/**
@@ -1319,7 +472,7 @@ gcm_profile_class_init (GcmProfileClass *klass)
 	 */
 	pspec = g_param_spec_uint ("size", NULL, NULL,
 				   0, G_MAXUINT, 0,
-				   G_PARAM_READABLE);
+				   G_PARAM_READWRITE);
 	g_object_class_install_property (object_class, PROP_SIZE, pspec);
 
 	/**
@@ -1327,15 +480,23 @@ gcm_profile_class_init (GcmProfileClass *klass)
 	 */
 	pspec = g_param_spec_object ("white-point", NULL, NULL,
 				     GCM_TYPE_XYZ,
-				     G_PARAM_READABLE);
+				     G_PARAM_READWRITE);
 	g_object_class_install_property (object_class, PROP_WHITE_POINT, pspec);
 
 	/**
+	 * GcmProfile:black-point:
+	 */
+	pspec = g_param_spec_object ("black-point", NULL, NULL,
+				     GCM_TYPE_XYZ,
+				     G_PARAM_READWRITE);
+	g_object_class_install_property (object_class, PROP_BLACK_POINT, pspec);
+
+	/**
 	 * GcmProfile:luminance-red:
 	 */
 	pspec = g_param_spec_object ("luminance-red", NULL, NULL,
 				     GCM_TYPE_XYZ,
-				     G_PARAM_READABLE);
+				     G_PARAM_READWRITE);
 	g_object_class_install_property (object_class, PROP_LUMINANCE_RED, pspec);
 
 	/**
@@ -1343,7 +504,7 @@ gcm_profile_class_init (GcmProfileClass *klass)
 	 */
 	pspec = g_param_spec_object ("luminance-green", NULL, NULL,
 				     GCM_TYPE_XYZ,
-				     G_PARAM_READABLE);
+				     G_PARAM_READWRITE);
 	g_object_class_install_property (object_class, PROP_LUMINANCE_GREEN, pspec);
 
 	/**
@@ -1351,7 +512,7 @@ gcm_profile_class_init (GcmProfileClass *klass)
 	 */
 	pspec = g_param_spec_object ("luminance-blue", NULL, NULL,
 				     GCM_TYPE_XYZ,
-				     G_PARAM_READABLE);
+				     G_PARAM_READWRITE);
 	g_object_class_install_property (object_class, PROP_LUMINANCE_BLUE, pspec);
 
 	g_type_class_add_private (klass, sizeof (GcmProfilePrivate));
@@ -1364,9 +525,6 @@ static void
 gcm_profile_init (GcmProfile *profile)
 {
 	profile->priv = GCM_PROFILE_GET_PRIVATE (profile);
-	profile->priv->vcgt_data = NULL;
-	profile->priv->mlut_data = NULL;
-	profile->priv->adobe_gamma_workaround = FALSE;
 	profile->priv->profile_type = GCM_PROFILE_TYPE_UNKNOWN;
 	profile->priv->colorspace = GCM_PROFILE_COLORSPACE_UNKNOWN;
 	profile->priv->white_point = gcm_xyz_new ();
@@ -1374,11 +532,6 @@ gcm_profile_init (GcmProfile *profile)
 	profile->priv->luminance_red = gcm_xyz_new ();
 	profile->priv->luminance_green = gcm_xyz_new ();
 	profile->priv->luminance_blue = gcm_xyz_new ();
-
-	/* setup LCMS */
-	cmsSetErrorHandler (gcm_profile_lcms_error_cb);
-	cmsErrorAction (LCMS_ERROR_SHOW);
-	cmsSetLanguage ("en", "US");
 }
 
 /**
@@ -1390,17 +543,12 @@ gcm_profile_finalize (GObject *object)
 	GcmProfile *profile = GCM_PROFILE (object);
 	GcmProfilePrivate *priv = profile->priv;
 
-	if (priv->lcms_profile != NULL)
-		cmsCloseProfile (priv->lcms_profile);
-
 	g_free (priv->copyright);
 	g_free (priv->description);
 	g_free (priv->filename);
 	g_free (priv->manufacturer);
 	g_free (priv->model);
 	g_free (priv->datetime);
-	g_free (priv->vcgt_data);
-	g_free (priv->mlut_data);
 	g_object_unref (priv->white_point);
 	g_object_unref (priv->black_point);
 	g_object_unref (priv->luminance_red);
@@ -1423,10 +571,26 @@ gcm_profile_new (void)
 	return GCM_PROFILE (profile);
 }
 
+/**
+ * gcm_profile_default_new:
+ *
+ * Return value: a new GcmProfile object.
+ **/
+GcmProfile *
+gcm_profile_default_new (void)
+{
+	GcmProfile *profile = NULL;
+#if 1
+	profile = GCM_PROFILE (gcm_profile_lcms1_new ());
+#endif
+	return profile;
+}
+
 /***************************************************************************
  ***                          MAKE CHECK TESTS                           ***
  ***************************************************************************/
 #ifdef EGG_TEST
+#include <math.h>
 #include "egg-test.h"
 
 typedef struct {
@@ -1459,14 +623,14 @@ gcm_profile_test_parse_file (EggTest *test, const guint8 *datafile, GcmProfileTe
 	gfloat gamma;
 	gboolean ret;
 	GError *error = NULL;
-	GcmProfile *profile;
+	GcmProfile *profile_lcms1;
 	GcmXyz *xyz;
 	gfloat luminance;
 
 	/************************************************************/
-	egg_test_title (test, "get a profile object");
-	profile = gcm_profile_new ();
-	egg_test_assert (test, profile != NULL);
+	egg_test_title (test, "get a profile_lcms1 object");
+	profile_lcms1 = GCM_PROFILE(gcm_profile_lcms1_new ());
+	egg_test_assert (test, profile_lcms1 != NULL);
 
 	/************************************************************/
 	egg_test_title (test, "get filename of data file");
@@ -1475,14 +639,14 @@ gcm_profile_test_parse_file (EggTest *test, const guint8 *datafile, GcmProfileTe
 
 	/************************************************************/
 	egg_test_title (test, "load ICC file");
-	ret = gcm_profile_parse (profile, filename, &error);
+	ret = gcm_profile_parse (profile_lcms1, filename, &error);
 	if (ret)
 		egg_test_success (test, NULL);
 	else
 		egg_test_failed (test, "failed to parse: %s", error->message);
 
 	/* get some properties */
-	g_object_get (profile,
+	g_object_get (profile_lcms1,
 		      "copyright", &copyright,
 		      "manufacturer", &manufacturer,
 		      "model", &model,
@@ -1551,7 +715,7 @@ gcm_profile_test_parse_file (EggTest *test, const guint8 *datafile, GcmProfileTe
 
 	/************************************************************/
 	egg_test_title (test, "check luminance red %s", datafile);
-	g_object_get (profile,
+	g_object_get (profile_lcms1,
 		      "luminance-red", &xyz,
 		      NULL);
 	luminance = gcm_xyz_get_x (xyz);
@@ -1561,7 +725,7 @@ gcm_profile_test_parse_file (EggTest *test, const guint8 *datafile, GcmProfileTe
 		egg_test_failed (test, "invalid value: %f, expecting: %f", luminance, test_data->luminance);
 
 	g_object_unref (xyz);
-	g_object_unref (profile);
+	g_object_unref (profile_lcms1);
 	g_free (copyright);
 	g_free (manufacturer);
 	g_free (model);
@@ -1576,75 +740,10 @@ void
 gcm_profile_test (EggTest *test)
 {
 	GcmProfileTestData test_data;
-	gfloat fp;
-	gfloat expected;
-	gboolean ret;
-	const gchar temp[5] = {0x6d, 0x42, 0x41, 0x20, 0x00};
-	gchar *text;
 
 	if (!egg_test_start (test, "GcmProfile"))
 		return;
 
-	/************************************************************/
-	egg_test_title (test, "check strip printable");
-	text = g_strdup ("1\r34 67_90");
-	gcm_profile_ensure_printable (text);
-	if (g_strcmp0 (text, "134 67 90") == 0)
-		egg_test_success (test, NULL);
-	else
-		egg_test_failed (test, "invalid value: %s", text);
-	g_free (text);
-
-	/************************************************************/
-	egg_test_title (test, "check sane length high");
-	text = g_strdup ("1234 67890");
-	gcm_profile_ensure_sane_length (text, 1024);
-	if (g_strcmp0 (text, "1234 67890") == 0)
-		egg_test_success (test, NULL);
-	else
-		egg_test_failed (test, "invalid value: %s", text);
-	g_free (text);
-
-	/************************************************************/
-	egg_test_title (test, "check sane length limit");
-	text = g_strdup ("1234 67890");
-	gcm_profile_ensure_sane_length (text, 10);
-	if (g_strcmp0 (text, "1234 67890") == 0)
-		egg_test_success (test, NULL);
-	else
-		egg_test_failed (test, "invalid value: %s", text);
-	g_free (text);
-
-	/************************************************************/
-	egg_test_title (test, "check sane length truncate");
-	text = g_strdup ("1234 67890");
-	gcm_profile_ensure_sane_length (text, 8);
-	if (g_strcmp0 (text, "1234...") == 0)
-		egg_test_success (test, NULL);
-	else
-		egg_test_failed (test, "invalid value: %s", text);
-	g_free (text);
-
-	/************************************************************/
-	egg_test_title (test, "check sane length no spaces");
-	text = g_strdup ("1234 67890");
-	gcm_profile_ensure_sane_length (text, 4);
-	if (g_strcmp0 (text, "1...") == 0)
-		egg_test_success (test, NULL);
-	else
-		egg_test_failed (test, "invalid value: %s", text);
-	g_free (text);
-
-	/************************************************************/
-	egg_test_title (test, "check sane length no data");
-	text = g_strdup ("");
-	gcm_profile_ensure_sane_length (text, 4);
-	if (g_strcmp0 (text, "") == 0)
-		egg_test_success (test, NULL);
-	else
-		egg_test_failed (test, "invalid value: %s", text);
-	g_free (text);
-
 	/* bluish test */
 	test_data.copyright = "Copyright (c) 1998 Hewlett-Packard Company";
 	test_data.manufacturer = "IEC http://www.iec.ch";;
diff --git a/src/gcm-profile.h b/src/gcm-profile.h
index 3c17b5f..2e1716e 100644
--- a/src/gcm-profile.h
+++ b/src/gcm-profile.h
@@ -46,7 +46,16 @@ struct _GcmProfile
 
 struct _GcmProfileClass
 {
-	GObjectClass	parent_class;
+	GObjectClass	 parent_class;
+	gboolean	 (*parse_data)		(GcmProfile	*profile,
+						 const guint8	*data,
+						 gsize		 length,
+						 GError		**error);
+	GcmClut		*(*generate_vcgt)	(GcmProfile	*profile,
+						 guint		 size);
+	GcmClut		*(*generate_curve)	(GcmProfile	*profile,
+						 guint		 size);
+
 	/* padding for future expansion */
 	void (*_gcm_reserved1) (void);
 	void (*_gcm_reserved2) (void);
@@ -82,6 +91,7 @@ typedef enum {
 
 GType		 gcm_profile_get_type		  	(void);
 GcmProfile	*gcm_profile_new			(void);
+GcmProfile	*gcm_profile_default_new		(void);
 gboolean	 gcm_profile_parse			(GcmProfile	*profile,
 							 const gchar	*filename,
 							 GError		**error);
diff --git a/src/gcm-trc-widget.c b/src/gcm-trc-widget.c
index 02a7c2e..66d54e0 100644
--- a/src/gcm-trc-widget.c
+++ b/src/gcm-trc-widget.c
@@ -432,7 +432,7 @@ gcm_trc_widget_test (EggTest *test)
 	filename_profile = egg_test_get_data_file ("AdobeGammaTest.icm");
 	egg_test_assert (test, (filename_profile != NULL));
 
-	profile = gcm_profile_new ();
+	profile = gcm_profile_default_new ();
 	gcm_profile_parse (profile, filename_profile, NULL);
 	clut = gcm_profile_generate_vcgt (profile, 256);
 	g_object_set (widget,
diff --git a/src/gcm-utils.c b/src/gcm-utils.c
index de20a73..e03d699 100644
--- a/src/gcm-utils.c
+++ b/src/gcm-utils.c
@@ -404,7 +404,7 @@ gcm_utils_set_gamma_for_device (GcmDevice *device, GError **error)
 	use_global = gconf_client_get_bool (gconf_client, GCM_SETTINGS_GLOBAL_DISPLAY_CORRECTION, NULL);
 	if (use_global && filename != NULL) {
 		/* create dummy CLUT */
-		profile = gcm_profile_new ();
+		profile = gcm_profile_default_new ();
 		ret = gcm_profile_parse (profile, filename, error);
 		if (!ret)
 			goto out;



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