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
This commit is contained in:
Owen W. Taylor 2009-09-20 18:03:18 -04:00
parent d92b1d8da2
commit 304b48a15d
2 changed files with 109 additions and 26 deletions

View File

@ -268,6 +268,52 @@ corner_get(guint radius,
return corner; 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 static void
big_rectangle_update_corners(BigRectangle *rectangle) big_rectangle_update_corners(BigRectangle *rectangle)
{ {
@ -278,6 +324,7 @@ big_rectangle_update_corners(BigRectangle *rectangle)
if (rectangle->radius != 0) { if (rectangle->radius != 0) {
ClutterColor *color; ClutterColor *color;
ClutterColor *border_color; ClutterColor *border_color;
ClutterColor effective_border;
guint border_width; guint border_width;
g_object_get(rectangle, g_object_get(rectangle,
@ -286,10 +333,12 @@ big_rectangle_update_corners(BigRectangle *rectangle)
"color", &color, "color", &color,
NULL); NULL);
over (border_color, color, &effective_border);
corner = corner_get(rectangle->radius, corner = corner_get(rectangle->radius,
color, color,
border_width, border_width,
border_color); &effective_border);
clutter_color_free(border_color); clutter_color_free(border_color);
clutter_color_free(color); clutter_color_free(color);
@ -329,12 +378,10 @@ big_rectangle_paint(ClutterActor *actor)
rectangle = BIG_RECTANGLE(actor); rectangle = BIG_RECTANGLE(actor);
if (rectangle->radius == 0) { /* We can't chain up, even when we the radius is 0, because of the different
/* In that case we are no different than our parent class, * interpretation of the border/background relationship here than for
* so don't bother */ * ClutterRectangle.
CLUTTER_ACTOR_CLASS(big_rectangle_parent_class)->paint(actor); */
return;
}
if (rectangle->corners_dirty) if (rectangle->corners_dirty)
big_rectangle_update_corners(rectangle); big_rectangle_update_corners(rectangle);
@ -345,6 +392,9 @@ big_rectangle_paint(ClutterActor *actor)
"color", &color, "color", &color,
NULL); NULL);
if (border_color->alpha == 0 && color->alpha == 0)
goto out;
actor_opacity = clutter_actor_get_paint_opacity (actor); actor_opacity = clutter_actor_get_paint_opacity (actor);
clutter_actor_get_allocation_box(actor, &box); clutter_actor_get_allocation_box(actor, &box);
@ -358,6 +408,11 @@ big_rectangle_paint(ClutterActor *actor)
radius = rectangle->radius; 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); max = MAX(border_width, radius);
if (radius != 0) { if (radius != 0) {
@ -393,33 +448,54 @@ big_rectangle_paint(ClutterActor *actor)
} }
if (border_width != 0) { if (border_width != 0) {
ClutterColor effective_border;
over (border_color, color, &effective_border);
if (!rectangle->border_material) if (!rectangle->border_material)
rectangle->border_material = cogl_material_new (); rectangle->border_material = cogl_material_new ();
cogl_color_set_from_4ub(&tmp_color, cogl_color_set_from_4ub(&tmp_color,
border_color->red, effective_border.red,
border_color->green, effective_border.green,
border_color->blue, effective_border.blue,
actor_opacity * border_color->alpha / 255); actor_opacity * effective_border.alpha / 255);
cogl_color_premultiply (&tmp_color); cogl_color_premultiply (&tmp_color);
cogl_material_set_color(rectangle->border_material, &tmp_color); cogl_material_set_color(rectangle->border_material, &tmp_color);
cogl_set_source(rectangle->border_material); cogl_set_source(rectangle->border_material);
/* NORTH */ if (radius > 0) { /* skip corners */
cogl_rectangle(max, 0, /* NORTH */
width - max, border_width); cogl_rectangle(max, 0,
width - max, border_width);
/* EAST */ /* EAST */
cogl_rectangle(width - border_width, max, cogl_rectangle(width - border_width, max,
width, height - max); width, height - max);
/* SOUTH */ /* SOUTH */
cogl_rectangle(max, height - border_width, cogl_rectangle(max, height - border_width,
width - max, height); width - max, height);
/* WEST */ /* WEST */
cogl_rectangle(0, max, cogl_rectangle(0, max,
border_width, height - 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) if (!rectangle->background_material)
@ -455,6 +531,7 @@ big_rectangle_paint(ClutterActor *actor)
cogl_rectangle(border_width, max, cogl_rectangle(border_width, max,
width - border_width, height - max); width - border_width, height - max);
out:
clutter_color_free(border_color); clutter_color_free(border_color);
clutter_color_free(color); clutter_color_free(color);
} }

View File

@ -8,13 +8,13 @@ const UI = imports.testcommon.ui;
UI.init(); UI.init();
let stage = Clutter.Stage.get_default(); let stage = Clutter.Stage.get_default();
stage.width = 600; stage.width = 600;
stage.height = 600; stage.height = 700;
let vbox = new St.BoxLayout({ vertical: true, let vbox = new St.BoxLayout({ vertical: true,
width: stage.width, width: stage.width,
height: stage.height, height: stage.height,
spacing: 20, spacing: 20,
style: 'padding: 10px; background: #ffee88' }); style: 'padding: 10px; background: #ffee88;' });
stage.add_actor(vbox); stage.add_actor(vbox);
vbox.add(new St.Label({ text: "Hello World", vbox.add(new St.Label({ text: "Hello World",
@ -43,8 +43,14 @@ vbox.add(b1);
b1.add(new St.BoxLayout({ width: 20, height: 20, b1.add(new St.BoxLayout({ width: 20, height: 20,
style: 'background: black' })); 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); ' 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; ' + 'background: white; '
+ 'padding: 10px;' })); + 'padding: 10px;' }));