[Pixman] [PATCH 2/4] Implement basic dithering for the wide pipeline

Basile Clement basile-pixman at clement.pm
Mon Dec 3 14:55:30 UTC 2018


This patch implements dithering in pixman.  A "dither" property is added
to BITS images, which is used to:

 - Force rendering to the image to go through the floating point
   pipeline.  Note that this is different from FAST_PATH_NARROW_FORMAT
   as it should not enable the floating point pipeline when reading from
   the image.

 - Enable dithering in dest_write_back_wide.  The dithering uses the
   destination format to determine noise amplitude.

This does not change pixman's behavior when dithering is disabled (the
default).

Additional types and functions are added to the public API:

 - The `pixman_dither_t` enum exposes the available dithering methods.
   Currently a single dithering method based on 8x8 Bayer matrices is
   implemented (PIXMAN_DITHER_BAYER_8).  This is known to cause
   flickering issues on some laptop screens, but is provided as a
   baseline implementation.  The PIXMAN_DITHER_FAST, PIXMAN_DITHER_GOOD
   and PIXMAN_DITHER_BEST aliases are provided and should be used to
   benefit for future specializations.

 - The `pixman_image_set_dither` function allows to set the dithering
   method to use when rendering to a bits image.

 - The `pixman_image_set_dither_offset` function allows to set a
   vertical and horizontal offsets for the dither matrix.  This can be
   used after scrolling to ensure a consistent spatial positioning of
   the dither matrix.
---
 pixman/pixman-bits-image.c      | 121 ++++++++++++++++++++++++++++++++
 pixman/pixman-general.c         |   3 +-
 pixman/pixman-image.c           |  27 +++++++
 pixman/pixman-linear-gradient.c |  11 +--
 pixman/pixman-private.h         |   4 ++
 pixman/pixman.h                 |  14 ++++
 6 files changed, 174 insertions(+), 6 deletions(-)

diff --git a/pixman/pixman-bits-image.c b/pixman/pixman-bits-image.c
index 9fb91ff..386a471 100644
--- a/pixman/pixman-bits-image.c
+++ b/pixman/pixman-bits-image.c
@@ -847,6 +847,111 @@ dest_write_back_narrow (pixman_iter_t *iter)
     iter->y++;
 }
 
+static const float
+dither_factor_bayer_8 (int x, int y)
+{
+    uint32_t m;
+
+    y ^= x;
+
+    /* Compute reverse(interleave(xor(x mod n, y mod n), x mod n))
+     * Here n = 8 and `mod n` is the bottom 3 bits.
+     */
+    m = ((y & 0b001) << 5) | ((x & 0b001) << 4) |
+	((y & 0b010) << 2) | ((x & 0b010) << 1) |
+	((y & 0b100) >> 1) | ((x & 0b100) >> 2);
+
+    /* m is in range [0, 63].  We scale it to [0, 63.0f/64.0f], then
+     * shift it to then shift it to [1.0f/128.0f, 127.0f/128.0f] so
+     * that 0 < d < 1.  This ensures exact values are not changed by
+     * dithering.
+     */
+    return (float)(m) * (1 / 64.0f) + (1.0f / 128.0f);
+}
+
+typedef float (* dither_factor_t)(int x, int y);
+
+static force_inline float
+dither_apply_channel (float f, float d, float s)
+{
+    /* float_to_unorm splits the [0, 1] segment in (1 << n_bits)
+     * subsections of equal length; however unorm_to_float does not
+     * map to the center of those sections.  In fact, pixel value u is
+     * mapped to:
+     *
+     *       u              u              u               1
+     * -------------- = ---------- + -------------- * ----------
+     *  2^n_bits - 1     2^n_bits     2^n_bits - 1     2^n_bits
+     *
+     * Hence if f = u / (2^n_bits - 1) is exactly representable on a
+     * n_bits palette, all the numbers between
+     *
+     *     u
+     * ----------  =  f - f * 2^n_bits = f + (0 - f) * 2^n_bits
+     *  2^n_bits
+     *
+     *  and
+     *
+     *    u + 1
+     * ---------- = f - (f - 1) * 2^n_bits = f + (1 - f) * 2^n_bits
+     *  2^n_bits
+     *
+     * are also mapped back to u.
+     *
+     * Hence the following calculation ensures that we add as much
+     * noise as possible without perturbing values which are exactly
+     * representable in the target colorspace.
+     */
+    return f + (d - f) * s;
+}
+
+static force_inline float
+dither_compute_scale (int n_bits)
+{
+    if (n_bits == 0)
+	return 0.f;
+
+    return 1.f / (float)(1 << n_bits);
+}
+
+static const uint32_t *
+dither_apply_unified (pixman_iter_t *iter, dither_factor_t factor)
+{
+    bits_image_t        *image  = &iter->image->bits;
+    int                  x      = iter->x + image->dither_offset_x;
+    int                  y      = iter->y + image->dither_offset_y;
+    int                  width  = iter->width;
+    argb_t              *buffer = (argb_t *)iter->buffer;
+
+    pixman_format_code_t format = image->format;
+    int                  a_size = PIXMAN_FORMAT_A (format);
+    int                  r_size = PIXMAN_FORMAT_R (format);
+    int                  g_size = PIXMAN_FORMAT_G (format);
+    int                  b_size = PIXMAN_FORMAT_B (format);
+
+    float a_scale = dither_compute_scale (a_size);
+    float r_scale = dither_compute_scale (r_size);
+    float g_scale = dither_compute_scale (g_size);
+    float b_scale = dither_compute_scale (b_size);
+
+    int   i;
+    float d;
+
+    for (i = 0; i < width; ++i)
+    {
+	d = factor (x + i, y);
+
+	buffer->a = dither_apply_channel (buffer->a, d, a_scale);
+	buffer->r = dither_apply_channel (buffer->r, d, r_scale);
+	buffer->g = dither_apply_channel (buffer->g, d, g_scale);
+	buffer->b = dither_apply_channel (buffer->b, d, b_scale);
+
+	buffer++;
+    }
+
+    return iter->buffer;
+}
+
 static void
 dest_write_back_wide (pixman_iter_t *iter)
 {
@@ -856,6 +961,19 @@ dest_write_back_wide (pixman_iter_t *iter)
     int             width  = iter->width;
     const uint32_t *buffer = iter->buffer;
 
+    switch (image->dither)
+    {
+    case PIXMAN_DITHER_NONE:
+	break;
+
+    case PIXMAN_DITHER_FAST:
+    case PIXMAN_DITHER_GOOD:
+    case PIXMAN_DITHER_BEST:
+    case PIXMAN_DITHER_BAYER_8:
+	buffer = dither_apply_unified (iter, dither_factor_bayer_8);
+	break;
+    }
+
     image->store_scanline_float (image, x, y, width, buffer);
 
     if (image->common.alpha_map)
@@ -971,6 +1089,9 @@ _pixman_bits_image_init (pixman_image_t *     image,
     image->bits.height = height;
     image->bits.bits = bits;
     image->bits.free_me = free_me;
+    image->bits.dither = PIXMAN_DITHER_NONE;
+    image->bits.dither_offset_x = 0;
+    image->bits.dither_offset_y = 0;
     image->bits.read_func = NULL;
     image->bits.write_func = NULL;
     image->bits.rowstride = rowstride;
diff --git a/pixman/pixman-general.c b/pixman/pixman-general.c
index 6141cb0..7d74f98 100644
--- a/pixman/pixman-general.c
+++ b/pixman/pixman-general.c
@@ -141,7 +141,8 @@ general_composite_rect  (pixman_implementation_t *imp,
     if ((src_image->common.flags & FAST_PATH_NARROW_FORMAT)		     &&
 	(!mask_image || mask_image->common.flags & FAST_PATH_NARROW_FORMAT)  &&
 	(dest_image->common.flags & FAST_PATH_NARROW_FORMAT)		     &&
-	!(operator_needs_division (op)))
+	!(operator_needs_division (op))                                      &&
+	(dest_image->bits.dither == PIXMAN_DITHER_NONE))
     {
 	width_flag = ITER_NARROW;
 	Bpp = 4;
diff --git a/pixman/pixman-image.c b/pixman/pixman-image.c
index 7a851e2..5ac785c 100644
--- a/pixman/pixman-image.c
+++ b/pixman/pixman-image.c
@@ -684,6 +684,33 @@ pixman_image_set_repeat (pixman_image_t *image,
     image_property_changed (image);
 }
 
+PIXMAN_EXPORT void
+pixman_image_set_dither (pixman_image_t *image,
+			 pixman_dither_t dither)
+{
+    if (image->type == BITS)
+    {
+	if (image->bits.dither == dither)
+	    return;
+
+	image->bits.dither = dither;
+
+	image_property_changed (image);
+    }
+}
+
+PIXMAN_EXPORT void
+pixman_image_set_dither_offset (pixman_image_t *image,
+				int             offset_x,
+				int             offset_y)
+{
+    if (image->type == BITS)
+    {
+	image->bits.dither_offset_x = offset_x;
+	image->bits.dither_offset_y = offset_y;
+    }
+}
+
 PIXMAN_EXPORT pixman_bool_t
 pixman_image_set_filter (pixman_image_t *      image,
                          pixman_filter_t       filter,
diff --git a/pixman/pixman-linear-gradient.c b/pixman/pixman-linear-gradient.c
index a5d8be9..3ce02b6 100644
--- a/pixman/pixman-linear-gradient.c
+++ b/pixman/pixman-linear-gradient.c
@@ -241,13 +241,14 @@ linear_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
 void
 _pixman_linear_gradient_iter_init (pixman_image_t *image, pixman_iter_t  *iter)
 {
-    if (linear_gradient_is_horizontal (
+    /* Disable horizontal gradient optimization for wide iterators as
+     * it interferes with dithering.
+     */
+    if ((iter->iter_flags & ITER_NARROW) &&
+	linear_gradient_is_horizontal (
 	    iter->image, iter->x, iter->y, iter->width, iter->height))
     {
-	if (iter->iter_flags & ITER_NARROW)
-	    linear_get_scanline_narrow (iter, NULL);
-	else
-	    linear_get_scanline_wide (iter, NULL);
+	linear_get_scanline_narrow (iter, NULL);
 
 	iter->get_scanline = _pixman_iter_get_scanline_noop;
     }
diff --git a/pixman/pixman-private.h b/pixman/pixman-private.h
index 0a07467..bb3a649 100644
--- a/pixman/pixman-private.h
+++ b/pixman/pixman-private.h
@@ -180,6 +180,10 @@ struct bits_image
     uint32_t *                 free_me;
     int                        rowstride;  /* in number of uint32_t's */
 
+    pixman_dither_t            dither;
+    uint32_t                   dither_offset_y;
+    uint32_t                   dither_offset_x;
+
     fetch_scanline_t           fetch_scanline_32;
     fetch_pixel_32_t	       fetch_pixel_32;
     store_scanline_t           store_scanline_32;
diff --git a/pixman/pixman.h b/pixman/pixman.h
index d644589..65d676f 100644
--- a/pixman/pixman.h
+++ b/pixman/pixman.h
@@ -285,6 +285,15 @@ typedef enum
     PIXMAN_REPEAT_REFLECT
 } pixman_repeat_t;
 
+typedef enum
+{
+    PIXMAN_DITHER_NONE,
+    PIXMAN_DITHER_FAST,
+    PIXMAN_DITHER_GOOD,
+    PIXMAN_DITHER_BEST,
+    PIXMAN_DITHER_BAYER_8,
+} pixman_dither_t;
+
 typedef enum
 {
     PIXMAN_FILTER_FAST,
@@ -826,6 +835,11 @@ pixman_bool_t   pixman_image_set_transform           (pixman_image_t
 						      const pixman_transform_t     *transform);
 void            pixman_image_set_repeat              (pixman_image_t               *image,
 						      pixman_repeat_t               repeat);
+void            pixman_image_set_dither              (pixman_image_t               *image,
+						      pixman_dither_t               dither);
+void            pixman_image_set_dither_offset       (pixman_image_t               *image,
+						      int                           offset_x,
+						      int                           offset_y);
 pixman_bool_t   pixman_image_set_filter              (pixman_image_t               *image,
 						      pixman_filter_t               filter,
 						      const pixman_fixed_t         *filter_params,
-- 
2.19.2



More information about the Pixman mailing list