Add iterators in the general implementation
authorSøren Sandmann Pedersen <ssp@redhat.com>
Fri, 10 Dec 2010 16:30:27 +0000 (11:30 -0500)
committerSøren Sandmann Pedersen <ssp@redhat.com>
Tue, 18 Jan 2011 17:42:25 +0000 (12:42 -0500)
We add a new structure called a pixman_iter_t that encapsulates the
information required to read scanlines from an image. It contains two
functions, get_scanline() and write_back(). The get_scanline()
function will generate pixels for the current scanline. For iterators
for source images, it will also advance to the next scanline. The
write_back() function is only called for destination images. Its
function is to write back the modified pixels to the image and then
advance to the next scanline.

When an iterator is initialized, it is passed this information:

   - The image to iterate

   - The rectangle to be iterated

   - A buffer that the iterator may (but is not required to) use. This
     buffer is guaranteed to have space for at least width pixels.

   - A flag indicating whether a8r8g8b8 or a16r16g16b16 pixels should
     be fetched

There are a number of (eventual) benefits to the iterators:

   - The initialization of the iterator can be virtualized such that
     implementations can plug in their own CPU specific get_scanline()
     and write_back() functions.

   - If an image is horizontal, it can simply plug in an appropriate
     get_scanline(). This way we can get rid of the annoying
     classify() virtual function.

   - In general, iterators can remember what they did on the last
     scanline, so for example a REPEAT_NONE image might reuse the same
     data for all the empty scanlines generated by the zero-extension.

   - More detailed information can be passed to iterator, allowing
     more specialized fetchers to be used.

   - We can fix the bug where destination filters and transformations
     are not currently being ignored as they should be.

However, this initial implementation is not optimized at all. We lose
several existing optimizations:

   - The ability to composite directly in the destination
   - The ability to only fetch one scanline for horizontal images
   - The ability to avoid fetching the src and mask for the CLEAR
     operator

Later patches will re-introduce these optimizations.

pixman/pixman-general.c

index 8130f16..ea27e80 100644 (file)
 #include "pixman-combine32.h"
 #include "pixman-private.h"
 
+typedef struct pixman_iter_t pixman_iter_t;
+typedef enum
+{
+    ITER_NARROW        = (1 << 0)
+} iter_flags_t;
+
+struct pixman_iter_t
+{
+    uint32_t *(* get_scanline) (pixman_iter_t *iter, const uint32_t *mask);
+    void      (* write_back)   (pixman_iter_t *iter);
+
+    pixman_image_t *    image;
+    uint32_t *          buffer;
+    int                 x, y;
+    int                 width;
+};
+
+static uint32_t *
+src_get_scanline_null (pixman_iter_t *iter, const uint32_t *mask)
+{
+    return NULL;
+}
+
+static uint32_t *
+src_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
+{
+    _pixman_image_get_scanline_32 (
+       iter->image, iter->x, iter->y++, iter->width, iter->buffer, mask);
+
+    return iter->buffer;
+}
+
+static uint32_t *
+src_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
+{
+    _pixman_image_get_scanline_64 (
+       iter->image, iter->x, iter->y++, iter->width, iter->buffer, mask);
+
+    return iter->buffer;
+}
+
+static void
+src_iter_init (pixman_implementation_t *imp,
+              pixman_iter_t *iter,
+              pixman_image_t *image,
+              int x, int y, int width, int height,
+              uint8_t *buffer, iter_flags_t flags)
+{
+    iter->image = image;
+    iter->x = x;
+    iter->y = y;
+    iter->width = width;
+    iter->buffer = (uint32_t *)buffer;
+
+    if (!image)
+       iter->get_scanline = src_get_scanline_null;
+    else if (flags & ITER_NARROW)
+       iter->get_scanline = src_get_scanline_narrow;
+    else
+       iter->get_scanline = src_get_scanline_wide;
+}
+
+static uint32_t *
+dest_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
+{
+    _pixman_image_get_scanline_32 (
+       iter->image, iter->x, iter->y, iter->width, iter->buffer, mask);
+
+    return iter->buffer;
+}
+
+static uint32_t *
+dest_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
+{
+    _pixman_image_get_scanline_64 (
+       iter->image, iter->x, iter->y, iter->width, iter->buffer, mask);
+
+    return iter->buffer;
+}
+
+static void
+write_back_narrow (pixman_iter_t *iter)
+{
+    _pixman_image_store_scanline_32 (
+       &iter->image->bits, iter->x, iter->y++, iter->width, iter->buffer);
+}
+
+static void
+write_back_wide (pixman_iter_t *iter)
+{
+    _pixman_image_store_scanline_64 (
+       &iter->image->bits, iter->x, iter->y++, iter->width, iter->buffer);
+}
+
+static void
+dest_iter_init (pixman_implementation_t *imp,
+               pixman_iter_t *iter,
+               pixman_image_t *image,
+               int x, int y, int width, int height,
+               uint8_t *buffer, iter_flags_t flags)
+{
+    iter->image = image;
+    iter->x = x;
+    iter->y = y;
+    iter->width = width;
+    iter->buffer = (uint32_t *)buffer;
+
+    if (flags & ITER_NARROW)
+    {
+       iter->get_scanline = dest_get_scanline_narrow;
+       iter->write_back = write_back_narrow;
+    }
+    else
+    {
+       iter->get_scanline = dest_get_scanline_wide;
+       iter->write_back = write_back_wide;
+    }
+}
+
 #define SCANLINE_BUFFER_LENGTH 8192
 
 static void
@@ -59,21 +178,25 @@ general_composite_rect  (pixman_implementation_t *imp,
     uint64_t stack_scanline_buffer[(SCANLINE_BUFFER_LENGTH * 3 + 7) / 8];
     uint8_t *scanline_buffer = (uint8_t *) stack_scanline_buffer;
     uint8_t *src_buffer, *mask_buffer, *dest_buffer;
-    fetch_scanline_t fetch_src = NULL, fetch_mask = NULL, fetch_dest = NULL;
+    pixman_iter_t src_iter, mask_iter, dest_iter;
     pixman_combine_32_func_t compose;
-    store_scanline_t store;
-    source_image_class_t src_class, mask_class;
     pixman_bool_t component_alpha;
-    uint32_t *bits;
-    int32_t stride;
-    int narrow, Bpp;
+    iter_flags_t narrow;
+    int Bpp;
     int i;
 
-    narrow =
-       (src->common.flags & FAST_PATH_NARROW_FORMAT)           &&
+    if ((src->common.flags & FAST_PATH_NARROW_FORMAT)          &&
        (!mask || mask->common.flags & FAST_PATH_NARROW_FORMAT) &&
-       (dest->common.flags & FAST_PATH_NARROW_FORMAT);
-    Bpp = narrow ? 4 : 8;
+       (dest->common.flags & FAST_PATH_NARROW_FORMAT))
+    {
+       narrow = ITER_NARROW;
+       Bpp = 4;
+    }
+    else
+    {
+       narrow = 0;
+       Bpp = 8;
+    }
 
     if (width * Bpp > SCANLINE_BUFFER_LENGTH)
     {
@@ -87,85 +210,19 @@ general_composite_rect  (pixman_implementation_t *imp,
     mask_buffer = src_buffer + width * Bpp;
     dest_buffer = mask_buffer + width * Bpp;
 
-    src_class = _pixman_image_classify (src,
-                                        src_x, src_y,
-                                        width, height);
-
-    mask_class = SOURCE_IMAGE_CLASS_UNKNOWN;
-
-    if (mask)
-    {
-       mask_class = _pixman_image_classify (mask,
-                                            src_x, src_y,
-                                            width, height);
-    }
-
-    if (op == PIXMAN_OP_CLEAR)
-       fetch_src = NULL;
-    else if (narrow)
-       fetch_src = _pixman_image_get_scanline_32;
-    else
-       fetch_src = _pixman_image_get_scanline_64;
+    src_iter_init (imp->toplevel, &src_iter, src,
+                  src_x, src_y, width, height,
+                  src_buffer, narrow);
 
-    if (!mask || op == PIXMAN_OP_CLEAR)
-       fetch_mask = NULL;
-    else if (narrow)
-       fetch_mask = _pixman_image_get_scanline_32;
-    else
-       fetch_mask = _pixman_image_get_scanline_64;
+    src_iter_init (imp->toplevel, &mask_iter, mask,
+                  mask_x, mask_y, width, height,
+                  mask_buffer, narrow);
 
-    if (op == PIXMAN_OP_CLEAR || op == PIXMAN_OP_SRC)
-       fetch_dest = NULL;
-    else if (narrow)
-       fetch_dest = _pixman_image_get_scanline_32;
-    else
-       fetch_dest = _pixman_image_get_scanline_64;
-
-    if (narrow)
-       store = _pixman_image_store_scanline_32;
-    else
-       store = _pixman_image_store_scanline_64;
-
-    /* Skip the store step and composite directly into the
-     * destination if the output format of the compose func matches
-     * the destination format.
-     *
-     * If the destination format is a8r8g8b8 then we can always do
-     * this. If it is x8r8g8b8, then we can only do it if the
-     * operator doesn't make use of destination alpha.
-     */
-    if ((dest->bits.format == PIXMAN_a8r8g8b8) ||
-       (dest->bits.format == PIXMAN_x8r8g8b8   &&
-        (op == PIXMAN_OP_OVER          ||
-         op == PIXMAN_OP_ADD           ||
-         op == PIXMAN_OP_SRC           ||
-         op == PIXMAN_OP_CLEAR         ||
-         op == PIXMAN_OP_IN_REVERSE    ||
-         op == PIXMAN_OP_OUT_REVERSE   ||
-         op == PIXMAN_OP_DST)))
-    {
-       if (narrow &&
-           !dest->common.alpha_map &&
-           !dest->bits.write_func)
-       {
-           store = NULL;
-       }
-    }
-
-    if (!store)
-    {
-       bits = dest->bits.bits;
-       stride = dest->bits.rowstride;
-    }
-    else
-    {
-       bits = NULL;
-       stride = 0;
-    }
+    dest_iter_init (imp->toplevel, &dest_iter, dest,
+                   dest_x, dest_y, width, height,
+                   dest_buffer, narrow);
 
     component_alpha =
-        fetch_src                       &&
-        fetch_mask                      &&
         mask                            &&
         mask->common.type == BITS       &&
         mask->common.component_alpha    &&
@@ -189,70 +246,17 @@ general_composite_rect  (pixman_implementation_t *imp,
     if (!compose)
        return;
 
-    if (!fetch_mask)
-       mask_buffer = NULL;
-
     for (i = 0; i < height; ++i)
     {
-       /* fill first half of scanline with source */
-       if (fetch_src)
-       {
-           if (fetch_mask)
-           {
-               /* fetch mask before source so that fetching of
-                  source can be optimized */
-               fetch_mask (mask, mask_x, mask_y + i,
-                           width, (void *)mask_buffer, 0);
-
-               if (mask_class == SOURCE_IMAGE_CLASS_HORIZONTAL)
-                   fetch_mask = NULL;
-           }
-
-           if (src_class == SOURCE_IMAGE_CLASS_HORIZONTAL)
-           {
-               fetch_src (src, src_x, src_y + i,
-                          width, (void *)src_buffer, 0);
-               fetch_src = NULL;
-           }
-           else
-           {
-               fetch_src (src, src_x, src_y + i,
-                          width, (void *)src_buffer, (void *)mask_buffer);
-           }
-       }
-       else if (fetch_mask)
-       {
-           fetch_mask (mask, mask_x, mask_y + i,
-                       width, (void *)mask_buffer, 0);
-       }
-
-       if (store)
-       {
-           /* fill dest into second half of scanline */
-           if (fetch_dest)
-           {
-               fetch_dest (dest, dest_x, dest_y + i,
-                           width, (void *)dest_buffer, 0);
-           }
-
-           /* blend */
-           compose (imp->toplevel, op,
-                    (void *)dest_buffer,
-                    (void *)src_buffer,
-                    (void *)mask_buffer,
-                    width);
-
-           /* write back */
-           store (&(dest->bits), dest_x, dest_y + i, width,
-                  (void *)dest_buffer);
-       }
-       else
-       {
-           /* blend */
-           compose (imp->toplevel, op,
-                    bits + (dest_y + i) * stride + dest_x,
-                    (void *)src_buffer, (void *)mask_buffer, width);
-       }
+       uint32_t *s, *m, *d;
+
+       m = mask_iter.get_scanline (&mask_iter, NULL);
+       s = src_iter.get_scanline (&src_iter, m);
+       d = dest_iter.get_scanline (&dest_iter, NULL);
+
+       compose (imp->toplevel, op, d, s, m, width);
+
+       dest_iter.write_back (&dest_iter);
     }
 
     if (scanline_buffer != (uint8_t *) stack_scanline_buffer)