[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