[Pixman] [PATCH 1/4] Change conditions for setting FAST_PATH_SAMPLES_COVER_CLIP flags
Oded Gabbay
oded.gabbay at gmail.com
Tue Sep 8 23:39:07 PDT 2015
On Fri, Sep 4, 2015 at 5:09 AM, Ben Avison <bavison at riscosopen.org> wrote:
> As discussed in
> http://lists.freedesktop.org/archives/pixman/2015-August/003905.html
>
> the 8 * pixman_fixed_e adjustment which was applied to the transformed
> coordinates is a legacy of rounding errors which used to occur in old
> versions of Pixman, but which no longer apply. For any affine transform,
> you are now guaranteed to get the same result by transforming the upper
> coordinate as though you transform the lower coordinate and add (size-1)
> steps of the increment in source coordinate space. No projective
> transform routines use the COVER_CLIP flags, so they cannot be affected.
>
> However, removing the 8 * pixman_fixed_e border exposes a bug in the
> calculation of the COVER_CLIP_NEAREST flag. Because the half-way cases
> round down, an adjustment by pixman_fixed_e is needed. This patch
> applies this adjustment.
>
> Many bilinear fast paths currently assume that COVER_CLIP_BILINEAR is
> not set when the transformed upper coordinate corresponds exactly to the
> last row/pixel in source space. This is due to a detail of many current
> implementations - they assume they can always load the pixel after the one
> you get by dividing the coordinate by 2^16 with rounding to -infinity.
>
> To avoid causing these implementations to exceed array bounds in these
> cases, the COVER_CLIP_BILINEAR flag retains this feature in this patch.
> Subsequent patches deal with removing this assumption, to enable cover
> fast paths to be used in the maximum number of cases.
>
> Proof:
>
> All implementations must give the same numerical results as
> bits_image_fetch_pixel_nearest() / bits_image_fetch_pixel_bilinear().
>
> The former does
> int x0 = pixman_fixed_to_int (x - pixman_fixed_e);
> which maps directly to the new test for the nearest flag, when you consider
> that x0 must fall in the interval [0,width).
>
> The latter does
> x1 = x - pixman_fixed_1 / 2;
> x1 = pixman_fixed_to_int (x1);
> x2 = x1 + 1;
> but then x2 is brought back in range by the repeat() function, so it can't
> stray beyond the end of the source line. When you write a cover path, you
> are effectively omitting the repeat() function. The weight applied to the
> pixel at offset x2 will be zero, so if you skip the load and leave the
> pixel value undefined, the numerical result is unaffected, but you have
> avoided a memory access that could potentially cause a page fault. If
> however, we assume that the implementation loads from offset x2
> unconditionally, then we need
> x1 >= 0
> x2 < width
> so
> x - pixman_fixed_1 / 2 >= 0
> x - pixman_fixed_1 / 2 + pixman_fixed_1 < width * pixman_fixed_1
> so
> pixman_fixed_to_int (x - pixman_fixed_1 / 2) >= 0
> pixman_fixed_to_int (x + pixman_fixed_1 / 2) < width
> which matches the source code lines for the bilinear case, once you delete
> the lines that apply the 8 * pixman_fixed_e border.
> ---
> pixman/pixman.c | 17 ++++-------------
> test/affine-bench.c | 16 ++++++++++------
> 2 files changed, 14 insertions(+), 19 deletions(-)
>
> diff --git a/pixman/pixman.c b/pixman/pixman.c
> index a07c577..f932eac 100644
> --- a/pixman/pixman.c
> +++ b/pixman/pixman.c
> @@ -497,21 +497,12 @@ analyze_extent (pixman_image_t *image,
> if (!compute_transformed_extents (transform, extents, &transformed))
> return FALSE;
>
> - /* Expand the source area by a tiny bit so account of different rounding that
> - * may happen during sampling. Note that (8 * pixman_fixed_e) is very far from
> - * 0.5 so this won't cause the area computed to be overly pessimistic.
> - */
> - transformed.x1 -= 8 * pixman_fixed_e;
> - transformed.y1 -= 8 * pixman_fixed_e;
> - transformed.x2 += 8 * pixman_fixed_e;
> - transformed.y2 += 8 * pixman_fixed_e;
> -
> if (image->common.type == BITS)
> {
> - if (pixman_fixed_to_int (transformed.x1) >= 0 &&
> - pixman_fixed_to_int (transformed.y1) >= 0 &&
> - pixman_fixed_to_int (transformed.x2) < image->bits.width &&
> - pixman_fixed_to_int (transformed.y2) < image->bits.height)
> + if (pixman_fixed_to_int (transformed.x1 - pixman_fixed_e) >= 0 &&
> + pixman_fixed_to_int (transformed.y1 - pixman_fixed_e) >= 0 &&
> + pixman_fixed_to_int (transformed.x2 - pixman_fixed_e) < image->bits.width &&
> + pixman_fixed_to_int (transformed.y2 - pixman_fixed_e) < image->bits.height)
Shouldn't the above two lines be with '+' ?
i.e:
pixman_fixed_to_int (transformed.x2 + pixman_fixed_e) < image->bits.width &&
pixman_fixed_to_int (transformed.y2 + pixman_fixed_e) < image->bits.height)
> {
> *flags |= FAST_PATH_SAMPLES_COVER_CLIP_NEAREST;
> }
> diff --git a/test/affine-bench.c b/test/affine-bench.c
> index 9e0121e..697684b 100644
> --- a/test/affine-bench.c
> +++ b/test/affine-bench.c
> @@ -396,13 +396,17 @@ main (int argc, char *argv[])
> }
>
> compute_transformed_extents (&binfo.transform, &dest_box, &transformed);
> - /* The source area is expanded by a tiny bit (8/65536th pixel)
> - * to match the calculation of the COVER_CLIP flags in analyze_extent()
> + xmin = pixman_fixed_to_int (transformed.x1 - pixman_fixed_1 / 2);
> + ymin = pixman_fixed_to_int (transformed.y1 - pixman_fixed_1 / 2);
> + xmax = pixman_fixed_to_int (transformed.x2 + pixman_fixed_1 / 2);
> + ymax = pixman_fixed_to_int (transformed.y2 + pixman_fixed_1 / 2);
> + /* Note: when FAST_PATH_SAMPLES_COVER_CLIP_BILINEAR is retired, the upper
> + * limits can be reduced to:
> + * xmax = pixman_fixed_to_int (transformed.x2 + pixman_fixed_1 / 2 - pixman_fixed_e);
> + * ymax = pixman_fixed_to_int (transformed.y2 + pixman_fixed_1 / 2 - pixman_fixed_e);
> + * This is equivalent to subtracting 0.5 and rounding up, rather than
> + * subtracting 0.5, rounding down and adding 1.
> */
> - xmin = pixman_fixed_to_int (transformed.x1 - 8 * pixman_fixed_e - pixman_fixed_1 / 2);
> - ymin = pixman_fixed_to_int (transformed.y1 - 8 * pixman_fixed_e - pixman_fixed_1 / 2);
> - xmax = pixman_fixed_to_int (transformed.x2 + 8 * pixman_fixed_e + pixman_fixed_1 / 2);
> - ymax = pixman_fixed_to_int (transformed.y2 + 8 * pixman_fixed_e + pixman_fixed_1 / 2);
> binfo.src_x = -xmin;
> binfo.src_y = -ymin;
>
> --
> 1.7.5.4
>
> _______________________________________________
> Pixman mailing list
> Pixman at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/pixman
Unfortunately, I don't understand 90% of what this patch talks about,
so I hope someone else will review this as well.
Oded
More information about the Pixman
mailing list