[Pixman] [PATCH 3/3] Add an iterator that can fetch bilinearly scaled images
Søren Sandmann Pedersen
soren.sandmann at gmail.com
Mon Jul 29 02:03:29 PDT 2013
This new iterator works in a separable way; that is, for a destination
scaline, it scales the two involved source scanlines and then caches
them so that they can be reused for the next destination scanlines.
This scheme saves a substantial amount of arithmetic for larger
scalings; the time-per-pixel as reported by scaling-bench (running
with PIXMAN_DISABLE="sse2 mmx") is graphed here:
http://people.freedesktop.org/~sandmann/scaling-graph.png
where the blue graph is the times before this patch, and the red graph
is after.
As the graph shows, this patch is a slowdown for scaling ratios below
0.6, but not a big one, and large downscalings with the bilinear
filter is not an important usecase anyway since the results are ugly.
The graph is also available here:
http://i.imgur.com/Nibcyes.png
The data used to generate the graph is available here:
http://people.freedesktop.org/~sandmann/before
http://people.freedesktop.org/~sandmann/after
---
pixman/pixman-fast-path.c | 192 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 192 insertions(+), 0 deletions(-)
diff --git a/pixman/pixman-fast-path.c b/pixman/pixman-fast-path.c
index 3982dce..0c2ca72 100644
--- a/pixman/pixman-fast-path.c
+++ b/pixman/pixman-fast-path.c
@@ -2261,6 +2261,188 @@ fast_write_back_r5g6b5 (pixman_iter_t *iter)
}
}
+typedef struct
+{
+ int y;
+ uint64_t * buffer;
+} line_t;
+
+typedef struct
+{
+ line_t line0;
+ line_t line1;
+ pixman_fixed_t y;
+ pixman_fixed_t x;
+ uint64_t data[1];
+} bilinear_info_t;
+
+static void
+fetch_horizontal (bits_image_t *image, line_t *line,
+ int y, pixman_fixed_t x, pixman_fixed_t ux, int n)
+{
+ uint32_t *bits = image->bits + y * image->rowstride;
+ int i;
+
+ for (i = 0; i < n; ++i)
+ {
+ int x0 = pixman_fixed_to_int (x);
+ int x1 = x0 + 1;
+ int32_t dist_x;
+
+ uint32_t left = *(bits + x0);
+ uint32_t right = *(bits + x1);
+ uint32_t lag, rag;
+ uint32_t lrb, rrb;
+ uint64_t lagrb, ragrb;
+ uint64_t a, r, g, b;
+ uint64_t p;
+
+ dist_x = pixman_fixed_to_bilinear_weight (x);
+ dist_x <<= (8 - BILINEAR_INTERPOLATION_BITS);
+
+ lag = (left & 0xff00ff00);
+ lrb = (left & 0x00ff00ff);
+ rag = (right & 0xff00ff00);
+ rrb = (right & 0x00ff00ff);
+ lagrb = (((uint64_t)lag) << 24) | lrb;
+ ragrb = (((uint64_t)rag) << 24) | rrb;
+
+ p = (lagrb << 8) + dist_x * (ragrb - lagrb);
+
+ a = (p << 0) & 0xffff000000000000ULL;
+ r = (p << 16) & 0x0000ffff00000000ULL;
+ g = (p >> 16) & 0x00000000ffff0000ULL;
+ b = (p >> 0) & 0x000000000000ffffULL;
+
+ line->buffer[i] = a | r | g | b;
+
+ x += ux;
+ }
+
+ line->y = y;
+}
+
+static uint32_t *
+fast_fetch_bilinear_cover (pixman_iter_t *iter, const uint32_t *mask)
+{
+ pixman_fixed_t fx, ux;
+ bilinear_info_t *info = iter->data;
+ line_t *line0, *line1;
+ int y0, y1;
+ int32_t dist_y;
+ int i;
+
+ fx = info->x;
+ ux = iter->image->common.transform->matrix[0][0];
+
+ y0 = pixman_fixed_to_int (info->y);
+ y1 = y0 + 1;
+ dist_y = pixman_fixed_to_bilinear_weight (info->y);
+ dist_y <<= (8 - BILINEAR_INTERPOLATION_BITS);
+
+ line0 = &info->line0;
+ line1 = &info->line1;
+
+ if (line0->y != y0 || line1->y != y1)
+ {
+ if (line0->y == y1 || line1->y == y0)
+ {
+ line_t tmp = *line0;
+ *line0 = *line1;
+ *line1 = tmp;
+ }
+
+ if (line0->y != y0)
+ {
+ fetch_horizontal (
+ &iter->image->bits, line0, y0, fx, ux, iter->width);
+ }
+
+ if (line1->y != y1)
+ {
+ fetch_horizontal (
+ &iter->image->bits, line1, y1, fx, ux, iter->width);
+ }
+ }
+
+ for (i = 0; i < iter->width; ++i)
+ {
+ uint64_t top = line0->buffer[i];
+ uint64_t bot = line1->buffer[i];
+ uint64_t tag = (top & 0xffff0000ffff0000ULL) >> 16;
+ uint64_t bag = (bot & 0xffff0000ffff0000ULL) >> 16;
+ uint64_t trb = (top & 0x0000ffff0000ffffULL);
+ uint64_t brb = (bot & 0x0000ffff0000ffffULL);
+ uint64_t ag, rb;
+ uint64_t a, r, g, b;
+
+ ag = (tag << 8) + dist_y * (bag - tag);
+ rb = (trb << 8) + dist_y * (brb - trb);
+
+ a = ((ag >> 24) & 0xff000000);
+ r = ((rb >> 32) & 0x00ff0000);
+ g = ((ag >> 8) & 0x0000ff00);
+ b = ((rb >> 16) & 0x000000ff);
+
+ iter->buffer[i] = a | r | g | b;
+ }
+
+ info->y += iter->image->common.transform->matrix[1][1];
+
+ return iter->buffer;
+}
+
+static void
+bilinear_cover_iter_fini (pixman_iter_t *iter)
+{
+ free (iter->data);
+}
+
+static void
+fast_bilinear_cover_iter_init (pixman_iter_t *iter, const pixman_iter_info_t *iter_info)
+{
+ int width = iter->width;
+ bilinear_info_t *info;
+ pixman_vector_t v;
+
+ /* Reference point is the center of the pixel */
+ v.vector[0] = pixman_int_to_fixed (iter->x) + pixman_fixed_1 / 2;
+ v.vector[1] = pixman_int_to_fixed (iter->y) + pixman_fixed_1 / 2;
+ v.vector[2] = pixman_fixed_1;
+
+ if (!pixman_transform_point_3d (iter->image->common.transform, &v))
+ goto fail;
+
+ info = malloc (sizeof (*info) + (2 * width - 1) * sizeof (uint64_t));
+ if (!info)
+ goto fail;
+
+ info->x = v.vector[0] - pixman_fixed_1 / 2;
+ info->y = v.vector[1] - pixman_fixed_1 / 2;
+
+ /* It is safe to set the y coordinates to -1 initially
+ * because COVER_CLIP_BILINEAR ensures that we will only
+ * be asked to fetch lines in the [0, height) interval
+ */
+ info->line0.y = -1;
+ info->line0.buffer = &(info->data[0]);
+ info->line1.y = -1;
+ info->line1.buffer = &(info->data[width]);
+
+ iter->get_scanline = fast_fetch_bilinear_cover;
+ iter->fini = bilinear_cover_iter_fini;
+
+ iter->data = info;
+ return;
+
+fail:
+ /* Something went wrong, either a bad matrix or OOM; in such cases,
+ * we don't guarantee any particular rendering.
+ */
+ iter->get_scanline = _pixman_iter_get_scanline_noop;
+ iter->fini = bilinear_cover_iter_fini;
+}
+
#define IMAGE_FLAGS \
(FAST_PATH_STANDARD_FLAGS | FAST_PATH_ID_TRANSFORM | \
FAST_PATH_BITS_IMAGE | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST)
@@ -2280,6 +2462,16 @@ static const pixman_iter_info_t fast_iters[] =
_pixman_iter_init_bits_stride,
fast_dest_fetch_noop, fast_write_back_r5g6b5 },
+ { PIXMAN_a8r8g8b8,
+ (FAST_PATH_STANDARD_FLAGS |
+ FAST_PATH_SCALE_TRANSFORM |
+ FAST_PATH_BILINEAR_FILTER |
+ FAST_PATH_SAMPLES_COVER_CLIP_BILINEAR),
+ ITER_NARROW | ITER_SRC,
+ fast_bilinear_cover_iter_init,
+ NULL, NULL
+ },
+
{ PIXMAN_null },
};
--
1.7.1
More information about the Pixman
mailing list