[gnome-shell] Match CSS for background extents



commit d263c12e2e028ab03adbcf3f41ea67c9fa5b4e73
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Sun Sep 20 18:03:18 2009 -0400

    Match CSS for background extents
    
    The CSS specification says that the background extends to the
    edge of the border (settable in CSS3 with border-clip), make
    BigRectangle match this by computing an "effective border color"
    as 'border OVER background'.
    
    (If we don't want this behavior - e.g., to be able to use the
    transparent borders as margins, then alternatively transparent
    border handling would have to be fixed in st-widget.c, since
    prior to this transparent and translucent borders were handled
    differently.)
    
    https://bugzilla.gnome.org/show_bug.cgi?id=595993

 src/big/rectangle.c          |  129 +++++++++++++++++++++++++++++++++---------
 tests/interactive/borders.js |   12 +++-
 2 files changed, 112 insertions(+), 29 deletions(-)
---
diff --git a/src/big/rectangle.c b/src/big/rectangle.c
index 8ca104d..fa594e6 100644
--- a/src/big/rectangle.c
+++ b/src/big/rectangle.c
@@ -268,6 +268,52 @@ corner_get(guint         radius,
     return corner;
 }
 
+/* To match the CSS specification, we want the border to look like it was
+ * drawn over the background. But actually drawing the border over the
+ * background will produce slightly bad antialiasing at the edges, so
+ * compute the effective border color instead.
+ */
+#define NORM(x) (t = (x) + 127, (t + (t >> 8)) >> 8)
+#define MULT(c,a) NORM(c*a)
+
+static void
+premultiply (ClutterColor *color)
+{
+    guint t;
+    color->red = MULT (color->red, color->alpha);
+    color->green = MULT (color->green, color->alpha);
+    color->blue = MULT (color->blue, color->alpha);
+}
+
+static void
+unpremultiply (ClutterColor *color)
+{
+    if (color->alpha != 0) {
+        color->red = (color->red * 255 + 127) / color->alpha;
+        color->green = (color->green * 255 + 127) / color->alpha;
+        color->blue = (color->blue * 255 + 127) / color->alpha;
+    }
+}
+
+static void
+over (const ClutterColor *source,
+      const ClutterColor *destination,
+      ClutterColor       *result)
+{
+    guint t;
+    ClutterColor src = *source;
+    ClutterColor dst = *destination;
+    premultiply (&src);
+    premultiply (&dst);
+
+    result->alpha = src.alpha + NORM ((255 - src.alpha) * dst.alpha);
+    result->red   = src.red +   NORM ((255 - src.alpha) * dst.red);
+    result->green = src.green + NORM ((255 - src.alpha) * dst.green);
+    result->blue  = src.blue +  NORM ((255 - src.alpha) * dst.blue);
+
+    unpremultiply (result);
+}
+
 static void
 big_rectangle_update_corners(BigRectangle *rectangle)
 {
@@ -278,6 +324,7 @@ big_rectangle_update_corners(BigRectangle *rectangle)
     if (rectangle->radius != 0) {
         ClutterColor *color;
         ClutterColor *border_color;
+        ClutterColor effective_border;
         guint border_width;
 
         g_object_get(rectangle,
@@ -286,10 +333,12 @@ big_rectangle_update_corners(BigRectangle *rectangle)
                      "color", &color,
                      NULL);
 
+        over (border_color, color, &effective_border);
+
         corner = corner_get(rectangle->radius,
                             color,
                             border_width,
-                            border_color);
+                            &effective_border);
 
         clutter_color_free(border_color);
         clutter_color_free(color);
@@ -329,12 +378,10 @@ big_rectangle_paint(ClutterActor *actor)
 
     rectangle = BIG_RECTANGLE(actor);
 
-    if (rectangle->radius == 0) {
-        /* In that case we are no different than our parent class,
-         * so don't bother */
-        CLUTTER_ACTOR_CLASS(big_rectangle_parent_class)->paint(actor);
-        return;
-    }
+    /* We can't chain up, even when we the radius is 0, because of the different
+     * interpretation of the border/background relationship here than for
+     * ClutterRectangle.
+     */
 
     if (rectangle->corners_dirty)
         big_rectangle_update_corners(rectangle);
@@ -345,6 +392,9 @@ big_rectangle_paint(ClutterActor *actor)
                  "color", &color,
                  NULL);
 
+    if (border_color->alpha == 0 && color->alpha == 0)
+        goto out;
+
     actor_opacity = clutter_actor_get_paint_opacity (actor);
 
     clutter_actor_get_allocation_box(actor, &box);
@@ -358,6 +408,11 @@ big_rectangle_paint(ClutterActor *actor)
 
     radius = rectangle->radius;
 
+    /* Optimization; if the border is transparent, it just looks like part of
+     * the background */
+    if (radius == 0 && border_color->alpha == 0)
+        border_width = 0;
+
     max = MAX(border_width, radius);
 
     if (radius != 0) {
@@ -393,33 +448,54 @@ big_rectangle_paint(ClutterActor *actor)
     }
 
     if (border_width != 0) {
+        ClutterColor effective_border;
+        over (border_color, color, &effective_border);
+
         if (!rectangle->border_material)
             rectangle->border_material = cogl_material_new ();
 
         cogl_color_set_from_4ub(&tmp_color,
-                                border_color->red,
-                                border_color->green,
-                                border_color->blue,
-                                actor_opacity * border_color->alpha / 255);
+                                effective_border.red,
+                                effective_border.green,
+                                effective_border.blue,
+                                actor_opacity * effective_border.alpha / 255);
         cogl_color_premultiply (&tmp_color);
         cogl_material_set_color(rectangle->border_material, &tmp_color);
         cogl_set_source(rectangle->border_material);
 
-        /* NORTH */
-        cogl_rectangle(max, 0,
-                       width - max, border_width);
-
-        /* EAST */
-        cogl_rectangle(width - border_width, max,
-                       width, height - max);
-
-        /* SOUTH */
-        cogl_rectangle(max, height - border_width,
-                       width - max, height);
-
-        /* WEST */
-        cogl_rectangle(0, max,
-                       border_width, height - max);
+        if (radius > 0) { /* skip corners */
+            /* NORTH */
+            cogl_rectangle(max, 0,
+                           width - max, border_width);
+
+            /* EAST */
+            cogl_rectangle(width - border_width, max,
+                           width, height - max);
+
+            /* SOUTH */
+            cogl_rectangle(max, height - border_width,
+                           width - max, height);
+
+            /* WEST */
+            cogl_rectangle(0, max,
+                           border_width, height - max);
+        } else { /* include corners */
+            /* NORTH */
+            cogl_rectangle(0, 0,
+                           width, border_width);
+
+            /* EAST */
+            cogl_rectangle(width - border_width, border_width,
+                           width, height - border_width);
+
+            /* SOUTH */
+            cogl_rectangle(0, height - border_width,
+                           width, height);
+
+            /* WEST */
+            cogl_rectangle(0, border_width,
+                           border_width, height - border_width);
+        }
     }
 
     if (!rectangle->background_material)
@@ -455,6 +531,7 @@ big_rectangle_paint(ClutterActor *actor)
     cogl_rectangle(border_width, max,
                    width - border_width, height - max);
 
+out:
     clutter_color_free(border_color);
     clutter_color_free(color);
 }
diff --git a/tests/interactive/borders.js b/tests/interactive/borders.js
index 258a621..d685345 100644
--- a/tests/interactive/borders.js
+++ b/tests/interactive/borders.js
@@ -8,13 +8,13 @@ const UI = imports.testcommon.ui;
 UI.init();
 let stage = Clutter.Stage.get_default();
 stage.width = 600;
-stage.height = 600;
+stage.height = 700;
 
 let vbox = new St.BoxLayout({ vertical: true,
                               width: stage.width,
                               height: stage.height,
                               spacing: 20,
-                              style: 'padding: 10px; background: #ffee88' });
+                              style: 'padding: 10px; background: #ffee88;' });
 stage.add_actor(vbox);
 
 vbox.add(new St.Label({ text: "Hello World",
@@ -43,8 +43,14 @@ vbox.add(b1);
 b1.add(new St.BoxLayout({ width: 20, height: 20,
                           style: 'background: black' }));
 
-vbox.add(new St.Label({ text: "Translucent blue border",
+vbox.add(new St.Label({ text: "Translucent blue border, with rounding",
                         style: 'border: 20px solid rgba(0, 0, 255, 0.2); '
+                               + 'border-radius: 10px; '
+                               + 'background: white; '
+                               + 'padding: 10px;' }));
+
+vbox.add(new St.Label({ text: "Transparent border",
+                        style: 'border: 20px solid transparent; '
                                + 'background: white; '
                                + 'padding: 10px;' }));
 



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