[gtk/matthiasc/color-profile-rebased: 28/43] Split GdkColorProfile




commit 384823c81122b9fbd1ac7b7b585840a593f1d863
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun May 8 12:49:18 2022 -0400

    Split GdkColorProfile
    
    Introduce GdkICCProfile and GdkDerivedProfile subclasses.
    GdkICCProfile takes over the ICC profile functionality
    from GdkColorProfile. A GdkDerivedProfile is our way
    of encoding coordinate transformations on top of ICC
    profiles, such as HSV or Lch. Update the memory conversion
    code to only handle ICC profiles, and update the color
    conversion code to accept derived profiles as well as
    ICC profiles.

 gdk/gdk.h                    |   2 +
 gdk/gdkcolorprofile.c        | 422 ++++-------------------------------
 gdk/gdkcolorprofile.h        |  10 +-
 gdk/gdkcolorprofileprivate.h |  26 ++-
 gdk/gdkderivedprofile.c      | 513 +++++++++++++++++++++++++++++++++++++++++++
 gdk/gdkderivedprofile.h      |  71 ++++++
 gdk/gdkiccprofile.c          | 451 +++++++++++++++++++++++++++++++++++++
 gdk/gdkiccprofile.h          |  56 +++++
 gdk/gdkiccprofileprivate.h   |  24 ++
 gdk/gdkmemoryformat.c        |  13 +-
 gdk/gdkmemorytexture.c       |   4 +-
 gdk/gdktexture.c             |   4 +-
 gdk/gdktypes.h               |   2 +
 gdk/loaders/gdkjpeg.c        |  18 +-
 gdk/loaders/gdkpng.c         |  15 +-
 gdk/loaders/gdktiff.c        |  12 +-
 gdk/meson.build              |   4 +
 gdk/x11/gdkdisplay-x11.c     |  72 +++---
 18 files changed, 1257 insertions(+), 462 deletions(-)
---
diff --git a/gdk/gdk.h b/gdk/gdk.h
index a9bbeb570c..0f3772544f 100644
--- a/gdk/gdk.h
+++ b/gdk/gdk.h
@@ -39,6 +39,7 @@
 #include <gdk/gdkcontentproviderimpl.h>
 #include <gdk/gdkcontentserializer.h>
 #include <gdk/gdkcursor.h>
+#include <gdk/gdkderivedprofile.h>
 #include <gdk/gdkdevice.h>
 #include <gdk/gdkdevicepad.h>
 #include <gdk/gdkdevicetool.h>
@@ -55,6 +56,7 @@
 #include <gdk/gdkframetimings.h>
 #include <gdk/gdkglcontext.h>
 #include <gdk/gdkgltexture.h>
+#include <gdk/gdkiccprofile.h>
 #include <gdk/gdkkeys.h>
 #include <gdk/gdkkeysyms.h>
 #include <gdk/gdkmemorytexture.h>
diff --git a/gdk/gdkcolorprofile.c b/gdk/gdkcolorprofile.c
index 45667870af..cbe68e0c09 100644
--- a/gdk/gdkcolorprofile.c
+++ b/gdk/gdkcolorprofile.c
@@ -40,307 +40,39 @@
 
 #include "gdkintl.h"
 
-struct _GdkColorProfile
-{
-  GObject parent_instance;
-
-  GBytes *icc_profile;
-  cmsHPROFILE lcms_profile;
-};
-
-struct _GdkColorProfileClass
-{
-  GObjectClass parent_class;
-};
-
-enum {
-  PROP_0,
-  PROP_ICC_PROFILE,
 
-  N_PROPS
-};
-
-static GParamSpec *properties[N_PROPS];
-
-static gboolean
-gdk_color_profile_real_init (GInitable     *initable,
-                             GCancellable  *cancellable,
-                             GError       **error)
-{
-  GdkColorProfile *self = GDK_COLOR_PROFILE (initable);
-
-  if (self->lcms_profile == NULL)
-    {
-      const guchar *data;
-      gsize size;
-
-      if (self->icc_profile == NULL)
-        self->icc_profile = g_bytes_new (NULL, 0);
-
-      data = g_bytes_get_data (self->icc_profile, &size);
-
-      self->lcms_profile = cmsOpenProfileFromMem (data, size);
-      if (self->lcms_profile == NULL)
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Failed to load ICC profile"));
-          return FALSE;
-        }
-    }
-
-  return TRUE;
-}
+G_DEFINE_TYPE (GdkColorProfile, gdk_color_profile, G_TYPE_OBJECT)
 
 static void
-gdk_color_profile_initable_init (GInitableIface *iface)
+gdk_color_profile_init (GdkColorProfile *profile)
 {
-  iface->init = gdk_color_profile_real_init;
 }
 
-
-G_DEFINE_TYPE_WITH_CODE (GdkColorProfile, gdk_color_profile, G_TYPE_OBJECT,
-                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gdk_color_profile_initable_init))
-
-static void
-gdk_color_profile_set_property (GObject      *gobject,
-                                guint         prop_id,
-                                const GValue *value,
-                                GParamSpec   *pspec)
+static gboolean
+gdk_color_profile_real_is_linear (GdkColorProfile *profile)
 {
-  GdkColorProfile *self = GDK_COLOR_PROFILE (gobject);
-
-  switch (prop_id)
-    {
-    case PROP_ICC_PROFILE:
-      self->icc_profile = g_value_dup_boxed (value);
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
-      break;
-    }
+  return FALSE;
 }
 
-static void
-gdk_color_profile_get_property (GObject    *gobject,
-                                guint       prop_id,
-                                GValue     *value,
-                                GParamSpec *pspec)
+static gsize
+gdk_color_profile_real_get_n_components (GdkColorProfile *profile)
 {
-  GdkColorProfile *self = GDK_COLOR_PROFILE (gobject);
-
-  switch (prop_id)
-    {
-    case PROP_ICC_PROFILE:
-      g_value_set_boxed (value, self->icc_profile);
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
-      break;
-    }
+  return 0;
 }
 
-static void
-gdk_color_profile_dispose (GObject *object)
+static gboolean
+gdk_color_profile_real_equal (gconstpointer profile1,
+                              gconstpointer profile2)
 {
-  GdkColorProfile *self = GDK_COLOR_PROFILE (object);
-
-  g_clear_pointer (&self->icc_profile, g_bytes_unref);
-  g_clear_pointer (&self->lcms_profile, cmsCloseProfile);
-
-  G_OBJECT_CLASS (gdk_color_profile_parent_class)->dispose (object);
+  return profile1 == profile2;
 }
 
 static void
 gdk_color_profile_class_init (GdkColorProfileClass *klass)
 {
-  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-
-  gobject_class->set_property = gdk_color_profile_set_property;
-  gobject_class->get_property = gdk_color_profile_get_property;
-  gobject_class->dispose = gdk_color_profile_dispose;
-
-  /**
-   * GdkColorProfile:icc-profile: (attributes org.gtk.Property.get=gdk_color_profile_get_icc_profile)
-   *
-   * the ICC profile for this color profile
-   */
-  properties[PROP_ICC_PROFILE] =
-    g_param_spec_boxed ("icc-profile",
-                        P_("ICC profile"),
-                        P_("ICC profile for this color profile"),
-                        G_TYPE_BYTES,
-                        G_PARAM_READWRITE |
-                        G_PARAM_CONSTRUCT_ONLY |
-                        G_PARAM_STATIC_STRINGS |
-                        G_PARAM_EXPLICIT_NOTIFY);
-
-  g_object_class_install_properties (gobject_class, N_PROPS, properties);
-}
-
-static void
-gdk_color_profile_init (GdkColorProfile *self)
-{
-}
-
-/**
- * gdk_color_profile_get_srgb:
- *
- * Returns the color profile representing the sRGB color space.
- *
- * If you don't know anything about color profiles but need one for
- * use with some function, this one is most likely the right one.
- *
- * Returns: (transfer none): the color profile for the sRGB
- *   color space.
- *
- * Since: 4.8
- */
-GdkColorProfile *
-gdk_color_profile_get_srgb (void)
-{
-  static GdkColorProfile *srgb_profile;
-
-  if (g_once_init_enter (&srgb_profile))
-    {
-      GdkColorProfile *new_profile;
-
-      new_profile = gdk_color_profile_new_from_lcms_profile (cmsCreate_sRGBProfile (), NULL);
-      g_assert (new_profile);
-
-      g_once_init_leave (&srgb_profile, new_profile);
-    }
-
-  return srgb_profile;
-}
-
-/*<private>
- * gdk_color_profile_get_srgb_linear:
- *
- * Returns the linear color profile corresponding to the sRGB
- * color space.
- *
- * It can display the same colors, but it does not have a gamma curve.
- *
- * Returns: (transfer none): the color profile for the linear sRGB
- *   color space.
- *
- * Since: 4.8
- */
-GdkColorProfile *
-gdk_color_profile_get_srgb_linear (void)
-{
-  static GdkColorProfile *srgb_linear_profile;
-
-  if (g_once_init_enter (&srgb_linear_profile))
-    {
-      cmsToneCurve *curve;
-      cmsHPROFILE lcms_profile;
-      GdkColorProfile *new_profile;
-
-      curve = cmsBuildGamma (NULL, 1.0);
-      lcms_profile = cmsCreateRGBProfile (&(cmsCIExyY) {
-                                            0.3127, 0.3290, 1.0
-                                          },
-                                          &(cmsCIExyYTRIPLE) {
-                                            { 0.6400, 0.3300, 1.0 },
-                                            { 0.3000, 0.6000, 1.0 },
-                                            { 0.1500, 0.0600, 1.0 }
-                                          },
-                                          (cmsToneCurve*[3]) { curve, curve, curve });
-      cmsFreeToneCurve (curve);
-
-      new_profile = gdk_color_profile_new_from_lcms_profile (lcms_profile, NULL);
-      g_assert (new_profile);
-
-      g_once_init_leave (&srgb_linear_profile, new_profile);
-    }
-
-  return srgb_linear_profile;
-}
-
-/**
- * gdk_color_profile_new_from_icc_bytes:
- * @bytes: The ICC profiles given as a `GBytes`
- * @error: Return location for an error
- *
- * Creates a new color profile for the given ICC profile data.
- *
- * if the profile is not valid, %NULL is returned and an error
- * is raised.
- *
- * Returns: a new `GdkColorProfile` or %NULL on error
- *
- * Since: 4.8
- */
-GdkColorProfile *
-gdk_color_profile_new_from_icc_bytes (GBytes  *bytes,
-                                      GError **error)
-{
-  g_return_val_if_fail (bytes != NULL, NULL);
-  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
-
-  return g_initable_new (GDK_TYPE_COLOR_PROFILE,
-                         NULL,
-                         error,
-                         "icc-profile", bytes,
-                         NULL);
-}
-
-GdkColorProfile *
-gdk_color_profile_new_from_lcms_profile (cmsHPROFILE   lcms_profile,
-                                         GError      **error)
-{
-  GdkColorProfile *result;
-  cmsUInt32Number size;
-  guchar *data;
-
-  size = 0;
-  if (!cmsSaveProfileToMem (lcms_profile, NULL, &size))
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Could not prepare ICC profile"));
-      return NULL;
-    }
-
-  data = g_malloc (size);
-  if (!cmsSaveProfileToMem (lcms_profile, data, &size))
-    {
-      g_free (data);
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Failed to save ICC profile"));
-      return NULL;
-    }
-
-  result = g_object_new (GDK_TYPE_COLOR_PROFILE, NULL);
-  result->lcms_profile = lcms_profile;
-  result->icc_profile = g_bytes_new_take (data, size);
-
-  return result;
-}
-
-/**
- * gdk_color_profile_get_icc_profile: (attributes org.gtk.Method.get_property=icc-profile)
- * @self: a `GdkColorProfile`
- *
- * Returns the serialized ICC profile of @self as %GBytes.
- *
- * Returns: (transfer none): the ICC profile
- *
- * Since: 4.8
- */
-GBytes *
-gdk_color_profile_get_icc_profile (GdkColorProfile *self)
-{
-  g_return_val_if_fail (GDK_IS_COLOR_PROFILE (self), NULL);
-
-  return self->icc_profile;
-}
-
-cmsHPROFILE *
-gdk_color_profile_get_lcms_profile (GdkColorProfile *self)
-{
-  g_return_val_if_fail (GDK_IS_COLOR_PROFILE (self), NULL);
-
-  return self->lcms_profile;
+  klass->is_linear = gdk_color_profile_real_is_linear;
+  klass->get_n_components = gdk_color_profile_real_get_n_components;
+  klass->equal = gdk_color_profile_real_equal;
 }
 
 /**
@@ -363,11 +95,28 @@ gdk_color_profile_is_linear (GdkColorProfile *self)
 {
   g_return_val_if_fail (GDK_IS_COLOR_PROFILE (self), FALSE);
 
-  /* FIXME: Make this useful */
-  if (self == gdk_color_profile_get_srgb_linear ())
-    return TRUE;
+  return GDK_COLOR_PROFILE_GET_CLASS (self)->is_linear (self);
+}
 
-  return FALSE;
+/**
+ * gdk_color_profile_get_n_components:
+ * @self: a `GdkColorProfile
+ *
+ * Gets the number of color components - also called channels - for
+ * the given profile. Note that this does not consider an alpha
+ * channel because color profiles have no alpha information. So
+ * for any form of RGB profile, this returned number will be 3.
+ *
+ * Returns: The number of components
+ *
+ * Since: 4.8
+ */
+gsize
+gdk_color_profile_get_n_components (GdkColorProfile *self)
+{
+  g_return_val_if_fail (GDK_IS_COLOR_PROFILE (self), 3);
+
+  return GDK_COLOR_PROFILE_GET_CLASS (self)->get_n_components (self);
 }
 
 /**
@@ -389,100 +138,11 @@ gboolean
 gdk_color_profile_equal (gconstpointer profile1,
                          gconstpointer profile2)
 {
-  return profile1 == profile2 ||
-         g_bytes_equal (GDK_COLOR_PROFILE (profile1)->icc_profile,
-                        GDK_COLOR_PROFILE (profile2)->icc_profile);
-}
-
-/* Check if the color profile and the memory format have the
- * same color components.
- */
-gboolean
-gdk_color_profile_supports_memory_format (GdkColorProfile *profile,
-                                          GdkMemoryFormat  format)
-{
-  /* Currently, all our memory formats are RGB (with or without alpha).
-   * Update this when that changes.
-   */
-  return cmsGetColorSpace (profile->lcms_profile) == cmsSigRgbData;
-}
-
-typedef struct _GdkColorTransformCache GdkColorTransformCache;
-
-struct _GdkColorTransformCache
-{
-  GdkColorProfile *source;
-  guint            source_type;
-  GdkColorProfile *dest;
-  guint            dest_type;
-};
-
-static void
-gdk_color_transform_cache_free (gpointer data)
-{
-  g_free (data);
-}
-
-static guint
-gdk_color_transform_cache_hash (gconstpointer data)
-{
-  const GdkColorTransformCache *cache = data;
-
-  return g_direct_hash (cache->source) ^
-         (g_direct_hash (cache->dest) >> 2) ^
-         ((cache->source_type << 16) | (cache->source_type >> 16)) ^
-         cache->dest_type;
-}
-
-static gboolean
-gdk_color_transform_cache_equal (gconstpointer data1,
-                                 gconstpointer data2)
-{
-  const GdkColorTransformCache *cache1 = data1;
-  const GdkColorTransformCache *cache2 = data2;
-
-  return cache1->source == cache2->source &&
-         cache1->source_type == cache2->source_type &&
-         cache1->dest == cache2->dest &&
-         cache1->dest_type == cache2->dest_type;
-}
-
-cmsHTRANSFORM *
-gdk_color_profile_lookup_transform (GdkColorProfile *source,
-                                    guint            source_type,
-                                    GdkColorProfile *dest,
-                                    guint            dest_type)
-{
-  GdkColorTransformCache *entry;
-  static GHashTable *cache = NULL;
-  cmsHTRANSFORM *transform;
-
-  if (cache == NULL)
-    cache = g_hash_table_new_full (gdk_color_transform_cache_hash,
-                                   gdk_color_transform_cache_equal,
-                                   gdk_color_transform_cache_free,
-                                   cmsDeleteTransform);
+  if (profile1 == profile2)
+    return TRUE;
 
-  transform = g_hash_table_lookup (cache,
-                                   &(GdkColorTransformCache) {
-                                     source, source_type,
-                                     dest, dest_type
-                                   });
-  if (G_UNLIKELY (transform == NULL))
-    {
-      transform = cmsCreateTransform (gdk_color_profile_get_lcms_profile (source),
-                                      source_type,
-                                      gdk_color_profile_get_lcms_profile (dest),
-                                      dest_type,
-                                      INTENT_PERCEPTUAL,
-                                      cmsFLAGS_COPY_ALPHA);
-      entry = g_new (GdkColorTransformCache, 1);
-      *entry = (GdkColorTransformCache) {
-                 source, source_type,
-                 dest, dest_type
-               };
-      g_hash_table_insert (cache, entry, transform);
-    }
+  if (G_OBJECT_TYPE (profile1) != G_OBJECT_TYPE (profile2))
+    return FALSE;
 
-  return transform;
+  return GDK_COLOR_PROFILE_GET_CLASS (profile1)->equal (profile1, profile2);
 }
diff --git a/gdk/gdkcolorprofile.h b/gdk/gdkcolorprofile.h
index a7b2d04f31..a7aef838a2 100644
--- a/gdk/gdkcolorprofile.h
+++ b/gdk/gdkcolorprofile.h
@@ -45,17 +45,13 @@ GDK_AVAILABLE_IN_4_8
 GType                        gdk_color_profile_get_type                   (void) G_GNUC_CONST;
 
 GDK_AVAILABLE_IN_4_8
-GdkColorProfile *            gdk_color_profile_get_srgb                   (void) G_GNUC_CONST;
+GdkColorProfile *            gdk_color_profile_get_srgb                   (void);
 
 GDK_AVAILABLE_IN_4_8
-GdkColorProfile *            gdk_color_profile_new_from_icc_bytes         (GBytes               *bytes,
-                                                                           GError              **error);
-
-GDK_AVAILABLE_IN_4_8
-GBytes *                     gdk_color_profile_get_icc_profile            (GdkColorProfile      *self);
+gboolean                     gdk_color_profile_is_linear                  (GdkColorProfile      *self) 
G_GNUC_PURE;
 
 GDK_AVAILABLE_IN_4_8
-gboolean                     gdk_color_profile_is_linear                  (GdkColorProfile      *self) 
G_GNUC_PURE;
+gsize                        gdk_color_profile_get_n_components           (GdkColorProfile      *self) 
G_GNUC_PURE;
 
 GDK_AVAILABLE_IN_4_8
 gboolean                     gdk_color_profile_equal                      (gconstpointer         profile1,
diff --git a/gdk/gdkcolorprofileprivate.h b/gdk/gdkcolorprofileprivate.h
index ab87ccde82..f92f91191e 100644
--- a/gdk/gdkcolorprofileprivate.h
+++ b/gdk/gdkcolorprofileprivate.h
@@ -8,20 +8,24 @@
 
 G_BEGIN_DECLS
 
-GdkColorProfile *            gdk_color_profile_get_srgb_linear            (void) G_GNUC_CONST;
-
-GdkColorProfile *            gdk_color_profile_new_from_lcms_profile      (cmsHPROFILE           
lcms_profile,
-                                                                           GError              **error);
+struct _GdkColorProfile
+{
+  GObject parent_instance;
+};
 
-cmsHPROFILE *                gdk_color_profile_get_lcms_profile           (GdkColorProfile      *self);
+struct _GdkColorProfileClass
+{
+  GObjectClass parent_class;
 
-gboolean                     gdk_color_profile_supports_memory_format     (GdkColorProfile      *profile,
-                                                                           GdkMemoryFormat       format);
+  gboolean (* is_linear)        (GdkColorProfile *profile);
+  gsize    (* get_n_components) (GdkColorProfile *profile);
+  gboolean (* equal)            (gconstpointer    profile1,
+                                 gconstpointer    profile2);
+};
 
-cmsHTRANSFORM *              gdk_color_profile_lookup_transform           (GdkColorProfile      *source,
-                                                                           guint                 source_type,
-                                                                           GdkColorProfile      *dest,
-                                                                           guint                 dest_type);
+GdkColorProfile *            gdk_color_profile_get_srgb_linear            (void) G_GNUC_CONST;
+GdkColorProfile *            gdk_color_profile_get_hsl                    (void) G_GNUC_CONST;
+GdkColorProfile *            gdk_color_profile_get_hwb                    (void) G_GNUC_CONST;
 
 G_END_DECLS
 
diff --git a/gdk/gdkderivedprofile.c b/gdk/gdkderivedprofile.c
new file mode 100644
index 0000000000..a734dc5a96
--- /dev/null
+++ b/gdk/gdkderivedprofile.c
@@ -0,0 +1,513 @@
+/* gdkderivedprofile.c
+ *
+ * Copyright 2021 (c) Red Hat, Inc.
+ *
+ * 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 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * GdkDerivedProfile:
+ *
+ * Since: 4.6
+ */
+
+#include "config.h"
+
+#include "gdkderivedprofile.h"
+#include "gdkcolorprofileprivate.h"
+#include "gdkenumtypes.h"
+
+#include "gdkintl.h"
+
+struct _GdkDerivedProfile
+{
+  GdkColorProfile parent_instance;
+
+  GdkColorSpace color_space;
+  GdkColorProfile *base_profile;
+};
+
+struct _GdkDerivedProfileClass
+{
+  GdkColorProfileClass parent_class;
+};
+
+enum {
+  PROP_0,
+  PROP_BASE_PROFILE,
+  PROP_COLOR_SPACE,
+
+  N_PROPS
+};
+
+static GParamSpec *properties[N_PROPS];
+
+G_DEFINE_TYPE (GdkDerivedProfile, gdk_derived_profile, GDK_TYPE_COLOR_PROFILE)
+
+static void
+gdk_derived_profile_init (GdkDerivedProfile *profile)
+{
+}
+
+static void
+gdk_derived_profile_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  GdkDerivedProfile *self = GDK_DERIVED_PROFILE (object);
+
+  switch (prop_id)
+    {
+    case PROP_BASE_PROFILE:
+      self->base_profile = g_value_dup_object (value);
+      break;
+
+    case PROP_COLOR_SPACE:
+      self->color_space = g_value_get_enum (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gdk_derived_profile_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  GdkDerivedProfile *self = GDK_DERIVED_PROFILE (object);
+
+  switch (prop_id)
+    {
+    case PROP_BASE_PROFILE:
+      g_value_set_object (value, self->base_profile);
+      break;
+
+    case PROP_COLOR_SPACE:
+      g_value_set_enum (value, self->color_space);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gdk_derived_profile_dispose (GObject *object)
+{
+  GdkDerivedProfile *self = GDK_DERIVED_PROFILE (object);
+
+  g_clear_object (&self->base_profile);
+
+  G_OBJECT_CLASS (gdk_derived_profile_parent_class)->dispose (object);
+}
+
+static gboolean
+gdk_derived_profile_is_linear (GdkColorProfile *profile)
+{
+  GdkDerivedProfile *self = GDK_DERIVED_PROFILE (profile);
+
+  return self->base_profile == gdk_color_profile_get_srgb_linear ();
+}
+
+static gsize
+gdk_derived_profile_get_n_components (GdkColorProfile *profile)
+{
+  GdkDerivedProfile *self = GDK_DERIVED_PROFILE (profile);
+
+  return gdk_color_profile_get_n_components (self->base_profile);
+}
+
+static gboolean
+gdk_derived_profile_equal (gconstpointer profile1,
+                           gconstpointer profile2)
+{
+  GdkDerivedProfile *p1 = GDK_DERIVED_PROFILE (profile1);
+  GdkDerivedProfile *p2 = GDK_DERIVED_PROFILE (profile2);
+
+  return p1->color_space == p2->color_space &&
+         gdk_color_profile_equal (p1->base_profile, p2->base_profile);
+}
+
+static void
+gdk_derived_profile_class_init (GdkDerivedProfileClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GdkColorProfileClass *profile_class = GDK_COLOR_PROFILE_CLASS (klass);
+
+  gobject_class->set_property = gdk_derived_profile_set_property;
+  gobject_class->get_property = gdk_derived_profile_get_property;
+  gobject_class->dispose = gdk_derived_profile_dispose;
+
+  profile_class->is_linear = gdk_derived_profile_is_linear;
+  profile_class->get_n_components = gdk_derived_profile_get_n_components;
+  profile_class->equal = gdk_derived_profile_equal;
+
+  /**
+   * GdkDerivedProfile:base-profile: (attributes org.gtk.Property.get=gdk_derived_profile_get_base_profile)
+   *
+   * The base profile for this color profile.
+   */
+  properties[PROP_BASE_PROFILE] =
+    g_param_spec_object ("base-profile",
+                         P_("Base profile"),
+                         P_("Base profile for this color profile"),
+                         GDK_TYPE_ICC_PROFILE,
+                         G_PARAM_READWRITE |
+                         G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS |
+                         G_PARAM_EXPLICIT_NOTIFY);
+
+  /**
+   * GdkDerivedProfile:color-space: (attributes org.gtk.Property.get=gdk_derived_profile_get_color_space)
+   *
+   * The color space for this color profile.
+   */
+  properties[PROP_COLOR_SPACE] =
+    g_param_spec_enum ("color-space",
+                       P_("Color space"),
+                       P_("Color space for this color profile"),
+                       GDK_TYPE_COLOR_SPACE,
+                       GDK_COLOR_SPACE_HSL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS |
+                       G_PARAM_EXPLICIT_NOTIFY);
+
+  g_object_class_install_properties (gobject_class, N_PROPS, properties);
+}
+
+/**
+ * gdk_derived_profile_get_base_profile:
+ * @profile: a `GdkDerivedProfile`
+ *
+ * Returns the base profile for this profile.
+ *
+ * Returns: (transfer none): the base profile
+ *
+ * Since: 4.6
+ */
+GdkColorProfile *
+gdk_derived_profile_get_base_profile (GdkDerivedProfile *self)
+{
+  return self->base_profile;
+}
+
+/**
+ * gdk_derived_profile_get_color_space:
+ * @profile: a `GdkDerivedProfile`
+ *
+ * Returns the color space for this profile.
+ *
+ * Returns: the color space
+ *
+ * Since: 4.6
+ */
+GdkColorSpace
+gdk_derived_profile_get_color_space (GdkDerivedProfile *self)
+{
+  return self->color_space;
+}
+
+/*<private>
+ * gdk_color_profile_get_hsl:
+ *
+ * Returns the color profile corresponding to the HSL
+ * color space.
+ *
+ * It can display the same colors as sRGB, but it encodes
+ * the coordinates differently.
+ *
+ * Returns: (transfer none): the color profile for the HSL
+ *   color space.
+ */
+GdkColorProfile *
+gdk_color_profile_get_hsl (void)
+{
+  static GdkColorProfile *hsl_profile;
+
+  if (g_once_init_enter (&hsl_profile))
+    {
+      GdkColorProfile *new_profile;
+
+      new_profile = g_object_new (GDK_TYPE_DERIVED_PROFILE,
+                                  "base-profile", gdk_color_profile_get_srgb (),
+                                  "color-space", GDK_COLOR_SPACE_HSL,
+                                  NULL);
+
+      g_assert (new_profile);
+
+      g_once_init_leave (&hsl_profile, new_profile);
+    }
+
+  return hsl_profile;
+}
+
+GdkColorProfile *
+gdk_color_profile_get_hwb (void)
+{
+  static GdkColorProfile *hwb_profile;
+
+  if (g_once_init_enter (&hwb_profile))
+    {
+      GdkColorProfile *new_profile;
+
+      new_profile = g_object_new (GDK_TYPE_DERIVED_PROFILE,
+                                  "base-profile", gdk_color_profile_get_srgb (),
+                                  "color-space", GDK_COLOR_SPACE_HWB,
+                                  NULL);
+
+      g_assert (new_profile);
+
+      g_once_init_leave (&hwb_profile, new_profile);
+    }
+
+  return hwb_profile;
+}
+
+static void
+hsl_to_rgb (const float *in,
+            float       *out)
+{
+  float lightness;
+  float saturation;
+  float m1, m2;
+  float orig_hue, hue;
+
+  lightness = in[2];
+  saturation = in[1];
+  orig_hue = in[0];
+
+  if (lightness <= 0.5)
+    m2 = lightness * (1 + saturation);
+  else
+    m2 = lightness + saturation - lightness * saturation;
+  m1 = 2 * lightness - m2;
+
+  if (saturation == 0)
+    {
+      out[0] = out[1] = out[2] = lightness;
+    }
+  else
+    {
+      hue = orig_hue + 120;
+      while (hue > 360)
+        hue -= 360;
+      while (hue < 0)
+        hue += 360;
+
+      if (hue < 60)
+        out[0] = m1 + (m2 - m1) * hue / 60;
+      else if (hue < 180)
+        out[0] = m2;
+      else if (hue < 240)
+        out[0] = m1 + (m2 - m1) * (240 - hue) / 60;
+      else
+        out[0] = m1;
+
+      hue = orig_hue;
+      while (hue > 360)
+        hue -= 360;
+      while (hue < 0)
+        hue += 360;
+
+      if (hue < 60)
+        out[1] = m1 + (m2 - m1) * hue / 60;
+      else if (hue < 180)
+        out[1] = m2;
+      else if (hue < 240)
+        out[1] = m1 + (m2 - m1) * (240 - hue) / 60;
+      else
+        out[1] = m1;
+
+      hue = orig_hue - 120;
+      while (hue > 360)
+        hue -= 360;
+      while (hue < 0)
+        hue += 360;
+
+      if (hue < 60)
+        out[2] = m1 + (m2 - m1) * hue / 60;
+      else if (hue < 180)
+        out[2] = m2;
+      else if (hue < 240)
+        out[2] = m1 + (m2 - m1) * (240 - hue) / 60;
+      else
+        out[2] = m1;
+    }
+}
+
+static void
+rgb_to_hsl (const float *in,
+            float       *out)
+{
+  float min;
+  float max;
+  float red;
+  float green;
+  float blue;
+  float delta;
+  float hue;
+  float saturation;
+  float lightness;
+
+  red = in[0];
+  green = in[1];
+  blue = in[2];
+
+  if (red > green)
+    {
+      if (red > blue)
+        max = red;
+      else
+        max = blue;
+
+      if (green < blue)
+        min = green;
+      else
+        min = blue;
+    }
+  else
+    {
+      if (green > blue)
+        max = green;
+      else
+        max = blue;
+
+      if (red < blue)
+        min = red;
+      else
+        min = blue;
+    }
+
+  lightness = (max + min) / 2;
+  saturation = 0;
+  hue = 0;
+
+  if (max != min)
+    {
+      if (lightness <= 0.5)
+        saturation = (max - min) / (max + min);
+      else
+        saturation = (max - min) / (2 - max - min);
+
+      delta = max -min;
+      if (red == max)
+        hue = (green - blue) / delta;
+      else if (green == max)
+        hue = 2 + (blue - red) / delta;
+      else if (blue == max)
+        hue = 4 + (red - green) / delta;
+
+      hue *= 60;
+      if (hue < 0.0)
+        hue += 360;
+    }
+
+  out[0] = hue;
+  out[1] = saturation;
+  out[2] = lightness;
+}
+
+static void
+hwb_to_rgb (const float *in,
+            float       *out)
+{
+  float hue;
+  float white;
+  float black;
+  float m[3];
+
+  hue = in[0];
+  white = in[1];
+  black = in[2];
+
+  white /= 100;
+  black /= 100;
+
+  if (white + black >= 1)
+    {
+      out[0] = out[1] = out[2] = white / (white + black);
+      return;
+    }
+
+  m[0] = hue;
+  m[1] = 1;
+  m[2] = 0.5;
+
+  hsl_to_rgb (m, out);
+
+  out[0] = out[0] * (1 - white - black) + white;
+  out[1] = out[1] * (1 - white - black) + white;
+  out[2] = out[2] * (1 - white - black) + white;
+}
+
+static void
+rgb_to_hwb (const float *in,
+            float       *out)
+{
+  float white;
+  float black;
+
+  rgb_to_hsl (in, out);
+
+  white = MIN (MIN (out[0], out[1]), out[2]);
+  black = 1 - MAX (MAX (out[0], out[1]), out[2]);
+
+  out[1] = white * 100;
+  out[2] = black * 100;
+}
+
+void
+gdk_derived_profile_convert_to_base_profile (GdkDerivedProfile *profile,
+                                             const float       *in,
+                                             float             *out)
+{
+  switch (gdk_derived_profile_get_color_space (profile))
+    {
+    case GDK_COLOR_SPACE_HSL:
+      hsl_to_rgb (in, out);
+      break;
+    case GDK_COLOR_SPACE_HWB:
+      hwb_to_rgb (in, out);
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+
+}
+
+void
+gdk_derived_profile_convert_from_base_profile (GdkDerivedProfile *profile,
+                                               const float       *in,
+                                               float            *out)
+{
+  switch (gdk_derived_profile_get_color_space (profile))
+    {
+    case GDK_COLOR_SPACE_HSL:
+      rgb_to_hsl (in, out);
+      break;
+    case GDK_COLOR_SPACE_HWB:
+      rgb_to_hwb (in, out);
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+}
+
diff --git a/gdk/gdkderivedprofile.h b/gdk/gdkderivedprofile.h
new file mode 100644
index 0000000000..015822027d
--- /dev/null
+++ b/gdk/gdkderivedprofile.h
@@ -0,0 +1,71 @@
+/* gdkderivedprofile.h
+ *
+ * Copyright 2021 (c) Red Hat, Inc.
+ *
+ * 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 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GDK_DERIVED_PROFILE_H__
+#define __GDK_DERIVED_PROFILE_H__
+
+#if !defined (__GDK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gdk/gdk.h> can be included directly."
+#endif
+
+#include <gdk/gdkversionmacros.h>
+#include <gdk/gdkcolorprofile.h>
+#include <gdk/gdkiccprofile.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_DERIVED_PROFILE (gdk_derived_profile_get_type ())
+
+#define GDK_DERIVED_PROFILE(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_DERIVED_PROFILE, 
GdkDerivedProfile))
+#define GDK_IS_DERIVED_PROFILE(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_DERIVED_PROFILE))
+#define GDK_DERIVED_PROFILE_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_DERIVED_PROFILE, 
GdkDerivedProfileClass))
+#define GDK_IS_DERIVED_PROFILE_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_DERIVED_PROFILE))
+#define GDK_DERIVED_PROFILE_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_DERIVED_PROFILE, 
GdkDerivedProfileClass))
+
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkDerivedProfile, g_object_unref)
+
+typedef struct _GdkDerivedProfileClass        GdkDerivedProfileClass;
+
+GDK_AVAILABLE_IN_4_6
+GType                        gdk_derived_profile_get_type  (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_4_6
+GdkColorProfile *            gdk_derived_profile_get_base_profile (GdkDerivedProfile *profile);
+
+typedef enum {
+  GDK_COLOR_SPACE_HSL,
+  GDK_COLOR_SPACE_HWB,
+} GdkColorSpace;
+
+GDK_AVAILABLE_IN_4_6
+GdkColorSpace                gdk_derived_profile_get_color_space (GdkDerivedProfile *profile);
+
+GDK_AVAILABLE_IN_4_6
+void                         gdk_derived_profile_convert_to_base_profile   (GdkDerivedProfile *profile,
+                                                                            const float       *in,
+                                                                            float             *out);
+
+GDK_AVAILABLE_IN_4_6
+void                         gdk_derived_profile_convert_from_base_profile (GdkDerivedProfile *profile,
+                                                                            const float       *in,
+                                                                            float             *out);
+
+G_END_DECLS
+
+#endif /* __GDK_DERIVED_PROFILE_H__ */
diff --git a/gdk/gdkiccprofile.c b/gdk/gdkiccprofile.c
new file mode 100644
index 0000000000..16089d37c2
--- /dev/null
+++ b/gdk/gdkiccprofile.c
@@ -0,0 +1,451 @@
+/* gdkiccprofile.c
+ *
+ * Copyright 2021 (c) Benjamin Otte
+ *
+ * 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 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * GdkICCProfile:
+ *
+ * `GdkICCProfile` is used to describe ICC color profiles.
+ *
+ * It is used to associate color profiles defined by the [International
+ * Color Consortium (ICC)](https://color.org/) with texture and color data.
+ *
+ * Each `GdkColorProfile` encapsulates an
+ * [ICC profile](https://en.wikipedia.org/wiki/ICC_profile). That profile
+ * can be queried via the using [property@Gdk.ColorProfile:icc-profile]
+ * property.
+ *
+ * `GdkICCProfile` objects are immutable and therefore threadsafe.
+ *
+ * Since: 4.6
+ */
+
+#include "config.h"
+
+#include "gdkiccprofileprivate.h"
+#include "gdkcolorprofileprivate.h"
+
+#include "gdkintl.h"
+
+struct _GdkICCProfile
+{
+  GdkColorProfile parent_instance;
+
+  GBytes *icc_profile;
+  cmsHPROFILE lcms_profile;
+};
+
+struct _GdkICCProfileClass
+{
+  GdkColorProfileClass parent_class;
+};
+
+enum {
+  PROP_0,
+  PROP_ICC_PROFILE,
+
+  N_PROPS
+};
+
+static GParamSpec *properties[N_PROPS];
+
+static gboolean
+gdk_icc_profile_real_init (GInitable     *initable,
+                           GCancellable  *cancellable,
+                           GError       **error)
+{
+  GdkICCProfile *self = GDK_ICC_PROFILE (initable);
+
+  if (self->lcms_profile == NULL)
+    {
+      const guchar *data;
+      gsize size;
+
+      if (self->icc_profile == NULL)
+        self->icc_profile = g_bytes_new (NULL, 0);
+
+      data = g_bytes_get_data (self->icc_profile, &size);
+
+      self->lcms_profile = cmsOpenProfileFromMem (data, size);
+      if (self->lcms_profile == NULL)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Failed to load ICC profile"));
+          return FALSE;
+        }
+    }
+
+  return TRUE;
+}
+
+static void
+gdk_icc_profile_initable_init (GInitableIface *iface)
+{
+  iface->init = gdk_icc_profile_real_init;
+}
+
+
+G_DEFINE_TYPE_WITH_CODE (GdkICCProfile, gdk_icc_profile, GDK_TYPE_COLOR_PROFILE,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gdk_icc_profile_initable_init))
+
+static void
+gdk_icc_profile_set_property (GObject      *gobject,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+  GdkICCProfile *self = GDK_ICC_PROFILE (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_ICC_PROFILE:
+      self->icc_profile = g_value_dup_boxed (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gdk_icc_profile_get_property (GObject    *gobject,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+  GdkICCProfile *self = GDK_ICC_PROFILE (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_ICC_PROFILE:
+      g_value_set_boxed (value, self->icc_profile);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gdk_icc_profile_dispose (GObject *object)
+{
+  GdkICCProfile *self = GDK_ICC_PROFILE (object);
+
+  g_clear_pointer (&self->icc_profile, g_bytes_unref);
+  g_clear_pointer (&self->lcms_profile, cmsCloseProfile);
+
+  G_OBJECT_CLASS (gdk_icc_profile_parent_class)->dispose (object);
+}
+
+static gboolean
+gdk_icc_profile_is_linear (GdkColorProfile *profile)
+{
+  return profile == gdk_color_profile_get_srgb_linear ();
+}
+
+static gsize
+gdk_icc_profile_get_n_components (GdkColorProfile *profile)
+{
+  GdkICCProfile *self = GDK_ICC_PROFILE (profile);
+
+  return cmsChannelsOf (cmsGetColorSpace (self->lcms_profile));
+}
+
+static gboolean
+gdk_icc_profile_equal (gconstpointer profile1,
+                         gconstpointer profile2)
+{
+  return g_bytes_equal (GDK_ICC_PROFILE (profile1)->icc_profile,
+                        GDK_ICC_PROFILE (profile2)->icc_profile);
+}
+
+static void
+gdk_icc_profile_class_init (GdkICCProfileClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GdkColorProfileClass *profile_class = GDK_COLOR_PROFILE_CLASS (klass);
+
+  gobject_class->set_property = gdk_icc_profile_set_property;
+  gobject_class->get_property = gdk_icc_profile_get_property;
+  gobject_class->dispose = gdk_icc_profile_dispose;
+
+  profile_class->is_linear = gdk_icc_profile_is_linear;
+  profile_class->get_n_components = gdk_icc_profile_get_n_components;
+  profile_class->equal = gdk_icc_profile_equal;
+
+  /**
+   * GdkICCProfile:icc-profile: (attributes org.gtk.Property.get=gdk_icc_profile_get_icc_profile)
+   *
+   * the ICC profile for this color profile
+   */
+  properties[PROP_ICC_PROFILE] =
+    g_param_spec_boxed ("icc-profile",
+                        P_("ICC profile"),
+                        P_("ICC profile for this color profile"),
+                        G_TYPE_BYTES,
+                        G_PARAM_READWRITE |
+                        G_PARAM_CONSTRUCT_ONLY |
+                        G_PARAM_STATIC_STRINGS |
+                        G_PARAM_EXPLICIT_NOTIFY);
+
+  g_object_class_install_properties (gobject_class, N_PROPS, properties);
+}
+
+static void
+gdk_icc_profile_init (GdkICCProfile *self)
+{
+}
+
+/**
+ * gdk_color_profile_get_srgb:
+ *
+ * Returns the color profile representing the sRGB color space.
+ *
+ * If you don't know anything about color profiles but need one for
+ * use with some function, this one is most likely the right one.
+ *
+ * Returns: (transfer none): the color profile for the sRGB
+ *   color space.
+ *
+ * Since: 4.6
+ */
+GdkColorProfile *
+gdk_color_profile_get_srgb (void)
+{
+  static GdkColorProfile *srgb_profile;
+
+  if (g_once_init_enter (&srgb_profile))
+    {
+      GdkColorProfile *new_profile;
+
+      new_profile = GDK_COLOR_PROFILE (gdk_icc_profile_new_from_lcms_profile (cmsCreate_sRGBProfile (), 
NULL));
+      g_assert (new_profile);
+
+      g_once_init_leave (&srgb_profile, new_profile);
+    }
+
+  return srgb_profile;
+}
+
+/*<private>
+ * gdk_color_profile_get_srgb_linear:
+ *
+ * Returns the linear color profile corresponding to the sRGB
+ * color space.
+ *
+ * It can display the same colors, but it does not have a gamma curve.
+ *
+ * Returns: (transfer none): the color profile for the linear sRGB
+ *   color space.
+ */
+GdkColorProfile *
+gdk_color_profile_get_srgb_linear (void)
+{
+  static GdkColorProfile *srgb_linear_profile;
+
+  if (g_once_init_enter (&srgb_linear_profile))
+    {
+      cmsToneCurve *curve;
+      cmsHPROFILE lcms_profile;
+      GdkColorProfile *new_profile;
+
+      curve = cmsBuildGamma (NULL, 1.0);
+      lcms_profile = cmsCreateRGBProfile (&(cmsCIExyY) {
+                                            0.3127, 0.3290, 1.0
+                                          },
+                                          &(cmsCIExyYTRIPLE) {
+                                            { 0.6400, 0.3300, 1.0 },
+                                            { 0.3000, 0.6000, 1.0 },
+                                            { 0.1500, 0.0600, 1.0 }
+                                          },
+                                          (cmsToneCurve*[3]) { curve, curve, curve });
+      cmsFreeToneCurve (curve);
+
+      new_profile = GDK_COLOR_PROFILE (gdk_icc_profile_new_from_lcms_profile (lcms_profile, NULL));
+      g_assert (new_profile);
+
+      g_once_init_leave (&srgb_linear_profile, new_profile);
+    }
+
+  return srgb_linear_profile;
+}
+
+/**
+ * gdk_icc_profile_new_from_icc_bytes:
+ * @bytes: The ICC profiles given as a `GBytes`
+ * @error: Return location for an error
+ *
+ * Creates a new color profile for the given ICC profile data.
+ *
+ * if the profile is not valid, %NULL is returned and an error
+ * is raised.
+ *
+ * Returns: a new `GdkColorProfile` or %NULL on error
+ *
+ * Since: 4.6
+ */
+GdkICCProfile *
+gdk_icc_profile_new_from_icc_bytes (GBytes  *bytes,
+                                    GError **error)
+{
+  g_return_val_if_fail (bytes != NULL, NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  return g_initable_new (GDK_TYPE_ICC_PROFILE,
+                         NULL,
+                         error,
+                         "icc-profile", bytes,
+                         NULL);
+}
+
+GdkICCProfile *
+gdk_icc_profile_new_from_lcms_profile (cmsHPROFILE   lcms_profile,
+                                       GError      **error)
+{
+  GdkICCProfile *result;
+  cmsUInt32Number size;
+  guchar *data;
+
+  size = 0;
+  if (!cmsSaveProfileToMem (lcms_profile, NULL, &size))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Could not prepare ICC profile"));
+      return NULL;
+    }
+
+  data = g_malloc (size);
+  if (!cmsSaveProfileToMem (lcms_profile, data, &size))
+    {
+      g_free (data);
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Failed to save ICC profile"));
+      return NULL;
+    }
+
+  result = g_object_new (GDK_TYPE_ICC_PROFILE, NULL);
+  result->lcms_profile = lcms_profile;
+  result->icc_profile = g_bytes_new_take (data, size);
+
+  return result;
+}
+
+/**
+ * gdk_icc_profile_get_icc_profile: (attributes org.gtk.Method.get_property=icc-profile)
+ * @self: a `GdkICCProfile`
+ *
+ * Returns the serialized ICC profile of @self as %GBytes.
+ *
+ * Returns: (transfer none): the ICC profile
+ *
+ * Since: 4.6
+ */
+GBytes *
+gdk_icc_profile_get_icc_profile (GdkICCProfile *self)
+{
+  g_return_val_if_fail (GDK_IS_ICC_PROFILE (self), NULL);
+
+  return self->icc_profile;
+}
+
+cmsHPROFILE *
+gdk_icc_profile_get_lcms_profile (GdkICCProfile *self)
+{
+  g_return_val_if_fail (GDK_IS_ICC_PROFILE (self), NULL);
+
+  return self->lcms_profile;
+}
+
+
+typedef struct _GdkICCTransformCache GdkICCTransformCache;
+
+struct _GdkICCTransformCache
+{
+  GdkICCProfile *source;
+  guint          source_type;
+  GdkICCProfile *dest;
+  guint          dest_type;
+};
+
+static void
+gdk_icc_transform_cache_free (gpointer data)
+{
+  g_free (data);
+}
+
+static guint
+gdk_icc_transform_cache_hash (gconstpointer data)
+{
+  const GdkICCTransformCache *cache = data;
+
+  return g_direct_hash (cache->source) ^
+         (g_direct_hash (cache->dest) >> 2) ^
+         ((cache->source_type << 16) | (cache->source_type >> 16)) ^
+         cache->dest_type;
+}
+
+static gboolean
+gdk_icc_transform_cache_equal (gconstpointer data1,
+                               gconstpointer data2)
+{
+  const GdkICCTransformCache *cache1 = data1;
+  const GdkICCTransformCache *cache2 = data2;
+
+  return cache1->source == cache2->source &&
+         cache1->source_type == cache2->source_type &&
+         cache1->dest == cache2->dest &&
+         cache1->dest_type == cache2->dest_type;
+}
+
+cmsHTRANSFORM *
+gdk_icc_profile_lookup_transform (GdkICCProfile *source,
+                                  guint          source_type,
+                                  GdkICCProfile *dest,
+                                  guint          dest_type)
+{
+  GdkICCTransformCache *entry;
+  static GHashTable *cache = NULL;
+  cmsHTRANSFORM *transform;
+
+  if (cache == NULL)
+    cache = g_hash_table_new_full (gdk_icc_transform_cache_hash,
+                                   gdk_icc_transform_cache_equal,
+                                   gdk_icc_transform_cache_free,
+                                   cmsDeleteTransform);
+
+  transform = g_hash_table_lookup (cache,
+                                   &(GdkICCTransformCache) {
+                                     source, source_type,
+                                     dest, dest_type
+                                   });
+  if (G_UNLIKELY (transform == NULL))
+    {
+      transform = cmsCreateTransform (gdk_icc_profile_get_lcms_profile (source),
+                                      source_type,
+                                      gdk_icc_profile_get_lcms_profile (dest),
+                                      dest_type,
+                                      INTENT_PERCEPTUAL,
+                                      cmsFLAGS_COPY_ALPHA);
+      entry = g_new (GdkICCTransformCache, 1);
+      *entry = (GdkICCTransformCache) {
+                 source, source_type,
+                 dest, dest_type
+               };
+      g_hash_table_insert (cache, entry, transform);
+    }
+
+  return transform;
+}
diff --git a/gdk/gdkiccprofile.h b/gdk/gdkiccprofile.h
new file mode 100644
index 0000000000..a3801e3cbd
--- /dev/null
+++ b/gdk/gdkiccprofile.h
@@ -0,0 +1,56 @@
+/* gdkiccprofile.h
+ *
+ * Copyright 2021 (c) Benjamin Otte
+ *
+ * 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 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GDK_ICC_PROFILE_H__
+#define __GDK_ICC_PROFILE_H__
+
+#if !defined (__GDK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gdk/gdk.h> can be included directly."
+#endif
+
+#include <gdk/gdkversionmacros.h>
+#include <gdk/gdkcolorprofile.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_ICC_PROFILE (gdk_icc_profile_get_type ())
+
+#define GDK_ICC_PROFILE(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_ICC_PROFILE, 
GdkICCProfile))
+#define GDK_IS_ICC_PROFILE(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_ICC_PROFILE))
+#define GDK_ICC_PROFILE_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_ICC_PROFILE, 
GdkICCProfileClass))
+#define GDK_IS_ICC_PROFILE_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_ICC_PROFILE))
+#define GDK_ICC_PROFILE_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_ICC_PROFILE, 
GdkICCProfileClass))
+
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkICCProfile, g_object_unref)
+
+typedef struct _GdkICCProfileClass        GdkICCProfileClass;
+
+GDK_AVAILABLE_IN_4_6
+GType                        gdk_icc_profile_get_type                   (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_4_6
+GdkICCProfile *              gdk_icc_profile_new_from_icc_bytes         (GBytes               *bytes,
+                                                                         GError              **error);
+
+GDK_AVAILABLE_IN_4_6
+GBytes *                     gdk_icc_profile_get_icc_profile            (GdkICCProfile      *self);
+
+G_END_DECLS
+
+#endif /* __GDK_ICC_PROFILE_H__ */
diff --git a/gdk/gdkiccprofileprivate.h b/gdk/gdkiccprofileprivate.h
new file mode 100644
index 0000000000..ba35535915
--- /dev/null
+++ b/gdk/gdkiccprofileprivate.h
@@ -0,0 +1,24 @@
+#ifndef __GDK_ICC_PROFILE_PRIVATE_H__
+#define __GDK_ICC_PROFILE_PRIVATE_H__
+
+#include "gdkiccprofile.h"
+#include "gdkmemorytexture.h"
+
+#include <lcms2.h>
+
+G_BEGIN_DECLS
+
+
+GdkICCProfile *              gdk_icc_profile_new_from_lcms_profile      (cmsHPROFILE         lcms_profile,
+                                                                         GError            **error);
+
+cmsHPROFILE *                gdk_icc_profile_get_lcms_profile           (GdkICCProfile      *self);
+
+cmsHTRANSFORM *              gdk_icc_profile_lookup_transform           (GdkICCProfile      *source,
+                                                                         guint               source_type,
+                                                                         GdkICCProfile      *dest,
+                                                                         guint               dest_type);
+
+G_END_DECLS
+
+#endif /* __GDK_COLOR_PROFILE_PRIVATE_H__ */
diff --git a/gdk/gdkmemoryformat.c b/gdk/gdkmemoryformat.c
index d7f080b842..1ad019b0f3 100644
--- a/gdk/gdkmemoryformat.c
+++ b/gdk/gdkmemoryformat.c
@@ -21,7 +21,7 @@
 
 #include "gdkmemoryformatprivate.h"
 
-#include "gdkcolorprofileprivate.h"
+#include "gdkiccprofileprivate.h"
 #include "gdkprofilerprivate.h"
 #include "gsk/gl/fp16private.h"
 
@@ -847,10 +847,13 @@ gdk_memory_convert_transform (guchar              *dest_data,
   guchar *src_tmp, *dest_tmp;
   gsize y;
 
-  transform = gdk_color_profile_lookup_transform (src_profile,
-                                                  src_desc->lcms.type,
-                                                  dest_profile,
-                                                  dest_desc->lcms.type);
+  g_assert (GDK_IS_ICC_PROFILE (src_profile));
+  g_assert (GDK_IS_ICC_PROFILE (dest_profile));
+
+  transform = gdk_icc_profile_lookup_transform (GDK_ICC_PROFILE (src_profile),
+                                                src_desc->lcms.type,
+                                                GDK_ICC_PROFILE (dest_profile),
+                                                dest_desc->lcms.type);
 
   if (src_desc->to_lcms)
     src_tmp = g_malloc_n (src_desc->lcms.bpp, width);
diff --git a/gdk/gdkmemorytexture.c b/gdk/gdkmemorytexture.c
index e439ca2d99..d1dade128e 100644
--- a/gdk/gdkmemorytexture.c
+++ b/gdk/gdkmemorytexture.c
@@ -21,7 +21,7 @@
 
 #include "gdkmemorytextureprivate.h"
 
-#include "gdkcolorprofileprivate.h"
+#include "gdkiccprofileprivate.h"
 #include "gdkmemoryformatprivate.h"
 #include "gsk/gl/fp16private.h"
 
@@ -196,7 +196,7 @@ gdk_memory_texture_new_with_color_profile (int              width,
   g_return_val_if_fail (GDK_IS_COLOR_PROFILE (profile), NULL);
   g_return_val_if_fail (bytes != NULL, NULL);
   g_return_val_if_fail (stride >= width * gdk_memory_format_bytes_per_pixel (format), NULL);
-  g_return_val_if_fail (gdk_color_profile_supports_memory_format (profile, format), NULL);
+  //g_return_val_if_fail (gdk_color_profile_supports_memory_format (profile, format), NULL);
 
   bytes = gdk_memory_sanitize (bytes, width, height, format, stride, &stride);
 
diff --git a/gdk/gdktexture.c b/gdk/gdktexture.c
index c0eb3345e3..7246243a99 100644
--- a/gdk/gdktexture.c
+++ b/gdk/gdktexture.c
@@ -40,7 +40,7 @@
 #include "gdktextureprivate.h"
 
 #include "gdkcairo.h"
-#include "gdkcolorprofile.h"
+#include "gdkiccprofile.h"
 #include "gdkintl.h"
 #include "gdkmemoryformatprivate.h"
 #include "gdkmemorytextureprivate.h"
@@ -426,7 +426,7 @@ gdk_color_profile_from_pixbuf (GdkPixbuf *pixbuf)
 
       icc_data = g_base64_decode (icc_profile_base64, &icc_len);
       bytes = g_bytes_new_take (icc_data, icc_len);
-      profile = gdk_color_profile_new_from_icc_bytes (bytes, NULL);
+      profile = GDK_COLOR_PROFILE (gdk_icc_profile_new_from_icc_bytes (bytes, NULL));
       g_bytes_unref (bytes);
     }
   if (!profile)
diff --git a/gdk/gdktypes.h b/gdk/gdktypes.h
index 2ff27a8d2d..bb6ef85f25 100644
--- a/gdk/gdktypes.h
+++ b/gdk/gdktypes.h
@@ -74,6 +74,8 @@ typedef cairo_rectangle_int_t         GdkRectangle;
 /* Forward declarations of commonly used types */
 typedef struct _GdkRGBA               GdkRGBA;
 typedef struct _GdkColorProfile       GdkColorProfile;
+typedef struct _GdkICCProfile         GdkICCProfile;
+typedef struct _GdkDerivedProfile     GdkDerivedProfile;
 typedef struct _GdkContentFormats     GdkContentFormats;
 typedef struct _GdkContentProvider    GdkContentProvider;
 typedef struct _GdkCursor             GdkCursor;
diff --git a/gdk/loaders/gdkjpeg.c b/gdk/loaders/gdkjpeg.c
index 1ef0630dcf..de4e096d65 100644
--- a/gdk/loaders/gdkjpeg.c
+++ b/gdk/loaders/gdkjpeg.c
@@ -21,7 +21,7 @@
 
 #include "gdkjpegprivate.h"
 
-#include "gdkcolorprofile.h"
+#include "gdkiccprofile.h"
 #include "gdkintl.h"
 #include "gdkmemorytextureprivate.h"
 #include "gdktexture.h"
@@ -232,7 +232,7 @@ gdk_load_jpeg (GBytes  *input_bytes,
   if (jpeg_read_icc_profile (&info, &icc_data, &icc_len))
     {
       GBytes *icc_bytes = g_bytes_new_with_free_func (icc_data, icc_len, free, icc_data);
-      color_profile = gdk_color_profile_new_from_icc_bytes (icc_bytes, error);
+      color_profile = GDK_COLOR_PROFILE (gdk_icc_profile_new_from_icc_bytes (icc_bytes, error));
       g_bytes_unref (icc_bytes);
     }
   else
@@ -315,11 +315,15 @@ gdk_save_jpeg (GdkTexture *texture)
 
   jpeg_start_compress (&info, TRUE);
 
-  bytes = gdk_color_profile_get_icc_profile (color_profile);
-  jpeg_write_icc_profile (&info,
-                          g_bytes_get_data (bytes, NULL),
-                          g_bytes_get_size (bytes));
-  g_bytes_unref (bytes);
+  /* FIXME handle cmyk ? */
+  if (GDK_IS_ICC_PROFILE (color_profile))
+    {
+      bytes = gdk_icc_profile_get_icc_profile (GDK_ICC_PROFILE (color_profile));
+      jpeg_write_icc_profile (&info,
+                              g_bytes_get_data (bytes, NULL),
+                              g_bytes_get_size (bytes));
+      g_bytes_unref (bytes);
+    }
 
   while (info.next_scanline < info.image_height)
     {
diff --git a/gdk/loaders/gdkpng.c b/gdk/loaders/gdkpng.c
index 8678c5bf4d..01d67e34c1 100644
--- a/gdk/loaders/gdkpng.c
+++ b/gdk/loaders/gdkpng.c
@@ -19,7 +19,7 @@
 
 #include "gdkpngprivate.h"
 
-#include "gdkcolorprofileprivate.h"
+#include "gdkiccprofileprivate.h"
 #include "gdkintl.h"
 #include "gdkmemoryformatprivate.h"
 #include "gdkmemorytextureprivate.h"
@@ -147,7 +147,7 @@ gdk_png_get_color_profile (png_struct *png,
     {
       GBytes *bytes = g_bytes_new (icc_data, icc_len);
 
-      profile = gdk_color_profile_new_from_icc_bytes (bytes, NULL);
+      profile = GDK_COLOR_PROFILE (gdk_icc_profile_new_from_icc_bytes (bytes, NULL));
       g_bytes_unref (bytes);
       if (profile)
         return profile;
@@ -191,7 +191,7 @@ gdk_png_get_color_profile (png_struct *png,
   lcms_profile = cmsCreateRGBProfile (&whitepoint,
                                       &primaries,
                                       (cmsToneCurve*[3]) { curve, curve, curve });
-  profile = gdk_color_profile_new_from_lcms_profile (lcms_profile, NULL);
+  profile = GDK_COLOR_PROFILE (gdk_icc_profile_new_from_lcms_profile (lcms_profile, NULL));
   /* FIXME: errors? */
   if (profile == NULL)
     profile = g_object_ref (gdk_color_profile_get_srgb ());
@@ -206,20 +206,23 @@ gdk_png_set_color_profile (png_struct      *png,
                            GdkColorProfile *profile)
 {
   /* FIXME: allow deconstructing RGB profiles into gAMA and cHRM instead of
-   * falling back to iCCP */
+   * falling back to iCCP
+   */
   if (profile == gdk_color_profile_get_srgb ())
     {
       png_set_sRGB_gAMA_and_cHRM (png, info, /* FIXME */ PNG_sRGB_INTENT_PERCEPTUAL);
     }
-  else
+  else if (GDK_IS_ICC_PROFILE (profile))
     {
-      GBytes *bytes = gdk_color_profile_get_icc_profile (profile);
+      GBytes *bytes = gdk_icc_profile_get_icc_profile (GDK_ICC_PROFILE (profile));
       png_set_iCCP (png, info,
                     "ICC profile",
                     0,
                     g_bytes_get_data (bytes, NULL),
                     g_bytes_get_size (bytes));
     }
+  else
+    g_assert_not_reached ();
 }
 
  /* }}} */
diff --git a/gdk/loaders/gdktiff.c b/gdk/loaders/gdktiff.c
index 55012c41e6..0e3348609f 100644
--- a/gdk/loaders/gdktiff.c
+++ b/gdk/loaders/gdktiff.c
@@ -19,6 +19,7 @@
 
 #include "gdktiffprivate.h"
 
+#include "gdkiccprofileprivate.h"
 #include "gdkintl.h"
 #include "gdkmemoryformatprivate.h"
 #include "gdkmemorytextureprivate.h"
@@ -235,7 +236,7 @@ gdk_tiff_get_color_profile (TIFF *tiff)
       GdkColorProfile *profile;
 
       icc_bytes = g_bytes_new (icc_data, icc_len);
-      profile = gdk_color_profile_new_from_icc_bytes (icc_bytes, NULL);
+      profile = GDK_COLOR_PROFILE (gdk_icc_profile_new_from_icc_bytes (icc_bytes, NULL));
       g_bytes_unref (icc_bytes);
 
       if (profile)
@@ -246,10 +247,10 @@ gdk_tiff_get_color_profile (TIFF *tiff)
 }
 
 static void
-gdk_tiff_set_color_profile (TIFF            *tiff,
-                            GdkColorProfile *profile)
+gdk_tiff_set_icc_profile (TIFF          *tiff,
+                          GdkICCProfile *profile)
 {
-  GBytes *bytes = gdk_color_profile_get_icc_profile (profile);
+  GBytes *bytes = gdk_icc_profile_get_icc_profile (profile);
 
   TIFFSetField (tiff, TIFFTAG_ICCPROFILE,
                 g_bytes_get_size (bytes),
@@ -332,7 +333,8 @@ gdk_save_tiff (GdkTexture *texture)
   TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
   TIFFSetField (tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
 
-  gdk_tiff_set_color_profile (tif, color_profile);
+  if (GDK_IS_ICC_PROFILE (color_profile))
+    gdk_tiff_set_icc_profile (tif, GDK_ICC_PROFILE (color_profile));
 
   memtex = gdk_memory_texture_from_texture (texture, fdata->format, color_profile);
   data = gdk_memory_texture_get_data (memtex);
diff --git a/gdk/meson.build b/gdk/meson.build
index a9e5ab1cc1..947c991433 100644
--- a/gdk/meson.build
+++ b/gdk/meson.build
@@ -11,6 +11,7 @@ gdk_public_sources = files([
   'gdkcontentproviderimpl.c',
   'gdkcontentserializer.c',
   'gdkcursor.c',
+  'gdkderivedprofile.c',
   'gdkdevice.c',
   'gdkdevicepad.c',
   'gdkdevicetool.c',
@@ -29,6 +30,7 @@ gdk_public_sources = files([
   'gdkglobals.c',
   'gdkgltexture.c',
   'gdkhsla.c',
+  'gdkiccprofile.c',
   'gdkkeys.c',
   'gdkkeyuni.c',
   'gdkmemoryformat.c',
@@ -72,6 +74,7 @@ gdk_public_headers = files([
   'gdkcontentproviderimpl.h',
   'gdkcontentserializer.h',
   'gdkcursor.h',
+  'gdkderivedprofile.h',
   'gdkdevice.h',
   'gdkdevicepad.h',
   'gdkdevicetool.h',
@@ -86,6 +89,7 @@ gdk_public_headers = files([
   'gdkframetimings.h',
   'gdkglcontext.h',
   'gdkgltexture.h',
+  'gdkiccprofile.h',
   'gdkkeys.h',
   'gdkkeysyms.h',
   'gdkmemorytexture.h',
diff --git a/gdk/x11/gdkdisplay-x11.c b/gdk/x11/gdkdisplay-x11.c
index 0d8e126a8a..858218889e 100644
--- a/gdk/x11/gdkdisplay-x11.c
+++ b/gdk/x11/gdkdisplay-x11.c
@@ -1,6 +1,6 @@
 /* GDK - The GIMP Drawing Kit
  * gdkdisplay-x11.c
- * 
+ *
  * Copyright 2001 Sun Microsystems Inc.
  * Copyright (C) 2004 Nokia Corporation
  *
@@ -895,7 +895,7 @@ gdk_x11_display_translate_event (GdkEventTranslator *translator,
        }
 #endif
 
-    if (surface && 
+    if (surface &&
        xevent->xconfigure.event == xevent->xconfigure.window)
         {
           int x, y;
@@ -1463,7 +1463,7 @@ gdk_x11_display_check_color_profile (GdkX11Display *self)
     }
 
   g_clear_object (&self->color_profile);
-  self->color_profile = gdk_color_profile_new_from_icc_bytes (bytes, NULL);
+  self->color_profile = GDK_COLOR_PROFILE (gdk_icc_profile_new_from_icc_bytes (bytes, NULL));
   if (!self->color_profile)
     self->color_profile = g_object_ref (gdk_color_profile_get_srgb ());
 }
@@ -1532,7 +1532,7 @@ gdk_x11_display_open (const char *display_name)
   }
 #endif
 
-  /* initialize the display's screens */ 
+  /* initialize the display's screens */
   display_x11->screen = _gdk_x11_screen_new (display, DefaultScreen (display_x11->xdisplay));
 
   /* We want this for the leader surface already */
@@ -1552,8 +1552,8 @@ gdk_x11_display_open (const char *display_name)
     }
 
 #ifdef HAVE_XFIXES
-  if (XFixesQueryExtension (display_x11->xdisplay, 
-                           &display_x11->xfixes_event_base, 
+  if (XFixesQueryExtension (display_x11->xdisplay,
+                           &display_x11->xfixes_event_base,
                            &ignore))
     {
       display_x11->have_xfixes = TRUE;
@@ -1627,7 +1627,7 @@ gdk_x11_display_open (const char *display_name)
 
   /* We don't yet know a valid time. */
   display_x11->user_time = 0;
-  
+
 #ifdef HAVE_XKB
   {
     int xkb_major = XkbMajorVersion;
@@ -1636,13 +1636,13 @@ gdk_x11_display_open (const char *display_name)
       {
         xkb_major = XkbMajorVersion;
         xkb_minor = XkbMinorVersion;
-           
-        if (XkbQueryExtension (display_x11->xdisplay, 
+
+        if (XkbQueryExtension (display_x11->xdisplay,
                               NULL, &display_x11->xkb_event_type, NULL,
                                &xkb_major, &xkb_minor))
           {
            Bool detectable_autorepeat_supported;
-           
+
            display_x11->use_xkb = TRUE;
 
             XkbSelectEvents (display_x11->xdisplay,
@@ -1663,7 +1663,7 @@ gdk_x11_display_open (const char *display_name)
            GDK_NOTE (MISC, g_message ("Detectable autorepeat %s.",
                                       detectable_autorepeat_supported ?
                                       "supported" : "not supported"));
-           
+
            display_x11->have_xkb_autorepeat = detectable_autorepeat_supported;
           }
       }
@@ -1675,7 +1675,7 @@ gdk_x11_display_open (const char *display_name)
   {
     int major, minor;
     int error_base, event_base;
-    
+
     if (XSyncQueryExtension (display_x11->xdisplay,
                             &event_base, &error_base) &&
         XSyncInitialize (display_x11->xdisplay,
@@ -1781,16 +1781,16 @@ gdk_add_connection_handler (Display *display,
 
   connection->fd = fd;
   connection->display = display;
-  
+
   io_channel = g_io_channel_unix_new (fd);
-  
+
   connection->source = g_io_create_watch (io_channel, G_IO_IN);
   g_source_set_callback (connection->source,
                         (GSourceFunc)process_internal_connection, connection, NULL);
   g_source_attach (connection->source, NULL);
-  
+
   g_io_channel_unref (io_channel);
-  
+
   return connection;
 }
 
@@ -1945,11 +1945,11 @@ void
 gdk_x11_display_grab (GdkDisplay *display)
 {
   GdkX11Display *display_x11;
-  
+
   g_return_if_fail (GDK_IS_DISPLAY (display));
-  
+
   display_x11 = GDK_X11_DISPLAY (display);
-  
+
   if (display_x11->grab_count == 0)
     XGrabServer (display_x11->xdisplay);
   display_x11->grab_count++;
@@ -1966,12 +1966,12 @@ void
 gdk_x11_display_ungrab (GdkDisplay *display)
 {
   GdkX11Display *display_x11;
-  
+
   g_return_if_fail (GDK_IS_DISPLAY (display));
-  
+
   display_x11 = GDK_X11_DISPLAY (display);;
   g_return_if_fail (display_x11->grab_count > 0);
-  
+
   display_x11->grab_count--;
   if (display_x11->grab_count == 0)
     {
@@ -2156,7 +2156,7 @@ broadcast_xmessage (GdkDisplay *display,
 {
   Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
   Window xroot_window = GDK_DISPLAY_XROOTWIN (display);
-  
+
   Atom type_atom;
   Atom type_atom_begin;
   Window xwindow;
@@ -2184,14 +2184,14 @@ broadcast_xmessage (GdkDisplay *display,
 
   type_atom = gdk_x11_get_xatom_by_name_for_display (display, message_type);
   type_atom_begin = gdk_x11_get_xatom_by_name_for_display (display, message_type_begin);
-  
+
   {
     XClientMessageEvent xclient;
     const char *src;
     const char *src_end;
     char *dest;
     char *dest_end;
-    
+
     memset(&xclient, 0, sizeof (xclient));
     xclient.type = ClientMessage;
     xclient.message_type = type_atom_begin;
@@ -2201,12 +2201,12 @@ broadcast_xmessage (GdkDisplay *display,
 
     src = message;
     src_end = message + strlen (message) + 1; /* +1 to include nul byte */
-    
+
     while (src != src_end)
       {
         dest = &xclient.data.b[0];
         dest_end = dest + 20;
-        
+
         while (dest != dest_end &&
                src != src_end)
           {
@@ -2220,7 +2220,7 @@ broadcast_xmessage (GdkDisplay *display,
            *dest = 0;
            ++dest;
          }
-        
+
         XSendEvent (xdisplay,
                     xroot_window,
                     False,
@@ -2245,7 +2245,7 @@ broadcast_xmessage (GdkDisplay *display,
  * skipped in the output.)
  *
  * Sends a startup notification message of type @message_type to
- * @display. 
+ * @display.
  *
  * This is a convenience function for use by code that implements the
  * freedesktop startup notification specification. Applications should
@@ -2336,7 +2336,7 @@ gdk_x11_display_request_selection_notification (GdkDisplay *display,
   if (display_x11->have_xfixes)
     {
       atom = gdk_x11_get_xatom_by_name_for_display (display, selection);
-      XFixesSelectSelectionInput (display_x11->xdisplay, 
+      XFixesSelectSelectionInput (display_x11->xdisplay,
                                  display_x11->leader_window,
                                  atom,
                                  XFixesSetSelectionOwnerNotifyMask |
@@ -2371,7 +2371,7 @@ gdk_x11_display_get_user_time (GdkDisplay *display)
  * @display: (type GdkX11Display): a `GdkDisplay`
  *
  * Gets the startup notification ID for a display.
- * 
+ *
  * Returns: the startup notification ID for @display
  */
 const char *
@@ -2788,7 +2788,7 @@ gdk_x11_set_sm_client_id (const char *sm_client_id)
 
   g_slist_free (displays);
 }
- 
+
 gsize
 gdk_x11_display_get_max_request_size (GdkDisplay *display)
 {
@@ -2798,7 +2798,7 @@ gdk_x11_display_get_max_request_size (GdkDisplay *display)
   size = XExtendedMaxRequestSize (xdisplay);
   if (size <= 0)
     size = XMaxRequestSize (xdisplay);
-  
+
   size = MIN (262144, size - 100);
   return size;
 }
@@ -2972,7 +2972,7 @@ gdk_x11_display_get_visual_info_for_visual (GdkX11Display  *self,
                             &template,
                             &nvisuals);
   g_warn_if_fail (nvisuals == 1);
-  
+
   return visinfo;
 }
 
@@ -3026,7 +3026,7 @@ gdk_x11_display_init_gl_backend (GdkX11Display  *self,
   int visualid;
 
   /* No env vars set, do the regular GL initialization.
-   * 
+   *
    * We try EGL first, but are very picky about what we accept.
    * If that fails, we try to go with GLX instead.
    * And if that also fails, we try EGL again, but this time accept anything.
@@ -3042,7 +3042,7 @@ gdk_x11_display_init_gl_backend (GdkX11Display  *self,
 
       if (gdk_x11_display_init_glx (self, out_visual, out_depth, error))
         return TRUE;
-      
+
       g_clear_error (error);
       if (!gdk_display_init_egl (display, EGL_PLATFORM_X11_KHR, dpy, TRUE, error))
         return FALSE;


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