[Pixman] [PATCH 1/3] Add CLEAR and SRC linear interpolation operators

Chris Wilson chris at chris-wilson.co.uk
Wed Sep 14 08:06:44 PDT 2011


Cairo, for instance, has a subtly different interpretation of how to use
the mask in combination with the Porter-Duff operators. In particular,
it has the notion of a clip mask, for which pixman has no parallel.

Quoting Soeren:

  Another aspect is that cairo uses a different rendering equation in some
  cases. The two equations used are:

	unbounded:   [(src IN shape) OP dest          ] LERP_clip dest

	bounded:     [(src OP dest) LERP_shape dest   ] LERP_clip dest

  With shaped clips, the LERP_clip part is taken care of, and the first
  equation is already directly supported by pixman. The second one could
  be supported by adding new <op>_LERP operators that would use the

	(src OP dest) LERP_shape dest

  equation. For cairo's purposes, all we need is CLEAR_LERP and SRC_LERP,
  but I think it could be useful to have the full set of LERP operators.

This patch introduces the first two which enables Cairo to avoid
painfully implementing LERP as a 2-pass composite operation (and opens
up more fast paths for Cairo).

Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
---
 pixman/pixman-combine.c.template |  224 ++++++++++++++++++++++++++++++++++++++
 pixman/pixman-general.c          |    1 -
 pixman/pixman.c                  |    9 ++
 pixman/pixman.h                  |   17 +++-
 test/composite.c                 |  110 ++++++++++++++-----
 5 files changed, 332 insertions(+), 29 deletions(-)

diff --git a/pixman/pixman-combine.c.template b/pixman/pixman-combine.c.template
index 806a184..8f7f3b2 100644
--- a/pixman/pixman-combine.c.template
+++ b/pixman/pixman-combine.c.template
@@ -417,6 +417,138 @@ combine_saturate_u (pixman_implementation_t *imp,
     }
 }
 
+/* Linear interpolation between src and dest using mask as the interpolant. */
+static void
+combine_lerp_src_u (pixman_implementation_t *imp,
+		    pixman_op_t              op,
+		    comp4_t *                dest,
+		    const comp4_t *          src,
+		    const comp4_t *          mask,
+		    int                      width)
+{
+    int i;
+
+    if (!mask)
+    {
+	memcpy (dest, src, width * sizeof (comp4_t));
+	return;
+    }
+
+    for (i = 0; i < width; ++i)
+    {
+	comp4_t m = *(mask + i);
+	comp4_t a = ALPHA_c (m);
+	if (a == MASK)
+	{
+	    *(dest + i) = *(src + i);
+	}
+	else if (a)
+	{
+	    comp4_t s = *(src + i);
+	    comp4_t d = *(dest + i);
+	    comp4_t ia = ALPHA_c (~m);
+	    UNcx4_MUL_UNc_ADD_UNcx4_MUL_UNc (d, ia, s, a);
+	    *(dest + i) = d;
+	}
+    }
+}
+
+static void
+combine_lerp_src_reverse_u (pixman_implementation_t *imp,
+			    pixman_op_t              op,
+			    comp4_t *                dest,
+			    const comp4_t *          src,
+			    const comp4_t *          mask,
+			    int                      width)
+{
+    int i;
+
+    if (!mask)
+	return;
+
+    for (i = 0; i < width; ++i)
+    {
+	comp4_t m = *(mask + i);
+	comp4_t a = ALPHA_c (m);
+	if (a == 0)
+	{
+	    *(dest + i) = *(src + i);
+	}
+	else if (a != MASK)
+	{
+	    comp4_t s = *(src + i);
+	    comp4_t d = *(dest + i);
+	    comp4_t ia = ALPHA_c (~m);
+	    UNcx4_MUL_UNc_ADD_UNcx4_MUL_UNc (d, a, s, ia);
+	    *(dest + i) = d;
+	}
+    }
+}
+
+/* Linear interpolation between CLEAR and dest using mask as the interpolant. */
+static void
+combine_lerp_clear_u (pixman_implementation_t *imp,
+		      pixman_op_t              op,
+		      comp4_t *                dest,
+		      const comp4_t *          src,
+		      const comp4_t *          mask,
+		      int                      width)
+{
+    int i;
+
+    if (!mask)
+    {
+	combine_clear (imp, op, dest, src, mask, width);
+	return;
+    }
+
+    for (i = 0; i < width; ++i)
+    {
+	comp4_t m = *(mask + i);
+	comp4_t ia = ALPHA_c (~m);
+	if (ia == 0)
+	{
+	    *(dest + i) = 0;
+	}
+	else if (ia != MASK)
+	{
+	    comp4_t d = *(dest + i);
+	    UNcx4_MUL_UNc (d, ia);
+	    *(dest + i) = d;
+	}
+    }
+}
+
+static void
+combine_lerp_clear_reverse_u (pixman_implementation_t *imp,
+			      pixman_op_t              op,
+			      comp4_t *                dest,
+			      const comp4_t *          src,
+			      const comp4_t *          mask,
+			      int                      width)
+{
+    int i;
+
+    if (!mask)
+	return;
+
+    for (i = 0; i < width; ++i)
+    {
+	comp4_t m = *(mask + i);
+	comp4_t a = ALPHA_c (m);
+	if (a == 0)
+	{
+	    *(dest + i) = 0;
+	}
+	else if (a != MASK)
+	{
+	    comp4_t d = *(dest + i);
+	    UNcx4_MUL_UNc (d, a);
+	    *(dest + i) = d;
+	}
+    }
+}
+
 /*
  * PDF blend modes:
  * The following blend modes have been taken from the PDF ISO 32000
@@ -1963,6 +2095,88 @@ combine_saturate_ca (pixman_implementation_t *imp,
     }
 }
 
+/* Linear interpolation between src and dest using mask as the interpolant. */
+static void
+combine_lerp_src_ca (pixman_implementation_t *imp,
+		     pixman_op_t              op,
+		     comp4_t *                dest,
+		     const comp4_t *          src,
+		     const comp4_t *          mask,
+		     int                      width)
+{
+    int i;
+
+    for (i = 0; i < width; ++i)
+    {
+	comp4_t a = *(mask + i);
+	comp4_t s = *(src + i);
+	comp4_t d = *(dest + i);
+	UNcx4_MUL_UNcx4 (s, a);
+	UNcx4_MUL_UNcx4_ADD_UNcx4 (d, ~a, s);
+	*(dest + i) = d;
+    }
+}
+
+static void
+combine_lerp_src_reverse_ca (pixman_implementation_t *imp,
+			     pixman_op_t              op,
+			     comp4_t *                dest,
+			     const comp4_t *          src,
+			     const comp4_t *          mask,
+			     int                      width)
+{
+    int i;
+
+    for (i = 0; i < width; ++i)
+    {
+	comp4_t a = *(mask + i);
+	comp4_t s = *(src + i);
+	comp4_t d = *(dest + i);
+	UNcx4_MUL_UNcx4 (d, a);
+	UNcx4_MUL_UNcx4_ADD_UNcx4 (s, ~a, d);
+	*(dest + i) = s;
+    }
+}
+
+/* Linear interpolation between CLEAR and dest using mask as the interpolant. */
+static void
+combine_lerp_clear_ca (pixman_implementation_t *imp,
+		       pixman_op_t              op,
+		       comp4_t *                dest,
+		       const comp4_t *          src,
+		       const comp4_t *          mask,
+		       int                      width)
+{
+    int i;
+
+    for (i = 0; i < width; ++i)
+    {
+	comp4_t a = *(mask + i);
+	comp4_t d = *(dest + i);
+	UNcx4_MUL_UNcx4 (d, ~a);
+	*(dest + i) = d;
+    }
+}
+
+static void
+combine_lerp_clear_reverse_ca (pixman_implementation_t *imp,
+			       pixman_op_t              op,
+			       comp4_t *                dest,
+			       const comp4_t *          src,
+			       const comp4_t *          mask,
+			       int                      width)
+{
+    int i;
+
+    for (i = 0; i < width; ++i)
+    {
+	comp4_t a = *(mask + i);
+	comp4_t d = *(dest + i);
+	UNcx4_MUL_UNcx4 (d, a);
+	*(dest + i) = d;
+    }
+}
+
 static void
 combine_disjoint_general_ca (comp4_t *      dest,
                              const comp4_t *src,
@@ -2351,6 +2565,11 @@ _pixman_setup_combiner_functions_width (pixman_implementation_t *imp)
     imp->combine_width[PIXMAN_OP_ADD] = combine_add_u;
     imp->combine_width[PIXMAN_OP_SATURATE] = combine_saturate_u;
 
+    imp->combine_width[PIXMAN_OP_LERP_SRC] = combine_lerp_src_u;
+    imp->combine_width[PIXMAN_OP_LERP_SRC_REVERSE] = combine_lerp_src_reverse_u;
+    imp->combine_width[PIXMAN_OP_LERP_CLEAR] = combine_lerp_clear_u;
+    imp->combine_width[PIXMAN_OP_LERP_CLEAR_REVERSE] = combine_lerp_clear_reverse_u;
+
     /* Disjoint, unified */
     imp->combine_width[PIXMAN_OP_DISJOINT_CLEAR] = combine_clear;
     imp->combine_width[PIXMAN_OP_DISJOINT_SRC] = combine_src_u;
@@ -2411,6 +2630,11 @@ _pixman_setup_combiner_functions_width (pixman_implementation_t *imp)
     imp->combine_width_ca[PIXMAN_OP_ADD] = combine_add_ca;
     imp->combine_width_ca[PIXMAN_OP_SATURATE] = combine_saturate_ca;
 
+    imp->combine_width_ca[PIXMAN_OP_LERP_SRC] = combine_lerp_src_ca;
+    imp->combine_width_ca[PIXMAN_OP_LERP_SRC_REVERSE] = combine_lerp_src_reverse_ca;
+    imp->combine_width_ca[PIXMAN_OP_LERP_CLEAR] = combine_lerp_clear_ca;
+    imp->combine_width_ca[PIXMAN_OP_LERP_CLEAR_REVERSE] = combine_lerp_clear_reverse_ca;
+
     /* Disjoint CA */
     imp->combine_width_ca[PIXMAN_OP_DISJOINT_CLEAR] = combine_clear_ca;
     imp->combine_width_ca[PIXMAN_OP_DISJOINT_SRC] = combine_src_ca;
diff --git a/pixman/pixman-general.c b/pixman/pixman-general.c
index 2ccdfcd..c24a3f3 100644
--- a/pixman/pixman-general.c
+++ b/pixman/pixman-general.c
@@ -261,4 +261,3 @@ _pixman_implementation_create_general (void)
 
     return imp;
 }
-
diff --git a/pixman/pixman.c b/pixman/pixman.c
index 7c856ff..d09ac28 100644
--- a/pixman/pixman.c
+++ b/pixman/pixman.c
@@ -135,6 +135,15 @@ static const operator_info_t operator_table[] =
     PACK (HSL_SATURATION,        HSL_SATURATION,        HSL_SATURATION,        HSL_SATURATION),
     PACK (HSL_COLOR,             HSL_COLOR,             HSL_COLOR,             HSL_COLOR),
     PACK (HSL_LUMINOSITY,        HSL_LUMINOSITY,        HSL_LUMINOSITY,        HSL_LUMINOSITY),
+
+    {{ 0 /* 0x3f */ }},
+
+    PACK (LERP_CLEAR,            LERP_CLEAR,            LERP_CLEAR,             LERP_CLEAR),
+    PACK (LERP_CLEAR_REVERSE,    LERP_CLEAR_REVERSE,    LERP_CLEAR_REVERSE,     LERP_CLEAR_REVERSE),
+
+    PACK (LERP_SRC,              LERP_SRC,              LERP_SRC,             LERP_SRC),
+    PACK (LERP_SRC_REVERSE,      LERP_SRC_REVERSE,      LERP_SRC_REVERSE,     LERP_SRC_REVERSE),
+
 };
 
 /*
diff --git a/pixman/pixman.h b/pixman/pixman.h
index c57092a..32f0d00 100644
--- a/pixman/pixman.h
+++ b/pixman/pixman.h
@@ -349,7 +349,22 @@ typedef enum
     PIXMAN_OP_HSL_HUE			= 0x3b,
     PIXMAN_OP_HSL_SATURATION		= 0x3c,
     PIXMAN_OP_HSL_COLOR			= 0x3d,
-    PIXMAN_OP_HSL_LUMINOSITY		= 0x3e
+    PIXMAN_OP_HSL_LUMINOSITY		= 0x3e,
+
+    /* LERP (linear interpolation) is not a classic Porter-Duff operator,
+     * but is useful for implementing clipping.
+     *
+     * In theory, we could implement every operator in combination with
+     * a LERP. However, only two (SRC and CLEAR) are immediately useful,
+     * for example by Cairo, due to its differing semantics of its
+     * implementation of the Porter-Duff operators in conjunction with
+     * clipping.
+     */
+    PIXMAN_OP_LERP_CLEAR		= 0x40,
+    PIXMAN_OP_LERP_CLEAR_REVERSE	= 0x41,
+    PIXMAN_OP_LERP_SRC			= 0x42,
+    PIXMAN_OP_LERP_SRC_REVERSE		= 0x43
+#define PIXMAN_HAS_OP_LERP 1
 
 #ifdef PIXMAN_USE_INTERNAL_API
     ,
diff --git a/test/composite.c b/test/composite.c
index edea9a9..8ed6943 100644
--- a/test/composite.c
+++ b/test/composite.c
@@ -178,6 +178,11 @@ static const operator_t operators[] =
     P(ADD),
     P(SATURATE),
 
+    P(LERP_SRC),
+    P(LERP_SRC_REVERSE),
+    P(LERP_CLEAR),
+    P(LERP_CLEAR_REVERSE),
+
     P(DISJOINT_CLEAR),
     P(DISJOINT_SRC),
     P(DISJOINT_DST),
@@ -260,6 +265,16 @@ calc_op (pixman_op_t op, double src, double dst, double srca, double dsta)
     case PIXMAN_OP_ADD:
 	return mult_chan (src, dst, 1.0, 1.0);
 
+    case PIXMAN_OP_LERP_SRC:
+	return mult_chan (src, dst, srca, 1.0 - srca);
+    case PIXMAN_OP_LERP_SRC_REVERSE:
+	return mult_chan (src, dst, 1.0 - srca, srca);
+
+    case PIXMAN_OP_LERP_CLEAR:
+	return mult_chan (0, dst, srca, 1.0 - srca);
+    case PIXMAN_OP_LERP_CLEAR_REVERSE:
+	return mult_chan (0, dst, 1.0 - srca, srca);
+
     case PIXMAN_OP_SATURATE:
     case PIXMAN_OP_DISJOINT_OVER_REVERSE:
 	if (srca == 0.0)
@@ -433,6 +448,15 @@ calc_op (pixman_op_t op, double src, double dst, double srca, double dsta)
 #undef mult_chan
 }
 
+static pixman_bool_t
+is_lerp (pixman_op_t op)
+{
+    return (op == PIXMAN_OP_LERP_SRC ||
+	    op == PIXMAN_OP_LERP_SRC_REVERSE ||
+	    op == PIXMAN_OP_LERP_CLEAR ||
+	    op == PIXMAN_OP_LERP_CLEAR_REVERSE);
+}
+
 static void
 do_composite (pixman_op_t op,
 	      const color_t *src,
@@ -443,38 +467,70 @@ do_composite (pixman_op_t op,
 {
     color_t srcval, srcalpha;
 
-    if (mask == NULL)
+    if (is_lerp (op))
     {
+	/* LERP is not a classic Porter-Duff operator */
 	srcval = *src;
-
-	srcalpha.r = src->a;
-	srcalpha.g = src->a;
-	srcalpha.b = src->a;
-	srcalpha.a = src->a;
-    }
-    else if (component_alpha)
-    {
-	srcval.r = src->r * mask->r;
-	srcval.g = src->g * mask->g;
-	srcval.b = src->b * mask->b;
-	srcval.a = src->a * mask->a;
-
-	srcalpha.r = src->a * mask->r;
-	srcalpha.g = src->a * mask->g;
-	srcalpha.b = src->a * mask->b;
-	srcalpha.a = src->a * mask->a;
+	if (mask)
+	{
+	    if (component_alpha)
+	    {
+		srcalpha.r = mask->r;
+		srcalpha.g = mask->g;
+		srcalpha.b = mask->b;
+		srcalpha.a = mask->a;
+	    }
+	    else
+	    {
+		srcalpha.r = mask->a;
+		srcalpha.g = mask->a;
+		srcalpha.b = mask->a;
+		srcalpha.a = mask->a;
+	    }
+	}
+	else
+	{
+	    srcalpha.r = 1.0;
+	    srcalpha.g = 1.0;
+	    srcalpha.b = 1.0;
+	    srcalpha.a = 1.0;
+	}
     }
     else
     {
-	srcval.r = src->r * mask->a;
-	srcval.g = src->g * mask->a;
-	srcval.b = src->b * mask->a;
-	srcval.a = src->a * mask->a;
-
-	srcalpha.r = src->a * mask->a;
-	srcalpha.g = src->a * mask->a;
-	srcalpha.b = src->a * mask->a;
-	srcalpha.a = src->a * mask->a;
+	if (mask == NULL)
+	{
+	    srcval = *src;
+
+	    srcalpha.r = src->a;
+	    srcalpha.g = src->a;
+	    srcalpha.b = src->a;
+	    srcalpha.a = src->a;
+	}
+	else if (component_alpha)
+	{
+	    srcval.r = src->r * mask->r;
+	    srcval.g = src->g * mask->g;
+	    srcval.b = src->b * mask->b;
+	    srcval.a = src->a * mask->a;
+
+	    srcalpha.r = src->a * mask->r;
+	    srcalpha.g = src->a * mask->g;
+	    srcalpha.b = src->a * mask->b;
+	    srcalpha.a = src->a * mask->a;
+	}
+	else
+	{
+	    srcval.r = src->r * mask->a;
+	    srcval.g = src->g * mask->a;
+	    srcval.b = src->b * mask->a;
+	    srcval.a = src->a * mask->a;
+
+	    srcalpha.r = src->a * mask->a;
+	    srcalpha.g = src->a * mask->a;
+	    srcalpha.b = src->a * mask->a;
+	    srcalpha.a = src->a * mask->a;
+	}
     }
 
     result->r = calc_op (op, srcval.r, dst->r, srcalpha.r, dst->a);
-- 
1.7.5.4



More information about the Pixman mailing list