[Pixman] [PATCH 1/2] Implement floating point gradient computation, v2.
Basile Clement
basile-pixman at clement.pm
Sat Feb 9 12:02:22 UTC 2019
On 1/14/19 2:52 PM, Maarten Lankhorst wrote:
> This patch modifies the gradient walker to be able to generate floating
> point values directly in addition to a8r8g8b8 32 bit values. This is
> then used by the various gradient implementations to render in floating
> point when asked to do so, instead of rendering to a8r8g8b8 and then
> expanding to floating point as they were doing previously.
>
> Changes since v1 (mlankhorst):
> - Implement pixman_gradient_walker_pixel_32 without calling
> pixman_gradient_walker_pixel_float, to prevent performance degradation.
> Suggested by Adam Jackson.
> - Fix whitespace errors.
> - Remove unnecessary function prototypes in pixman-private.h
>
> Signed-off-by: Maarten Lankhorst
Thanks for fixing the performance degradation. I'd add a comment inline
explaining that we use this instead of expand_from_float due to
performance issues, but maybe that is not necessary.
> ---
> diff --git a/pixman/pixman-conical-gradient.c b/pixman/pixman-conical-gradient.c
> index 8bb46aecdcab..a39e20c4eb68 100644
> --- a/pixman/pixman-conical-gradient.c
> +++ b/pixman/pixman-conical-gradient.c
> @@ -51,7 +51,10 @@ coordinates_to_parameter (double x, double y, double angle)
> }
>
> static uint32_t *
> -conical_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
> +conical_get_scanline (pixman_iter_t *iter,
> + const uint32_t *mask,
> + int Bpp,
> + pixman_gradient_walker_write_t write_pixel)
> {
> pixman_image_t *image = iter->image;
> int x = iter->x;
> @@ -61,7 +64,7 @@ conical_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
>
> gradient_t *gradient = (gradient_t *)image;
> conical_gradient_t *conical = (conical_gradient_t *)image;
> - uint32_t *end = buffer + width;
> + uint32_t *end = buffer + width * (Bpp / 4);
> pixman_gradient_walker_t walker;
> pixman_bool_t affine = TRUE;
> double cx = 1.;
> @@ -109,11 +112,12 @@ conical_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
> {
> double t = coordinates_to_parameter (rx, ry, conical->angle);
>
> - *buffer = _pixman_gradient_walker_pixel (
> - &walker, (pixman_fixed_48_16_t)pixman_double_to_fixed (t));
> + write_pixel (&walker,
> + (pixman_fixed_48_16_t)pixman_double_to_fixed (t),
> + buffer);
> }
>
> - ++buffer;
> + buffer += (Bpp / 4);
>
> rx += cx;
> ry += cy;
> @@ -144,11 +148,12 @@ conical_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
>
> t = coordinates_to_parameter (x, y, conical->angle);
>
> - *buffer = _pixman_gradient_walker_pixel (
> - &walker, (pixman_fixed_48_16_t)pixman_double_to_fixed (t));
> + write_pixel (&walker,
> + (pixman_fixed_48_16_t)pixman_double_to_fixed (t),
> + buffer);
> }
>
> - ++buffer;
> + buffer += (Bpp / 4);
>
> rx += cx;
> ry += cy;
> @@ -161,14 +166,17 @@ conical_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
> }
>
> static uint32_t *
> -conical_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
> +conical_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
> {
> - uint32_t *buffer = conical_get_scanline_narrow (iter, NULL);
> -
> - pixman_expand_to_float (
> - (argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
> + return conical_get_scanline (iter, mask, 4,
> + _pixman_gradient_walker_write_narrow);
> +}
>
> - return buffer;
> +static uint32_t *
> +conical_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
> +{
> + return conical_get_scanline (iter, NULL, 16,
> + _pixman_gradient_walker_write_wide);
> }
>
> void
> diff --git a/pixman/pixman-gradient-walker.c b/pixman/pixman-gradient-walker.c
> index 822f8e62bae7..58db4f3cc63a 100644
> --- a/pixman/pixman-gradient-walker.c
> +++ b/pixman/pixman-gradient-walker.c
> @@ -122,10 +122,9 @@ gradient_walker_reset (pixman_gradient_walker_t *walker,
> left_c = right_c;
> }
>
> - /* The alpha channel is scaled to be in the [0, 255] interval,
> - * and the red/green/blue channels are scaled to be in [0, 1].
> + /* The alpha/red/green/blue channels are scaled to be in [0, 1].
> * This ensures that after premultiplication all channels will
> - * be in the [0, 255] interval.
> + * be in the [0, 1] interval.
> */
> la = (left_c->alpha * (1.0f/257.0f));
> lr = (left_c->red * (1.0f/257.0f));
> @@ -143,7 +142,7 @@ gradient_walker_reset (pixman_gradient_walker_t *walker,
> if (FLOAT_IS_ZERO (rx - lx) || left_x == INT32_MIN || right_x == INT32_MAX)
> {
> walker->a_s = walker->r_s = walker->g_s = walker->b_s = 0.0f;
> - walker->a_b = (la + ra) / 2.0f;
> + walker->a_b = (la + ra) / 510.0f;
> walker->r_b = (lr + rr) / 510.0f;
> walker->g_b = (lg + rg) / 510.0f;
> walker->b_b = (lb + rb) / 510.0f;
> @@ -152,12 +151,12 @@ gradient_walker_reset (pixman_gradient_walker_t *walker,
> {
> float w_rec = 1.0f / (rx - lx);
>
> - walker->a_b = (la * rx - ra * lx) * w_rec;
> + walker->a_b = (la * rx - ra * lx) * w_rec * (1.0f/255.0f);
> walker->r_b = (lr * rx - rr * lx) * w_rec * (1.0f/255.0f);
> walker->g_b = (lg * rx - rg * lx) * w_rec * (1.0f/255.0f);
> walker->b_b = (lb * rx - rb * lx) * w_rec * (1.0f/255.0f);
>
> - walker->a_s = (ra - la) * w_rec;
> + walker->a_s = (ra - la) * w_rec * (1.0f/255.0f);
> walker->r_s = (rr - lr) * w_rec * (1.0f/255.0f);
> walker->g_s = (rg - lg) * w_rec * (1.0f/255.0f);
> walker->b_s = (rb - lb) * w_rec * (1.0f/255.0f);
> @@ -169,34 +168,93 @@ gradient_walker_reset (pixman_gradient_walker_t *walker,
> walker->need_reset = FALSE;
> }
>
> -uint32_t
> -_pixman_gradient_walker_pixel (pixman_gradient_walker_t *walker,
> - pixman_fixed_48_16_t x)
> +static argb_t
> +pixman_gradient_walker_pixel_float (pixman_gradient_walker_t *walker,
> + pixman_fixed_48_16_t x)
> {
> - float a, r, g, b;
> - uint8_t a8, r8, g8, b8;
> - uint32_t v;
> + argb_t f;
> float y;
>
> if (walker->need_reset || x < walker->left_x || x >= walker->right_x)
> - gradient_walker_reset (walker, x);
> + gradient_walker_reset (walker, x);
>
> y = x * (1.0f / 65536.0f);
>
> - a = walker->a_s * y + walker->a_b;
> - r = a * (walker->r_s * y + walker->r_b);
> - g = a * (walker->g_s * y + walker->g_b);
> - b = a * (walker->b_s * y + walker->b_b);
> + f.a = walker->a_s * y + walker->a_b;
> + f.r = f.a * (walker->r_s * y + walker->r_b);
> + f.g = f.a * (walker->g_s * y + walker->g_b);
> + f.b = f.a * (walker->b_s * y + walker->b_b);
>
> - a8 = a + 0.5f;
> - r8 = r + 0.5f;
> - g8 = g + 0.5f;
> - b8 = b + 0.5f;
> + return f;
> +}
> +
> +static uint32_t
> +pixman_gradient_walker_pixel_32 (pixman_gradient_walker_t *walker,
> + pixman_fixed_48_16_t x)
> +{
> + argb_t f;
> + float y;
> +
> + if (walker->need_reset || x < walker->left_x || x >= walker->right_x)
> + gradient_walker_reset (walker, x);
> +
> + y = x * (1.0f / 65536.0f);
> +
> + /* Instead of [0...1] for ARGB, we want [0...255],
> + * multiply alpha with 255 and the color channels
> + * also get multiplied by the alpha multiplier.
> + */
> + f.a = 255.f * (walker->a_s * y + walker->a_b);
> + f.r = f.a * (walker->r_s * y + walker->r_b);
> + f.g = f.a * (walker->g_s * y + walker->g_b);
> + f.b = f.a * (walker->b_s * y + walker->b_b);
>
> - v = ((a8 << 24) & 0xff000000) |
> - ((r8 << 16) & 0x00ff0000) |
> - ((g8 << 8) & 0x0000ff00) |
> - ((b8 >> 0) & 0x000000ff);
> + return (((uint8_t)(f.a + .5f) << 24) & 0xff000000) |
> + (((uint8_t)(f.r + .5f) << 16) & 0x00ff0000) |
> + (((uint8_t)(f.g + .5f) << 8) & 0x0000ff00) |
> + (((uint8_t)(f.b + .5f) >> 0) & 0x000000ff);
> +}
> +
> +void
> +_pixman_gradient_walker_write_narrow (pixman_gradient_walker_t *walker,
> + pixman_fixed_48_16_t x,
> + uint32_t *buffer)
> +{
> + *buffer = pixman_gradient_walker_pixel_32 (walker, x);
> +}
> +
> +void
> +_pixman_gradient_walker_write_wide (pixman_gradient_walker_t *walker,
> + pixman_fixed_48_16_t x,
> + uint32_t *buffer)
> +{
> + *(argb_t *)buffer = pixman_gradient_walker_pixel_float (walker, x);
> +}
> +
> +void
> +_pixman_gradient_walker_fill_narrow (pixman_gradient_walker_t *walker,
> + pixman_fixed_48_16_t x,
> + uint32_t *buffer,
> + uint32_t *end)
> +{
> + register uint32_t color;
> +
> + color = pixman_gradient_walker_pixel_32 (walker, x);
> + while (buffer < end)
> + *buffer++ = color;
> +}
> +
> +void
> +_pixman_gradient_walker_fill_wide (pixman_gradient_walker_t *walker,
> + pixman_fixed_48_16_t x,
> + uint32_t *buffer,
> + uint32_t *end)
> +{
> + register argb_t color;
> + argb_t *buffer_wide = (argb_t *)buffer;
> + argb_t *end_wide = (argb_t *)end;
>
> - return v;
> + color = pixman_gradient_walker_pixel_float (walker, x);
> + while (buffer_wide < end_wide)
> + *buffer_wide++ = color;
> }
> diff --git a/pixman/pixman-linear-gradient.c b/pixman/pixman-linear-gradient.c
> index 40c8c9f37dbe..3f528508a188 100644
> --- a/pixman/pixman-linear-gradient.c
> +++ b/pixman/pixman-linear-gradient.c
> @@ -89,8 +89,11 @@ linear_gradient_is_horizontal (pixman_image_t *image,
> }
>
> static uint32_t *
> -linear_get_scanline_narrow (pixman_iter_t *iter,
> - const uint32_t *mask)
> +linear_get_scanline (pixman_iter_t *iter,
> + const uint32_t *mask,
> + int Bpp,
> + pixman_gradient_walker_write_t write_pixel,
> + pixman_gradient_walker_fill_t fill_pixel)
> {
> pixman_image_t *image = iter->image;
> int x = iter->x;
> @@ -103,7 +106,7 @@ linear_get_scanline_narrow (pixman_iter_t *iter,
> pixman_fixed_48_16_t dx, dy;
> gradient_t *gradient = (gradient_t *)image;
> linear_gradient_t *linear = (linear_gradient_t *)image;
> - uint32_t *end = buffer + width;
> + uint32_t *end = buffer + width * (Bpp / 4);
> pixman_gradient_walker_t walker;
>
> _pixman_gradient_walker_init (&walker, gradient, image->common.repeat);
> @@ -137,7 +140,7 @@ linear_get_scanline_narrow (pixman_iter_t *iter,
> if (l == 0 || unit.vector[2] == 0)
> {
> /* affine transformation only */
> - pixman_fixed_32_32_t t, next_inc;
> + pixman_fixed_32_32_t t, next_inc;
> double inc;
>
> if (l == 0 || v.vector[2] == 0)
> @@ -152,7 +155,7 @@ linear_get_scanline_narrow (pixman_iter_t *iter,
> invden = pixman_fixed_1 * (double) pixman_fixed_1 /
> (l * (double) v.vector[2]);
> v2 = v.vector[2] * (1. / pixman_fixed_1);
> - t = ((dx * v.vector[0] + dy * v.vector[1]) -
> + t = ((dx * v.vector[0] + dy * v.vector[1]) -
> (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
> inc = (dx * unit.vector[0] + dy * unit.vector[1]) * invden;
> }
> @@ -160,11 +163,7 @@ linear_get_scanline_narrow (pixman_iter_t *iter,
>
> if (((pixman_fixed_32_32_t )(inc * width)) == 0)
> {
> - register uint32_t color;
> -
> - color = _pixman_gradient_walker_pixel (&walker, t);
> - while (buffer < end)
> - *buffer++ = color;
> + fill_pixel (&walker, t, buffer, end);
> }
> else
> {
> @@ -175,12 +174,11 @@ linear_get_scanline_narrow (pixman_iter_t *iter,
> {
> if (!mask || *mask++)
> {
> - *buffer = _pixman_gradient_walker_pixel (&walker,
> - t + next_inc);
> + write_pixel (&walker, t + next_inc, buffer);
> }
> i++;
> next_inc = inc * i;
> - buffer++;
> + buffer += (Bpp / 4);
> }
> }
> }
> @@ -202,14 +200,14 @@ linear_get_scanline_narrow (pixman_iter_t *iter,
> invden = pixman_fixed_1 * (double) pixman_fixed_1 /
> (l * (double) v.vector[2]);
> v2 = v.vector[2] * (1. / pixman_fixed_1);
> - t = ((dx * v.vector[0] + dy * v.vector[1]) -
> + t = ((dx * v.vector[0] + dy * v.vector[1]) -
> (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
> }
>
> - *buffer = _pixman_gradient_walker_pixel (&walker, t);
> + write_pixel (&walker, t, buffer);
> }
>
> - ++buffer;
> + buffer += (Bpp / 4);
>
> v.vector[0] += unit.vector[0];
> v.vector[1] += unit.vector[1];
> @@ -223,14 +221,21 @@ linear_get_scanline_narrow (pixman_iter_t *iter,
> }
>
> static uint32_t *
> -linear_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
> +linear_get_scanline_narrow (pixman_iter_t *iter,
> + const uint32_t *mask)
> {
> - uint32_t *buffer = linear_get_scanline_narrow (iter, NULL);
> + return linear_get_scanline (iter, mask, 4,
> + _pixman_gradient_walker_write_narrow,
> + _pixman_gradient_walker_fill_narrow);
> +}
>
> - pixman_expand_to_float (
> - (argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
>
> - return buffer;
> +static uint32_t *
> +linear_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
> +{
> + return linear_get_scanline (iter, NULL, 16,
> + _pixman_gradient_walker_write_wide,
> + _pixman_gradient_walker_fill_wide);
> }
>
> void
> diff --git a/pixman/pixman-private.h b/pixman/pixman-private.h
> index 73a54146ddb1..1bd969591b34 100644
> --- a/pixman/pixman-private.h
> +++ b/pixman/pixman-private.h
> @@ -363,9 +363,38 @@ void
> _pixman_gradient_walker_reset (pixman_gradient_walker_t *walker,
> pixman_fixed_48_16_t pos);
>
> -uint32_t
> -_pixman_gradient_walker_pixel (pixman_gradient_walker_t *walker,
> - pixman_fixed_48_16_t x);
> +typedef void (*pixman_gradient_walker_write_t) (
> + pixman_gradient_walker_t *walker,
> + pixman_fixed_48_16_t x,
> + uint32_t *buffer);
> +
> +void
> +_pixman_gradient_walker_write_narrow(pixman_gradient_walker_t *walker,
> + pixman_fixed_48_16_t x,
> + uint32_t *buffer);
> +
> +void
> +_pixman_gradient_walker_write_wide(pixman_gradient_walker_t *walker,
> + pixman_fixed_48_16_t x,
> + uint32_t *buffer);
> +
> +typedef void (*pixman_gradient_walker_fill_t) (
> + pixman_gradient_walker_t *walker,
> + pixman_fixed_48_16_t x,
> + uint32_t *buffer,
> + uint32_t *end);
> +
> +void
> +_pixman_gradient_walker_fill_narrow(pixman_gradient_walker_t *walker,
> + pixman_fixed_48_16_t x,
> + uint32_t *buffer,
> + uint32_t *end);
> +
> +void
> +_pixman_gradient_walker_fill_wide(pixman_gradient_walker_t *walker,
> + pixman_fixed_48_16_t x,
> + uint32_t *buffer,
> + uint32_t *end);
>
> /*
> * Edges
> diff --git a/pixman/pixman-radial-gradient.c b/pixman/pixman-radial-gradient.c
> index 6a217963da18..0367d78424d9 100644
> --- a/pixman/pixman-radial-gradient.c
> +++ b/pixman/pixman-radial-gradient.c
> @@ -66,15 +66,18 @@ fdot (double x1,
> return x1 * x2 + y1 * y2 + z1 * z2;
> }
>
> -static uint32_t
> -radial_compute_color (double a,
> - double b,
> - double c,
> - double inva,
> - double dr,
> - double mindr,
> - pixman_gradient_walker_t *walker,
> - pixman_repeat_t repeat)
> +static void
> +radial_write_color (double a,
> + double b,
> + double c,
> + double inva,
> + double dr,
> + double mindr,
> + pixman_gradient_walker_t *walker,
> + pixman_repeat_t repeat,
> + int Bpp,
> + pixman_gradient_walker_write_t write_pixel,
> + uint32_t *buffer)
> {
> /*
> * In this function error propagation can lead to bad results:
> @@ -99,21 +102,25 @@ radial_compute_color (double a,
> double t;
>
> if (b == 0)
> - return 0;
> + {
> + memset (buffer, 0, Bpp);
> + return;
> + }
>
> t = pixman_fixed_1 / 2 * c / b;
> if (repeat == PIXMAN_REPEAT_NONE)
> {
> if (0 <= t && t <= pixman_fixed_1)
> - return _pixman_gradient_walker_pixel (walker, t);
> + return write_pixel (walker, t, buffer);
> }
> else
> {
> if (t * dr >= mindr)
> - return _pixman_gradient_walker_pixel (walker, t);
> + return write_pixel (walker, t, buffer);
> }
>
> - return 0;
> + memset (buffer, 0, Bpp);
> + return;
> }
>
> discr = fdot (b, a, 0, b, -c, 0);
> @@ -139,24 +146,28 @@ radial_compute_color (double a,
> if (repeat == PIXMAN_REPEAT_NONE)
> {
> if (0 <= t0 && t0 <= pixman_fixed_1)
> - return _pixman_gradient_walker_pixel (walker, t0);
> + return write_pixel (walker, t0, buffer);
> else if (0 <= t1 && t1 <= pixman_fixed_1)
> - return _pixman_gradient_walker_pixel (walker, t1);
> + return write_pixel (walker, t1, buffer);
> }
> else
> {
> if (t0 * dr >= mindr)
> - return _pixman_gradient_walker_pixel (walker, t0);
> + return write_pixel (walker, t0, buffer);
> else if (t1 * dr >= mindr)
> - return _pixman_gradient_walker_pixel (walker, t1);
> + return write_pixel (walker, t1, buffer);
> }
> }
>
> - return 0;
> + memset (buffer, 0, Bpp);
> + return;
> }
>
> static uint32_t *
> -radial_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
> +radial_get_scanline (pixman_iter_t *iter,
> + const uint32_t *mask,
> + int Bpp,
> + pixman_gradient_walker_write_t write_pixel)
> {
> /*
> * Implementation of radial gradients following the PDF specification.
> @@ -247,7 +258,7 @@ radial_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
>
> gradient_t *gradient = (gradient_t *)image;
> radial_gradient_t *radial = (radial_gradient_t *)image;
> - uint32_t *end = buffer + width;
> + uint32_t *end = buffer + width * (Bpp / 4);
> pixman_gradient_walker_t walker;
> pixman_vector_t v, unit;
>
> @@ -330,18 +341,21 @@ radial_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
> {
> if (!mask || *mask++)
> {
> - *buffer = radial_compute_color (radial->a, b, c,
> - radial->inva,
> - radial->delta.radius,
> - radial->mindr,
> - &walker,
> - image->common.repeat);
> + radial_write_color (radial->a, b, c,
> + radial->inva,
> + radial->delta.radius,
> + radial->mindr,
> + &walker,
> + image->common.repeat,
> + Bpp,
> + write_pixel,
> + buffer);
> }
>
> b += db;
> c += dc;
> dc += ddc;
> - ++buffer;
> + buffer += (Bpp / 4);
> }
> }
> else
> @@ -375,20 +389,23 @@ radial_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
> pdx, pdy, radial->c1.radius);
> /* / pixman_fixed_1 / pixman_fixed_1 */
>
> - *buffer = radial_compute_color (radial->a, b, c,
> - radial->inva,
> - radial->delta.radius,
> - radial->mindr,
> - &walker,
> - image->common.repeat);
> + radial_write_color (radial->a, b, c,
> + radial->inva,
> + radial->delta.radius,
> + radial->mindr,
> + &walker,
> + image->common.repeat,
> + Bpp,
> + write_pixel,
> + buffer);
> }
> else
> {
> - *buffer = 0;
> + memset (buffer, 0, Bpp);
> }
> }
>
> - ++buffer;
> + buffer += (Bpp / 4);
>
> v.vector[0] += unit.vector[0];
> v.vector[1] += unit.vector[1];
> @@ -401,14 +418,17 @@ radial_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
> }
>
> static uint32_t *
> -radial_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
> +radial_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
> {
> - uint32_t *buffer = radial_get_scanline_narrow (iter, NULL);
> -
> - pixman_expand_to_float (
> - (argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
> + return radial_get_scanline (iter, mask, 4,
> + _pixman_gradient_walker_write_narrow);
> +}
>
> - return buffer;
> +static uint32_t *
> +radial_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
> +{
> + return radial_get_scanline (iter, NULL, 16,
> + _pixman_gradient_walker_write_wide);
> }
>
> void
> @@ -422,11 +442,11 @@ _pixman_radial_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter)
>
> PIXMAN_EXPORT pixman_image_t *
> pixman_image_create_radial_gradient (const pixman_point_fixed_t * inner,
> - const pixman_point_fixed_t * outer,
> - pixman_fixed_t inner_radius,
> - pixman_fixed_t outer_radius,
> - const pixman_gradient_stop_t *stops,
> - int n_stops)
> + const pixman_point_fixed_t * outer,
> + pixman_fixed_t inner_radius,
> + pixman_fixed_t outer_radius,
> + const pixman_gradient_stop_t *stops,
> + int n_stops)
> {
> pixman_image_t *image;
> radial_gradient_t *radial;
>
> _______________________________________________
> Pixman mailing list
> Pixman at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/pixman
More information about the Pixman
mailing list