[gtk/wip/otte/color-profiles: 40/40] gdk: Introduce GdkColor




commit 75675a4c792329f5631d785ef69e482fc215d431
Author: Benjamin Otte <otte redhat com>
Date:   Mon Sep 27 06:03:14 2021 +0200

    gdk: Introduce GdkColor
    
    GdkColor represents a color in the real world, by combining a color
    profile, an alpha valueand N component values.
    
    gsk_render_node_draw() has been ported to use GdkColor when rendering,
    which makes it so the rendering happens in a color-managed way when a
    ColorProfile has been attached to the target.

 gdk/gdkcairo.c            |  16 ++++--
 gdk/gdkcolor.c            | 122 ++++++++++++++++++++++++++++++++++++++++++++++
 gdk/gdkcolorprivate.h     |  73 +++++++++++++++++++++++++++
 gdk/gdkcolorprivateimpl.h |  97 ++++++++++++++++++++++++++++++++++++
 gdk/gdkcolorprofile.c     |  19 ++++++++
 gdk/gdkcolorprofile.h     |   3 ++
 gdk/meson.build           |   1 +
 gsk/gskcairorenderer.c    | 101 ++++++++++++++++++++++++--------------
 gsk/gskrendernodeimpl.c   | 121 ++++++++++++++++++++++++++-------------------
 9 files changed, 462 insertions(+), 91 deletions(-)
---
diff --git a/gdk/gdkcairo.c b/gdk/gdkcairo.c
index 1f1eb58a61..e599616f08 100644
--- a/gdk/gdkcairo.c
+++ b/gdk/gdkcairo.c
@@ -19,7 +19,9 @@
 
 #include "gdkcairoprivate.h"
 
+#include "gdkcolorprivate.h"
 #include "gdkcolorprofile.h"
+#include "gdkmemoryformatprivate.h"
 
 #include <math.h>
 
@@ -34,14 +36,20 @@ void
 gdk_cairo_set_source_rgba (cairo_t       *cr,
                            const GdkRGBA *rgba)
 {
+  GdkColor color;
+  const float *components;
+
   g_return_if_fail (cr != NULL);
   g_return_if_fail (rgba != NULL);
 
+  gdk_color_convert_rgba (&color, gdk_cairo_get_color_profile (cr), rgba);
+  components = gdk_color_get_components (&color);
   cairo_set_source_rgba (cr,
-                         rgba->red,
-                         rgba->green,
-                         rgba->blue,
-                         rgba->alpha);
+                         components[0],
+                         components[1],
+                         components[2],
+                         gdk_color_get_alpha (&color));
+  gdk_color_finish (&color);
 }
 
 /**
diff --git a/gdk/gdkcolor.c b/gdk/gdkcolor.c
new file mode 100644
index 0000000000..ac5f3266c3
--- /dev/null
+++ b/gdk/gdkcolor.c
@@ -0,0 +1,122 @@
+/* GDK - The GIMP Drawing Kit
+ *
+ * Copyright (C) 2021 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/>.
+ */
+
+#include "config.h"
+
+#include "gdkcolorprivate.h"
+
+#include "gdkcolorprofileprivate.h"
+
+static inline cmsHTRANSFORM *
+gdk_color_get_transform (GdkColorProfile *src,
+                         GdkColorProfile *dest)
+{
+  cmsHPROFILE *src_profile, *dest_profile;
+
+  src_profile = gdk_color_profile_get_lcms_profile (src);
+  dest_profile = gdk_color_profile_get_lcms_profile (dest);
+
+  return gdk_color_profile_lookup_transform (src,
+                                             cmsFormatterForColorspaceOfProfile (src_profile, 4, 1),
+                                             dest,
+                                             cmsFormatterForColorspaceOfProfile (dest_profile, 4, 1));
+}
+
+void
+gdk_color_convert (GdkColor        *self,
+                   GdkColorProfile *profile,
+                   const GdkColor  *other)
+{
+  gdk_color_init (self,
+                  profile,
+                  other->alpha,
+                  NULL,
+                  gdk_color_profile_get_n_components (profile));
+
+  cmsDoTransform (gdk_color_get_transform (other->profile, profile),
+                  gdk_color_get_components (other),
+                  (float *) gdk_color_get_components (self),
+                  1);
+}
+
+void
+gdk_color_convert_rgba (GdkColor        *self,
+                        GdkColorProfile *profile,
+                        const GdkRGBA   *rgba)
+{
+  gdk_color_init (self,
+                  profile,
+                  rgba->alpha,
+                  NULL,
+                  gdk_color_profile_get_n_components (profile));
+
+  cmsDoTransform (gdk_color_get_transform (gdk_color_profile_get_srgb (), profile),
+                  (float[3]) { rgba->red, rgba->green, rgba->blue },
+                  (float *) gdk_color_get_components (self),
+                  1);
+}
+
+void
+gdk_color_mix (GdkColor        *self,
+               GdkColorProfile *color_profile,
+               const GdkColor  *src1,
+               const GdkColor  *src2,
+               double           progress)
+{
+  if (src1->profile != color_profile)
+    {
+      GdkColor tmp;
+      gdk_color_convert (&tmp, color_profile, src1);
+      gdk_color_mix (self, color_profile, &tmp, src2, progress);
+    }
+  else if (src2->profile != color_profile)
+    {
+      GdkColor tmp;
+      gdk_color_convert (&tmp, color_profile, src2);
+      gdk_color_mix (self, color_profile, src1, &tmp, progress);
+    }
+  else
+    {
+      gsize i, n;
+      const float *s1, *s2;
+      float *d;
+
+      n = gdk_color_profile_get_n_components (color_profile);
+
+      gdk_color_init (self,
+                      color_profile, 
+                      src1->alpha * (1.0 - progress) + src2->alpha * progress,
+                      NULL, n);
+
+      d = (float *) gdk_color_get_components (self);
+      s1 = gdk_color_get_components (src1);
+      s2 = gdk_color_get_components (src2);
+
+      if (self->alpha == 0)
+        {
+          for (i = 0; i < n; i++)
+            d[i] = s1[i] * (1.0 - progress) + s2[i] * progress;
+        }
+      else
+        {
+          for (i = 0; i < n; i++)
+            d[i] = (s1[i] * src1->alpha * (1.0 - progress) + s2[i] * src2->alpha * progress) / self->alpha;
+        }
+    }
+}
+
diff --git a/gdk/gdkcolorprivate.h b/gdk/gdkcolorprivate.h
new file mode 100644
index 0000000000..9efe9187b2
--- /dev/null
+++ b/gdk/gdkcolorprivate.h
@@ -0,0 +1,73 @@
+/* GDK - The GIMP Drawing Kit
+ *
+ * Copyright (C) 2021 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_COLOR_PRIVATE_H__
+#define __GDK_COLOR_PRIVATE_H__
+
+#include <gdk/gdkcolorprofile.h>
+#include <gdk/gdkrgba.h>
+
+/* RGB - and it makes the struct size a multiple of 8 bytes, ie pointer-aligned */
+#define GDK_COLOR_MAX_NATIVE_COMPONENTS 3
+
+typedef struct _GdkColor GdkColor;
+
+struct _GdkColor
+{
+  GdkColorProfile *profile;
+  float alpha;
+  union {
+    float values[GDK_COLOR_MAX_NATIVE_COMPONENTS];
+    float *components;
+  };
+};
+
+G_STATIC_ASSERT (sizeof (GdkColor) % sizeof (gpointer) == 0);
+
+static inline void              gdk_color_init                          (GdkColor               *self,
+                                                                         GdkColorProfile        *profile,
+                                                                         float                   alpha,
+                                                                         float                  *components,
+                                                                         gsize                   
n_components);
+static inline void              gdk_color_init_from_rgba                (GdkColor               *self,
+                                                                         const GdkRGBA          *rgba);
+static inline void              gdk_color_finish                        (GdkColor               *self);
+
+void                            gdk_color_convert                       (GdkColor               *self,
+                                                                         GdkColorProfile        *profile,
+                                                                         const GdkColor         *other);
+void                            gdk_color_convert_rgba                  (GdkColor               *self,
+                                                                         GdkColorProfile        *profile,
+                                                                         const GdkRGBA          *rgba);
+
+void                            gdk_color_mix                           (GdkColor               *self,
+                                                                         GdkColorProfile        
*color_profile,
+                                                                         const GdkColor         *src1,
+                                                                         const GdkColor         *src2,
+                                                                         double                  progress);
+
+static inline GdkColorProfile * gdk_color_get_color_profile             (const GdkColor         *self);
+static inline float             gdk_color_get_alpha                     (const GdkColor         *self);
+static inline const float *     gdk_color_get_components                (const GdkColor         *self);
+static inline gsize             gdk_color_get_n_components              (const GdkColor         *self);
+
+#include "gdkcolorprivateimpl.h"
+
+G_END_DECLS
+
+#endif
diff --git a/gdk/gdkcolorprivateimpl.h b/gdk/gdkcolorprivateimpl.h
new file mode 100644
index 0000000000..3eada32eb8
--- /dev/null
+++ b/gdk/gdkcolorprivateimpl.h
@@ -0,0 +1,97 @@
+/* GDK - The GIMP Drawing Kit
+ *
+ * Copyright (C) 2021 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/>.
+ */
+
+static inline gboolean
+gdk_color_is_allocated (const GdkColor *self)
+{
+  return GPOINTER_TO_SIZE (self->profile) & 1;
+}
+
+static inline void
+gdk_color_init (GdkColor        *self,
+                GdkColorProfile *profile,
+                float            alpha,
+                float           *components,
+                gsize            n_components)
+{
+  gboolean allocated = n_components > GDK_COLOR_MAX_NATIVE_COMPONENTS;
+
+  g_assert (n_components == gdk_color_profile_get_n_components (profile));
+
+  self->profile = GSIZE_TO_POINTER (GPOINTER_TO_SIZE (g_object_ref (profile)) | allocated);
+  self->alpha = alpha;
+  if (allocated)
+    {
+      if (components)
+        self->components = g_memdup (components, sizeof (float) * n_components);
+      else
+        self->components = g_new (float, n_components);
+    }
+  else
+    {
+      if (components)
+        memcpy (self->values, components, sizeof (float) * n_components);
+    }
+}
+
+static inline void
+gdk_color_init_from_rgba (GdkColor      *self,
+                          const GdkRGBA *rgba)
+{
+  gdk_color_init (self,
+                  gdk_color_profile_get_srgb(),
+                  rgba->alpha,
+                  (float[3]) { rgba->red, rgba->green, rgba->blue },
+                  3);
+}
+
+static inline void
+gdk_color_finish (GdkColor *self)
+{
+  if (gdk_color_is_allocated (self))
+    g_free (self->components);
+
+  g_object_unref (gdk_color_get_color_profile (self));
+}
+
+static inline GdkColorProfile *
+gdk_color_get_color_profile (const GdkColor *self)
+{
+  return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (self->profile) & ~1); 
+}
+
+static inline float
+gdk_color_get_alpha (const GdkColor *self)
+{
+  return self->alpha;
+}
+
+static inline const float *
+gdk_color_get_components (const GdkColor *self)
+{
+  if (gdk_color_is_allocated (self))
+    return self->components;
+  else
+    return self->values;
+}
+
+static inline gsize
+gdk_color_get_n_components (const GdkColor *self)
+{
+  return gdk_color_profile_get_n_components (gdk_color_get_color_profile (self));
+}
diff --git a/gdk/gdkcolorprofile.c b/gdk/gdkcolorprofile.c
index 6c398f7811..1120009965 100644
--- a/gdk/gdkcolorprofile.c
+++ b/gdk/gdkcolorprofile.c
@@ -368,6 +368,25 @@ gdk_color_profile_is_linear (GdkColorProfile *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
+ **/
+gsize
+gdk_color_profile_get_n_components (GdkColorProfile *self)
+{
+  g_return_val_if_fail (GDK_IS_COLOR_PROFILE (self), 3);
+
+  return cmsChannelsOf (cmsGetColorSpace (self->lcms_profile));
+}
+
 /**
  * gdk_color_profile_equal:
  * @profile1: (type GdkColorProfile): a `GdkColorProfile`
diff --git a/gdk/gdkcolorprofile.h b/gdk/gdkcolorprofile.h
index 189363f7f2..6cd5329694 100644
--- a/gdk/gdkcolorprofile.h
+++ b/gdk/gdkcolorprofile.h
@@ -56,6 +56,9 @@ GBytes *                     gdk_color_profile_get_icc_profile            (GdkCo
 
 GDK_AVAILABLE_IN_4_6
 gboolean                     gdk_color_profile_is_linear                  (GdkColorProfile      *self) 
G_GNUC_PURE;
+GDK_AVAILABLE_IN_4_6
+gsize                        gdk_color_profile_get_n_components           (GdkColorProfile      *self) 
G_GNUC_PURE;
+
 GDK_AVAILABLE_IN_4_6
 gboolean                     gdk_color_profile_equal                      (gconstpointer         profile1,
                                                                            gconstpointer         profile2);
diff --git a/gdk/meson.build b/gdk/meson.build
index ba059f2cb8..3cabde2d08 100644
--- a/gdk/meson.build
+++ b/gdk/meson.build
@@ -4,6 +4,7 @@ gdk_public_sources = files([
   'gdkcairo.c',
   'gdkcairocontext.c',
   'gdkclipboard.c',
+  'gdkcolor.c',
   'gdkcolorprofile.c',
   'gdkcontentdeserializer.c',
   'gdkcontentformats.c',
diff --git a/gsk/gskcairorenderer.c b/gsk/gskcairorenderer.c
index 34625c66a8..e0e24ad5be 100644
--- a/gsk/gskcairorenderer.c
+++ b/gsk/gskcairorenderer.c
@@ -27,6 +27,7 @@
 #include "gskrendernodeprivate.h"
 
 #include "gdk/gdkcolorprofileprivate.h"
+#include "gdk/gdkmemorytextureprivate.h"
 #include "gdk/gdktextureprivate.h"
 
 #ifdef G_ENABLE_DEBUG
@@ -91,41 +92,7 @@ gsk_cairo_renderer_do_render (GskRenderer   *renderer,
   gsk_profiler_timer_begin (profiler, self->profile_timers.cpu_time);
 #endif
 
-  if (!self->color_managed ||
-      gdk_color_profile_is_linear (gdk_cairo_get_color_profile (cr)))
-    {
-      gsk_render_node_draw (root, cr);
-    }
-  else
-    {
-      GdkSurface *surface = gsk_renderer_get_surface (renderer);
-      cairo_surface_t *cairo_surface;
-      cairo_t *cr2;
-      GdkTexture *color_correct;
-
-      /* We can't use cairo_push_group() here, because we'd lose the
-       * color profile information. */
-      cairo_surface = gdk_surface_create_similar_surface (surface,
-                                                          CAIRO_CONTENT_COLOR_ALPHA,
-                                                          gdk_surface_get_width (surface),
-                                                          gdk_surface_get_height (surface));
-      gdk_cairo_surface_set_color_profile (cairo_surface,
-                                           gdk_color_profile_get_srgb_linear ());
-
-      cr2 = cairo_create (cairo_surface);
-      gsk_render_node_draw (root, cr2);
-      cairo_destroy (cr2);
-
-      color_correct = gdk_texture_new_for_surface (cairo_surface);
-      cairo_surface_destroy (cairo_surface);
-      cairo_surface = gdk_texture_download_surface (color_correct,
-                                                    gdk_cairo_get_color_profile (cr));
-      g_object_unref (color_correct);
-
-      cairo_set_source_surface (cr, cairo_surface, 0, 0);
-      cairo_paint (cr);
-      cairo_surface_destroy (cairo_surface);
-    }
+  gsk_render_node_draw (root, cr);
 
 #ifdef G_ENABLE_DEBUG
   cpu_time = gsk_profiler_timer_end (profiler, self->profile_timers.cpu_time);
@@ -140,11 +107,15 @@ gsk_cairo_renderer_render_texture (GskRenderer           *renderer,
                                    GskRenderNode         *root,
                                    const graphene_rect_t *viewport)
 {
+  GskCairoRenderer *self = GSK_CAIRO_RENDERER (renderer);
   GdkTexture *texture;
   cairo_surface_t *surface;
   cairo_t *cr;
 
   surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, ceil (viewport->size.width), ceil 
(viewport->size.height));
+  if (self->color_managed)
+      gdk_cairo_surface_set_color_profile (surface, gdk_color_profile_get_srgb_linear ());
+
   cr = cairo_create (surface);
 
   cairo_translate (cr, - viewport->origin.x, - viewport->origin.y);
@@ -189,7 +160,65 @@ gsk_cairo_renderer_render (GskRenderer          *renderer,
     }
 #endif
 
-  gsk_cairo_renderer_do_render (renderer, cr, root);
+  if (!self->color_managed ||
+      gdk_color_profile_is_linear (gdk_cairo_get_color_profile (cr)))
+    {
+      gsk_cairo_renderer_do_render (renderer, cr, root);
+    }
+  else
+    {
+      GdkSurface *surface = gsk_renderer_get_surface (renderer);
+      GdkColorProfile *target_profile = gdk_cairo_get_color_profile (cr);
+      cairo_surface_t *cairo_surface;
+      cairo_t *cr2;
+      GdkTexture *color_correct;
+      const cairo_region_t *frame_region;
+      cairo_rectangle_int_t extents;
+      guint i, n;
+
+      frame_region = gdk_draw_context_get_frame_region (GDK_DRAW_CONTEXT (self->cairo_context));
+      cairo_region_get_extents (frame_region, &extents);
+      /* We can't use cairo_push_group() here, because we'd lose the
+       * color profile information. */
+      cairo_surface = gdk_surface_create_similar_surface (surface,
+                                                          CAIRO_CONTENT_COLOR_ALPHA,
+                                                          extents.width,
+                                                          extents.height);
+      gdk_cairo_surface_set_color_profile (cairo_surface,
+                                           gdk_color_profile_get_srgb_linear ());
+
+      cr2 = cairo_create (cairo_surface);
+      cairo_translate (cr2, -extents.x, -extents.y);
+      gdk_cairo_region (cr2, frame_region);
+      cairo_clip (cr2);
+      gsk_cairo_renderer_do_render (renderer, cr2, root);
+      cairo_destroy (cr2);
+
+      color_correct = gdk_texture_new_for_surface (cairo_surface);
+      cairo_surface_destroy (cairo_surface);
+      n = cairo_region_num_rectangles (frame_region);
+      for (i = 0; i < n; i++)
+        {
+          cairo_rectangle_int_t rect;
+          GdkMemoryTexture *sub;
+
+          cairo_region_get_rectangle (frame_region, i, &rect);
+          rect.x -= extents.x;
+          rect.y -= extents.y;
+
+          sub = gdk_memory_texture_convert (g_object_ref (GDK_MEMORY_TEXTURE (color_correct)),
+                                            GDK_MEMORY_DEFAULT,
+                                            target_profile,
+                                            &rect);
+          cairo_surface = gdk_texture_download_surface (GDK_TEXTURE (sub), target_profile);
+          cairo_set_source_surface (cr, cairo_surface, rect.x + extents.x, rect.y + extents.y);
+          cairo_paint (cr);
+          cairo_surface_destroy (cairo_surface);
+          g_object_unref (sub);
+        }
+      g_object_unref (color_correct);
+
+    }
 
   cairo_destroy (cr);
 
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index fa0bdb661e..e928273cff 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -27,6 +27,7 @@
 #include "gskroundedrectprivate.h"
 #include "gsktransformprivate.h"
 
+#include "gdk/gdkcolorprivate.h"
 #include "gdk/gdktextureprivate.h"
 #include "gdk/gdk-private.h"
 
@@ -179,6 +180,7 @@ gsk_linear_gradient_node_draw (GskRenderNode *node,
                                cairo_t       *cr)
 {
   GskLinearGradientNode *self = (GskLinearGradientNode *) node;
+  GdkColorProfile *profile;
   cairo_pattern_t *pattern;
   gsize i;
 
@@ -188,14 +190,20 @@ gsk_linear_gradient_node_draw (GskRenderNode *node,
   if (gsk_render_node_get_node_type (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE)
     cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
 
+  profile = gdk_cairo_get_color_profile (cr);
   for (i = 0; i < self->n_stops; i++)
     {
+      GdkColor color;
+      const float *components;
+      gdk_color_convert_rgba (&color, profile, &self->stops[i].color);
+      components = gdk_color_get_components (&color);
       cairo_pattern_add_color_stop_rgba (pattern,
                                          self->stops[i].offset,
-                                         self->stops[i].color.red,
-                                         self->stops[i].color.green,
-                                         self->stops[i].color.blue,
-                                         self->stops[i].color.alpha);
+                                         components[0],
+                                         components[1],
+                                         components[2],
+                                         gdk_color_get_alpha (&color));
+      gdk_color_finish (&color);
     }
 
   cairo_set_source (cr, pattern);
@@ -812,11 +820,17 @@ gsk_conic_gradient_node_finalize (GskRenderNode *node)
 #define DEG_TO_RAD(x)          ((x) * (G_PI / 180.f))
 
 static void
-_cairo_mesh_pattern_set_corner_rgba (cairo_pattern_t *pattern,
-                                     guint            corner_num,
-                                     const GdkRGBA   *rgba)
+_cairo_mesh_pattern_set_corner_color (cairo_pattern_t *pattern,
+                                      guint            corner_num,
+                                      const GdkColor  *color)
 {
-  cairo_mesh_pattern_set_corner_color_rgba (pattern, corner_num, rgba->red, rgba->green, rgba->blue, 
rgba->alpha);
+  const float *components = gdk_color_get_components (color);
+  cairo_mesh_pattern_set_corner_color_rgba (pattern,
+                                            corner_num,
+                                            components[0],
+                                            components[1],
+                                            components[2],
+                                            gdk_color_get_alpha (color));
 }
 
 static void
@@ -845,9 +859,9 @@ static void
 gsk_conic_gradient_node_add_patch (cairo_pattern_t *pattern,
                                    float            radius,
                                    float            start_angle,
-                                   const GdkRGBA   *start_color,
+                                   const GdkColor  *start_color,
                                    float            end_angle,
-                                   const GdkRGBA   *end_color)
+                                   const GdkColor  *end_color)
 {
   double x, y;
 
@@ -860,46 +874,26 @@ gsk_conic_gradient_node_add_patch (cairo_pattern_t *pattern,
   cairo_mesh_pattern_line_to  (pattern, x, y);
   cairo_mesh_pattern_line_to  (pattern, 0, 0);
 
-  _cairo_mesh_pattern_set_corner_rgba (pattern, 0, start_color);
-  _cairo_mesh_pattern_set_corner_rgba (pattern, 1, start_color);
-  _cairo_mesh_pattern_set_corner_rgba (pattern, 2, end_color);
-  _cairo_mesh_pattern_set_corner_rgba (pattern, 3, end_color);
+  _cairo_mesh_pattern_set_corner_color (pattern, 0, start_color);
+  _cairo_mesh_pattern_set_corner_color (pattern, 1, start_color);
+  _cairo_mesh_pattern_set_corner_color (pattern, 2, end_color);
+  _cairo_mesh_pattern_set_corner_color (pattern, 3, end_color);
 
   cairo_mesh_pattern_end_patch (pattern);
 }
 
-static void
-gdk_rgba_color_interpolate (GdkRGBA       *dest,
-                            const GdkRGBA *src1,
-                            const GdkRGBA *src2,
-                            double         progress)
-{
-  double alpha = src1->alpha * (1.0 - progress) + src2->alpha * progress;
-
-  dest->alpha = alpha;
-  if (alpha == 0)
-    {
-      dest->red = src1->red * (1.0 - progress) + src2->red * progress;
-      dest->green = src1->green * (1.0 - progress) + src2->green * progress;
-      dest->blue = src1->blue * (1.0 - progress) + src2->blue * progress;
-    }
-  else
-    {
-      dest->red = (src1->red * src1->alpha * (1.0 - progress) + src2->red * src2->alpha * progress) / alpha;
-      dest->green = (src1->green * src1->alpha * (1.0 - progress) + src2->green * src2->alpha * progress) / 
alpha;
-      dest->blue = (src1->blue * src1->alpha * (1.0 - progress) + src2->blue * src2->alpha * progress) / 
alpha;
-    }
-}
-
 static void
 gsk_conic_gradient_node_draw (GskRenderNode *node,
                               cairo_t       *cr)
 {
   GskConicGradientNode *self = (GskConicGradientNode *) node;
+  GdkColorProfile *color_profile;
   cairo_pattern_t *pattern;
   graphene_point_t corner;
   float radius;
   gsize i;
+  
+  color_profile = gdk_cairo_get_color_profile (cr);
 
   pattern = cairo_pattern_create_mesh ();
   graphene_rect_get_top_right (&node->bounds, &corner);
@@ -915,26 +909,31 @@ gsk_conic_gradient_node_draw (GskRenderNode *node,
     {
       GskColorStop *stop1 = &self->stops[MAX (i, 1) - 1];
       GskColorStop *stop2 = &self->stops[MIN (i, self->n_stops - 1)];
+      GdkColor stop1_color, stop2_color;
       double offset1 = i > 0 ? stop1->offset : 0;
       double offset2 = i < self->n_stops ? stop2->offset : 1;
       double start_angle, end_angle;
 
       offset1 = offset1 * 360 + self->rotation - 90;
       offset2 = offset2 * 360 + self->rotation - 90;
+      gdk_color_convert_rgba (&stop1_color, color_profile, &stop1->color);
+      gdk_color_convert_rgba (&stop2_color, color_profile, &stop1->color);
 
       for (start_angle = offset1; start_angle < offset2; start_angle = end_angle)
         {
-          GdkRGBA start_color, end_color;
+          GdkColor start_color, end_color;
           end_angle = (floor (start_angle / 45) + 1) * 45;
           end_angle = MIN (end_angle, offset2);
-          gdk_rgba_color_interpolate (&start_color,
-                                      &stop1->color,
-                                      &stop2->color,
-                                      (start_angle - offset1) / (offset2 - offset1));
-          gdk_rgba_color_interpolate (&end_color,
-                                      &stop1->color,
-                                      &stop2->color,
-                                      (end_angle - offset1) / (offset2 - offset1));
+          gdk_color_mix (&start_color,
+                         color_profile,
+                         &stop1_color,
+                         &stop2_color,
+                         (start_angle - offset1) / (offset2 - offset1));
+          gdk_color_mix (&end_color,
+                         color_profile,
+                         &stop1_color,
+                         &stop2_color,
+                         (end_angle - offset1) / (offset2 - offset1));
 
           gsk_conic_gradient_node_add_patch (pattern,
                                              radius,
@@ -943,6 +942,9 @@ gsk_conic_gradient_node_draw (GskRenderNode *node,
                                              DEG_TO_RAD (end_angle),
                                              &end_color);
         }
+
+      gdk_color_finish (&stop2_color);
+      gdk_color_finish (&stop1_color);
     }
 
   cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
@@ -1158,7 +1160,8 @@ struct _GskBorderNode
 
 static void
 gsk_border_node_mesh_add_patch (cairo_pattern_t *pattern,
-                                const GdkRGBA   *color,
+                                GdkColorProfile *color_profile,
+                                const GdkRGBA   *rgba,
                                 double           x0,
                                 double           y0,
                                 double           x1,
@@ -1168,16 +1171,26 @@ gsk_border_node_mesh_add_patch (cairo_pattern_t *pattern,
                                 double           x3,
                                 double           y3)
 {
+  GdkColor color;
+  const float *components;
+  float alpha;
+
+  gdk_color_convert_rgba (&color, color_profile, start_color);
+  components = gdk_color_get_components (&color);
+  alpha = gdk_color_get_alpha (&color);
+
   cairo_mesh_pattern_begin_patch (pattern);
   cairo_mesh_pattern_move_to (pattern, x0, y0);
   cairo_mesh_pattern_line_to (pattern, x1, y1);
   cairo_mesh_pattern_line_to (pattern, x2, y2);
   cairo_mesh_pattern_line_to (pattern, x3, y3);
-  cairo_mesh_pattern_set_corner_color_rgba (pattern, 0, color->red, color->green, color->blue, color->alpha);
-  cairo_mesh_pattern_set_corner_color_rgba (pattern, 1, color->red, color->green, color->blue, color->alpha);
-  cairo_mesh_pattern_set_corner_color_rgba (pattern, 2, color->red, color->green, color->blue, color->alpha);
-  cairo_mesh_pattern_set_corner_color_rgba (pattern, 3, color->red, color->green, color->blue, color->alpha);
+  cairo_mesh_pattern_set_corner_color_rgba (pattern, 0, components[0], components[1], components[2], alpha);
+  cairo_mesh_pattern_set_corner_color_rgba (pattern, 1, components[0], components[1], components[2], alpha);
+  cairo_mesh_pattern_set_corner_color_rgba (pattern, 2, components[0], components[1], components[2], alpha);
+  cairo_mesh_pattern_set_corner_color_rgba (pattern, 3, components[0], components[1], components[2], alpha);
   cairo_mesh_pattern_end_patch (pattern);
+
+  gdk_color_finish (&color);
 }
 
 static void
@@ -1222,11 +1235,13 @@ gsk_border_node_draw (GskRenderNode *node,
        * Note that the call to cairo_fill() will add the potential final
        * segment by closing the path, so we don't have to care.
        */
+      GdkColorProfile *profile;
       cairo_pattern_t *mesh;
       cairo_matrix_t mat;
       graphene_point_t tl, br;
       float scale;
 
+      profile = gdk_cairo_get_color_profile (cr);
       mesh = cairo_pattern_create_mesh ();
       cairo_matrix_init_translate (&mat, -bounds->origin.x, -bounds->origin.y);
       cairo_pattern_set_matrix (mesh, &mat);
@@ -1244,6 +1259,7 @@ gsk_border_node_draw (GskRenderNode *node,
       if (self->border_width[0] > 0)
         {
           gsk_border_node_mesh_add_patch (mesh,
+                                          profile,
                                           &self->border_color[0],
                                           0, 0,
                                           tl.x, tl.y,
@@ -1255,6 +1271,7 @@ gsk_border_node_draw (GskRenderNode *node,
       if (self->border_width[1] > 0)
         {
           gsk_border_node_mesh_add_patch (mesh,
+                                          profile,
                                           &self->border_color[1],
                                           bounds->size.width, 0,
                                           br.x, tl.y,
@@ -1266,6 +1283,7 @@ gsk_border_node_draw (GskRenderNode *node,
       if (self->border_width[2] > 0)
         {
           gsk_border_node_mesh_add_patch (mesh,
+                                          profile,
                                           &self->border_color[2],
                                           0, bounds->size.height,
                                           tl.x, br.y,
@@ -1277,6 +1295,7 @@ gsk_border_node_draw (GskRenderNode *node,
       if (self->border_width[3] > 0)
         {
           gsk_border_node_mesh_add_patch (mesh,
+                                          profile,
                                           &self->border_color[3],
                                           0, 0,
                                           tl.x, tl.y,


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