https://github.com/mozilla/gecko-dev
Raw File
Tip revision: a3acaf81ef198f6bebe0959e8a3359f64edbb320 authored by seabld on 06 December 2012, 15:04:38 UTC
Added tag SEAMONKEY_2_15b3_RELEASE for changeset FIREFOX_18_0b3_BUILD1. CLOSED TREE a=release
Tip revision: a3acaf8
pixman-16-bit-pipeline.patch
diff --git a/gfx/cairo/libpixman/src/pixman-access.c b/gfx/cairo/libpixman/src/pixman-access.c
--- a/gfx/cairo/libpixman/src/pixman-access.c
+++ b/gfx/cairo/libpixman/src/pixman-access.c
@@ -933,16 +933,54 @@ store_scanline_x2b10g10r10 (bits_image_t
     {
 	WRITE (image, pixel++,
 	       ((values[i] >> 38) & 0x3ff) |
 	       ((values[i] >> 12) & 0xffc00) |
 	       ((values[i] << 14) & 0x3ff00000));
     }
 }
 
+static void
+store_scanline_16 (bits_image_t *  image,
+		   int             x,
+		   int             y,
+		   int             width,
+		   const uint32_t *v)
+{
+    uint16_t *bits = (uint16_t*)(image->bits + image->rowstride * y);
+    uint16_t *values = (uint16_t *)v;
+    uint16_t *pixel = bits + x;
+    int i;
+
+    for (i = 0; i < width; ++i)
+    {
+	WRITE (image, pixel++, values[i]);
+    }
+}
+
+static void
+fetch_scanline_16 (pixman_image_t *image,
+                            int             x,
+                            int             y,
+                            int             width,
+                            uint32_t *      b,
+                            const uint32_t *mask)
+{
+    const uint16_t *bits = (uint16_t*)(image->bits.bits + y * image->bits.rowstride);
+    const uint16_t *pixel = bits + x;
+    int i;
+    uint16_t *buffer = (uint16_t *)b;
+
+    for (i = 0; i < width; ++i)
+    {
+	*buffer++ = READ (image, pixel++);
+    }
+}
+
+
 /*
  * Contracts a 64bpp image to 32bpp and then stores it using a regular 32-bit
  * store proc. Despite the type, this function expects a uint64_t buffer.
  */
 static void
 store_scanline_generic_64 (bits_image_t *  image,
                            int             x,
                            int             y,
@@ -1044,32 +1082,47 @@ fetch_pixel_generic_lossy_32 (bits_image
     pixman_contract (&result, &pixel64, 1);
 
     return result;
 }
 
 typedef struct
 {
     pixman_format_code_t	format;
+    fetch_scanline_t		fetch_scanline_16;
     fetch_scanline_t		fetch_scanline_32;
     fetch_scanline_t		fetch_scanline_64;
     fetch_pixel_32_t		fetch_pixel_32;
     fetch_pixel_64_t		fetch_pixel_64;
+    store_scanline_t		store_scanline_16;
     store_scanline_t		store_scanline_32;
     store_scanline_t		store_scanline_64;
 } format_info_t;
 
 #define FORMAT_INFO(format) 						\
     {									\
 	PIXMAN_ ## format,						\
+	    NULL,							\
 	    fetch_scanline_ ## format,					\
 	    fetch_scanline_generic_64,					\
 	    fetch_pixel_ ## format, fetch_pixel_generic_64,		\
+	    NULL,							\
 	    store_scanline_ ## format, store_scanline_generic_64	\
     }
+#define FORMAT_INFO16(format) 						\
+    {									\
+	PIXMAN_ ## format,						\
+	    fetch_scanline_16,						\
+	    fetch_scanline_ ## format,					\
+	    fetch_scanline_generic_64,					\
+	    fetch_pixel_ ## format, fetch_pixel_generic_64,		\
+	    store_scanline_16,						\
+	    store_scanline_ ## format, store_scanline_generic_64	\
+    }
+
 
 static const format_info_t accessors[] =
 {
 /* 32 bpp formats */
     FORMAT_INFO (a8r8g8b8),
     FORMAT_INFO (x8r8g8b8),
     FORMAT_INFO (a8b8g8r8),
     FORMAT_INFO (x8b8g8r8),
@@ -1079,18 +1132,18 @@ static const format_info_t accessors[] =
     FORMAT_INFO (r8g8b8x8),
     FORMAT_INFO (x14r6g6b6),
 
 /* 24bpp formats */
     FORMAT_INFO (r8g8b8),
     FORMAT_INFO (b8g8r8),
     
 /* 16bpp formats */
-    FORMAT_INFO (r5g6b5),
-    FORMAT_INFO (b5g6r5),
+    FORMAT_INFO16 (r5g6b5),
+    FORMAT_INFO16 (b5g6r5),
     
     FORMAT_INFO (a1r5g5b5),
     FORMAT_INFO (x1r5g5b5),
     FORMAT_INFO (a1b5g5r5),
     FORMAT_INFO (x1b5g5r5),
     FORMAT_INFO (a4r4g4b4),
     FORMAT_INFO (x4r4g4b4),
     FORMAT_INFO (a4b4g4r4),
@@ -1132,62 +1185,64 @@ static const format_info_t accessors[] =
     
 /* 1bpp formats */
     FORMAT_INFO (a1),
     FORMAT_INFO (g1),
     
 /* Wide formats */
     
     { PIXMAN_a2r10g10b10,
-      NULL, fetch_scanline_a2r10g10b10,
+      NULL, NULL, fetch_scanline_a2r10g10b10,
       fetch_pixel_generic_lossy_32, fetch_pixel_a2r10g10b10,
       NULL, store_scanline_a2r10g10b10 },
     
     { PIXMAN_x2r10g10b10,
-      NULL, fetch_scanline_x2r10g10b10,
+      NULL, NULL, fetch_scanline_x2r10g10b10,
       fetch_pixel_generic_lossy_32, fetch_pixel_x2r10g10b10,
       NULL, store_scanline_x2r10g10b10 },
     
     { PIXMAN_a2b10g10r10,
-      NULL, fetch_scanline_a2b10g10r10,
+      NULL, NULL, fetch_scanline_a2b10g10r10,
       fetch_pixel_generic_lossy_32, fetch_pixel_a2b10g10r10,
       NULL, store_scanline_a2b10g10r10 },
     
     { PIXMAN_x2b10g10r10,
-      NULL, fetch_scanline_x2b10g10r10,
+      NULL, NULL, fetch_scanline_x2b10g10r10,
       fetch_pixel_generic_lossy_32, fetch_pixel_x2b10g10r10,
       NULL, store_scanline_x2b10g10r10 },
     
 /* YUV formats */
     { PIXMAN_yuy2,
-      fetch_scanline_yuy2, fetch_scanline_generic_64,
+      NULL, fetch_scanline_yuy2, fetch_scanline_generic_64,
       fetch_pixel_yuy2, fetch_pixel_generic_64,
       NULL, NULL },
     
     { PIXMAN_yv12,
-      fetch_scanline_yv12, fetch_scanline_generic_64,
+      NULL, fetch_scanline_yv12, fetch_scanline_generic_64,
       fetch_pixel_yv12, fetch_pixel_generic_64,
       NULL, NULL },
     
     { PIXMAN_null },
 };
 
 static void
 setup_accessors (bits_image_t *image)
 {
     const format_info_t *info = accessors;
     
     while (info->format != PIXMAN_null)
     {
 	if (info->format == image->format)
 	{
+	    image->fetch_scanline_16 = info->fetch_scanline_16;
 	    image->fetch_scanline_32 = info->fetch_scanline_32;
 	    image->fetch_scanline_64 = info->fetch_scanline_64;
 	    image->fetch_pixel_32 = info->fetch_pixel_32;
 	    image->fetch_pixel_64 = info->fetch_pixel_64;
+	    image->store_scanline_16 = info->store_scanline_16;
 	    image->store_scanline_32 = info->store_scanline_32;
 	    image->store_scanline_64 = info->store_scanline_64;
 	    
 	    return;
 	}
 	
 	info++;
     }
diff --git a/gfx/cairo/libpixman/src/pixman-bits-image.c b/gfx/cairo/libpixman/src/pixman-bits-image.c
--- a/gfx/cairo/libpixman/src/pixman-bits-image.c
+++ b/gfx/cairo/libpixman/src/pixman-bits-image.c
@@ -1247,16 +1247,31 @@ src_get_scanline_wide (pixman_iter_t *it
 
 void
 _pixman_bits_image_src_iter_init (pixman_image_t *image, pixman_iter_t *iter)
 {
     if (iter->flags & ITER_NARROW)
 	iter->get_scanline = src_get_scanline_narrow;
     else
 	iter->get_scanline = src_get_scanline_wide;
+
+}
+
+static uint32_t *
+dest_get_scanline_16 (pixman_iter_t *iter, const uint32_t *mask)
+{
+    pixman_image_t *image  = iter->image;
+    int             x      = iter->x;
+    int             y      = iter->y;
+    int             width  = iter->width;
+    uint32_t *	    buffer = iter->buffer;
+
+    image->bits.fetch_scanline_16 (image, x, y, width, buffer, mask);
+
+    return iter->buffer;
 }
 
 static uint32_t *
 dest_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
 {
     pixman_image_t *image  = iter->image;
     int             x      = iter->x;
     int             y      = iter->y;
@@ -1327,16 +1342,30 @@ dest_get_scanline_wide (pixman_iter_t *i
 	    free (alpha);
 	}
     }
 
     return iter->buffer;
 }
 
 static void
+dest_write_back_16 (pixman_iter_t *iter)
+{
+    bits_image_t *  image  = &iter->image->bits;
+    int             x      = iter->x;
+    int             y      = iter->y;
+    int             width  = iter->width;
+    const uint32_t *buffer = iter->buffer;
+
+    image->store_scanline_16 (image, x, y, width, buffer);
+
+    iter->y++;
+}
+
+static void
 dest_write_back_narrow (pixman_iter_t *iter)
 {
     bits_image_t *  image  = &iter->image->bits;
     int             x      = iter->x;
     int             y      = iter->y;
     int             width  = iter->width;
     const uint32_t *buffer = iter->buffer;
 
@@ -1375,28 +1404,41 @@ dest_write_back_wide (pixman_iter_t *ite
     }
 
     iter->y++;
 }
 
 void
 _pixman_bits_image_dest_iter_init (pixman_image_t *image, pixman_iter_t *iter)
 {
-    if (iter->flags & ITER_NARROW)
+    if (iter->flags & ITER_16)
+    {
+        if ((iter->flags & (ITER_IGNORE_RGB | ITER_IGNORE_ALPHA)) ==
+	    (ITER_IGNORE_RGB | ITER_IGNORE_ALPHA))
+	{
+            iter->get_scanline = _pixman_iter_get_scanline_noop;
+        }
+        else
+        {
+	    iter->get_scanline = dest_get_scanline_16;
+        }
+	iter->write_back = dest_write_back_16;
+    }
+    else if (iter->flags & ITER_NARROW)
     {
 	if ((iter->flags & (ITER_IGNORE_RGB | ITER_IGNORE_ALPHA)) ==
 	    (ITER_IGNORE_RGB | ITER_IGNORE_ALPHA))
 	{
 	    iter->get_scanline = _pixman_iter_get_scanline_noop;
 	}
 	else
 	{
 	    iter->get_scanline = dest_get_scanline_narrow;
 	}
-	
+
 	iter->write_back = dest_write_back_narrow;
     }
     else
     {
 	iter->get_scanline = dest_get_scanline_wide;
 	iter->write_back = dest_write_back_wide;
     }
 }
diff --git a/gfx/cairo/libpixman/src/pixman-combine16.c b/gfx/cairo/libpixman/src/pixman-combine16.c
new file mode 100644
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-combine16.c
@@ -0,0 +1,124 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <string.h>
+
+#include "pixman-private.h"
+
+#include "pixman-combine32.h"
+
+static force_inline uint32_t
+combine_mask (const uint32_t src, const uint32_t mask)
+{
+    uint32_t s, m;
+
+    m = mask >> A_SHIFT;
+
+    if (!m)
+	return 0;
+    s = src;
+
+    UN8x4_MUL_UN8 (s, m);
+
+    return s;
+}
+
+static inline uint32_t convert_0565_to_8888(uint16_t color)
+{
+    return CONVERT_0565_TO_8888(color);
+}
+
+static inline uint16_t convert_8888_to_0565(uint32_t color)
+{
+    return CONVERT_8888_TO_0565(color);
+}
+
+static void
+combine_src_u (pixman_implementation_t *imp,
+               pixman_op_t              op,
+               uint32_t *               dest,
+               const uint32_t *         src,
+               const uint32_t *         mask,
+               int                      width)
+{
+    int i;
+
+    if (!mask)
+	memcpy (dest, src, width * sizeof (uint16_t));
+    else
+    {
+	uint16_t *d = (uint16_t*)dest;
+	uint16_t *src16 = (uint16_t*)src;
+	for (i = 0; i < width; ++i)
+	{
+	    if ((*mask & 0xff000000) == 0xff000000) {
+		// it's likely worth special casing
+		// fully opaque because it avoids
+		// the cost of conversion as well the multiplication
+		*(d + i) = *src16;
+	    } else {
+		// the mask is still 32bits
+		uint32_t s = combine_mask (convert_0565_to_8888(*src16), *mask);
+		*(d + i) = convert_8888_to_0565(s);
+	    }
+	    mask++;
+	    src16++;
+	}
+    }
+
+}
+
+static void
+combine_over_u (pixman_implementation_t *imp,
+               pixman_op_t              op,
+               uint32_t *                dest,
+               const uint32_t *          src,
+               const uint32_t *          mask,
+               int                      width)
+{
+    int i;
+
+    if (!mask)
+	memcpy (dest, src, width * sizeof (uint16_t));
+    else
+    {
+	uint16_t *d = (uint16_t*)dest;
+	uint16_t *src16 = (uint16_t*)src;
+	for (i = 0; i < width; ++i)
+	{
+	    if ((*mask & 0xff000000) == 0xff000000) {
+		// it's likely worth special casing
+		// fully opaque because it avoids
+		// the cost of conversion as well the multiplication
+		*(d + i) = *src16;
+	    } else if ((*mask & 0xff000000) == 0x00000000) {
+		// keep the dest the same
+	    } else {
+		// the mask is still 32bits
+		uint32_t s = combine_mask (convert_0565_to_8888(*src16), *mask);
+		uint32_t ia = ALPHA_8 (~s);
+		uint32_t d32 = convert_0565_to_8888(*(d + i));
+		UN8x4_MUL_UN8_ADD_UN8x4 (d32, ia, s);
+		*(d + i) = convert_8888_to_0565(d32);
+	    }
+	    mask++;
+	    src16++;
+	}
+    }
+
+}
+
+
+void
+_pixman_setup_combiner_functions_16 (pixman_implementation_t *imp)
+{
+    int i;
+    for (i = 0; i < PIXMAN_N_OPERATORS; i++) {
+	imp->combine_16[i] = NULL;
+    }
+    imp->combine_16[PIXMAN_OP_SRC] = combine_src_u;
+    imp->combine_16[PIXMAN_OP_OVER] = combine_over_u;
+}
+
diff --git a/gfx/cairo/libpixman/src/pixman-general.c b/gfx/cairo/libpixman/src/pixman-general.c
--- a/gfx/cairo/libpixman/src/pixman-general.c
+++ b/gfx/cairo/libpixman/src/pixman-general.c
@@ -106,46 +106,61 @@ general_composite_rect  (pixman_implemen
     PIXMAN_COMPOSITE_ARGS (info);
     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;
     pixman_iter_t src_iter, mask_iter, dest_iter;
     pixman_combine_32_func_t compose;
     pixman_bool_t component_alpha;
     iter_flags_t narrow, src_flags;
+    iter_flags_t rgb16;
     int Bpp;
     int i;
 
     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))
     {
 	narrow = ITER_NARROW;
 	Bpp = 4;
     }
     else
     {
 	narrow = 0;
 	Bpp = 8;
     }
 
+    // XXX: This special casing is bad. Ideally, we'd keep the general code general perhaps
+    // by having it deal more specifically with different intermediate formats
+    if (
+	(dest_image->common.flags & FAST_PATH_16_FORMAT && (src_image->type == LINEAR || src_image->type == RADIAL)) &&
+	( op == PIXMAN_OP_SRC ||
+         (op == PIXMAN_OP_OVER && (src_image->common.flags & FAST_PATH_IS_OPAQUE))
+	)
+	) {
+	rgb16 = ITER_16;
+    } else {
+	rgb16 = 0;
+    }
+
+
     if (width * Bpp > SCANLINE_BUFFER_LENGTH)
     {
 	scanline_buffer = pixman_malloc_abc (width, 3, Bpp);
 
 	if (!scanline_buffer)
 	    return;
     }
 
     src_buffer = scanline_buffer;
     mask_buffer = src_buffer + width * Bpp;
     dest_buffer = mask_buffer + width * Bpp;
 
     /* src iter */
-    src_flags = narrow | op_flags[op].src;
+    src_flags = narrow | op_flags[op].src | rgb16;
 
     _pixman_implementation_src_iter_init (imp->toplevel, &src_iter, src_image,
 					  src_x, src_y, width, height,
 					  src_buffer, src_flags);
 
     /* mask iter */
     if ((src_flags & (ITER_IGNORE_ALPHA | ITER_IGNORE_RGB)) ==
 	(ITER_IGNORE_ALPHA | ITER_IGNORE_RGB))
@@ -164,20 +179,20 @@ general_composite_rect  (pixman_implemen
 
     _pixman_implementation_src_iter_init (
 	imp->toplevel, &mask_iter, mask_image, mask_x, mask_y, width, height,
 	mask_buffer, narrow | (component_alpha? 0 : ITER_IGNORE_RGB));
 
     /* dest iter */
     _pixman_implementation_dest_iter_init (
 	imp->toplevel, &dest_iter, dest_image, dest_x, dest_y, width, height,
-	dest_buffer, narrow | op_flags[op].dst);
+	dest_buffer, narrow | op_flags[op].dst | rgb16);
 
     compose = _pixman_implementation_lookup_combiner (
-	imp->toplevel, op, component_alpha, narrow);
+	imp->toplevel, op, component_alpha, narrow, !!rgb16);
 
     if (!compose)
 	return;
 
     for (i = 0; i < height; ++i)
     {
 	uint32_t *s, *m, *d;
 
@@ -234,16 +249,17 @@ general_fill (pixman_implementation_t *i
     return FALSE;
 }
 
 pixman_implementation_t *
 _pixman_implementation_create_general (void)
 {
     pixman_implementation_t *imp = _pixman_implementation_create (NULL, general_fast_path);
 
+    _pixman_setup_combiner_functions_16 (imp);
     _pixman_setup_combiner_functions_32 (imp);
     _pixman_setup_combiner_functions_64 (imp);
 
     imp->blt = general_blt;
     imp->fill = general_fill;
     imp->src_iter_init = general_src_iter_init;
     imp->dest_iter_init = general_dest_iter_init;
 
diff --git a/gfx/cairo/libpixman/src/pixman-image.c b/gfx/cairo/libpixman/src/pixman-image.c
--- a/gfx/cairo/libpixman/src/pixman-image.c
+++ b/gfx/cairo/libpixman/src/pixman-image.c
@@ -451,16 +451,20 @@ compute_image_info (pixman_image_t *imag
 		flags |= FAST_PATH_IS_OPAQUE;
 	}
 
 	if (image->bits.read_func || image->bits.write_func)
 	    flags &= ~FAST_PATH_NO_ACCESSORS;
 
 	if (PIXMAN_FORMAT_IS_WIDE (image->bits.format))
 	    flags &= ~FAST_PATH_NARROW_FORMAT;
+
+	if (image->bits.format == PIXMAN_r5g6b5)
+	    flags |= FAST_PATH_16_FORMAT;
+
 	break;
 
     case RADIAL:
 	code = PIXMAN_unknown;
 
 	/*
 	 * As explained in pixman-radial-gradient.c, every point of
 	 * the plane has a valid associated radius (and thus will be
diff --git a/gfx/cairo/libpixman/src/pixman-implementation.c b/gfx/cairo/libpixman/src/pixman-implementation.c
--- a/gfx/cairo/libpixman/src/pixman-implementation.c
+++ b/gfx/cairo/libpixman/src/pixman-implementation.c
@@ -101,45 +101,51 @@ pixman_implementation_t *
     imp->fill = delegate_fill;
     imp->src_iter_init = delegate_src_iter_init;
     imp->dest_iter_init = delegate_dest_iter_init;
 
     imp->fast_paths = fast_paths;
 
     for (i = 0; i < PIXMAN_N_OPERATORS; ++i)
     {
+	imp->combine_16[i] = NULL;
 	imp->combine_32[i] = NULL;
 	imp->combine_64[i] = NULL;
 	imp->combine_32_ca[i] = NULL;
 	imp->combine_64_ca[i] = NULL;
     }
 
     return imp;
 }
 
 pixman_combine_32_func_t
 _pixman_implementation_lookup_combiner (pixman_implementation_t *imp,
 					pixman_op_t		 op,
 					pixman_bool_t		 component_alpha,
-					pixman_bool_t		 narrow)
+					pixman_bool_t		 narrow,
+					pixman_bool_t		 rgb16)
 {
     pixman_combine_32_func_t f;
 
     do
     {
 	pixman_combine_32_func_t (*combiners[]) =
 	{
 	    (pixman_combine_32_func_t *)imp->combine_64,
 	    (pixman_combine_32_func_t *)imp->combine_64_ca,
 	    imp->combine_32,
 	    imp->combine_32_ca,
+	    (pixman_combine_32_func_t *)imp->combine_16,
+	    NULL,
 	};
-
-	f = combiners[component_alpha | (narrow << 1)][op];
-
+        if (rgb16) {
+            f = combiners[4][op];
+        } else {
+            f = combiners[component_alpha + (narrow << 1)][op];
+        }
 	imp = imp->delegate;
     }
     while (!f);
 
     return f;
 }
 
 pixman_bool_t
diff --git a/gfx/cairo/libpixman/src/pixman-linear-gradient.c b/gfx/cairo/libpixman/src/pixman-linear-gradient.c
--- a/gfx/cairo/libpixman/src/pixman-linear-gradient.c
+++ b/gfx/cairo/libpixman/src/pixman-linear-gradient.c
@@ -217,42 +217,185 @@ linear_get_scanline_narrow (pixman_iter_
 	}
     }
 
     iter->y++;
 
     return iter->buffer;
 }
 
+static uint16_t convert_8888_to_0565(uint32_t color)
+{
+    return CONVERT_8888_TO_0565(color);
+}
+
+static uint32_t *
+linear_get_scanline_16 (pixman_iter_t  *iter,
+			const uint32_t *mask)
+{
+    pixman_image_t *image  = iter->image;
+    int             x      = iter->x;
+    int             y      = iter->y;
+    int             width  = iter->width;
+    uint16_t *      buffer = (uint16_t*)iter->buffer;
+
+    pixman_vector_t v, unit;
+    pixman_fixed_32_32_t l;
+    pixman_fixed_48_16_t dx, dy;
+    gradient_t *gradient = (gradient_t *)image;
+    linear_gradient_t *linear = (linear_gradient_t *)image;
+    uint16_t *end = buffer + width;
+    pixman_gradient_walker_t walker;
+
+    _pixman_gradient_walker_init (&walker, gradient, image->common.repeat);
+
+    /* reference point is the center of the pixel */
+    v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2;
+    v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2;
+    v.vector[2] = pixman_fixed_1;
+
+    if (image->common.transform)
+    {
+	if (!pixman_transform_point_3d (image->common.transform, &v))
+	    return iter->buffer;
+
+	unit.vector[0] = image->common.transform->matrix[0][0];
+	unit.vector[1] = image->common.transform->matrix[1][0];
+	unit.vector[2] = image->common.transform->matrix[2][0];
+    }
+    else
+    {
+	unit.vector[0] = pixman_fixed_1;
+	unit.vector[1] = 0;
+	unit.vector[2] = 0;
+    }
+
+    dx = linear->p2.x - linear->p1.x;
+    dy = linear->p2.y - linear->p1.y;
+
+    l = dx * dx + dy * dy;
+
+    if (l == 0 || unit.vector[2] == 0)
+    {
+	/* affine transformation only */
+        pixman_fixed_32_32_t t, next_inc;
+	double inc;
+
+	if (l == 0 || v.vector[2] == 0)
+	{
+	    t = 0;
+	    inc = 0;
+	}
+	else
+	{
+	    double invden, v2;
+
+	    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]) - 
+		 (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
+	    inc = (dx * unit.vector[0] + dy * unit.vector[1]) * invden;
+	}
+	next_inc = 0;
+
+	if (((pixman_fixed_32_32_t )(inc * width)) == 0)
+	{
+	    register uint16_t color;
+
+	    color = convert_8888_to_0565(_pixman_gradient_walker_pixel (&walker, t));
+	    while (buffer < end)
+		*buffer++ = color;
+	}
+	else
+	{
+	    int i;
+
+	    i = 0;
+	    while (buffer < end)
+	    {
+		if (!mask || *mask++)
+		{
+		    *buffer = convert_8888_to_0565(_pixman_gradient_walker_pixel (&walker,
+										  t + next_inc));
+		}
+		i++;
+		next_inc = inc * i;
+		buffer++;
+	    }
+	}
+    }
+    else
+    {
+	/* projective transformation */
+        double t;
+
+	t = 0;
+
+	while (buffer < end)
+	{
+	    if (!mask || *mask++)
+	    {
+	        if (v.vector[2] != 0)
+		{
+		    double invden, v2;
+
+		    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]) - 
+			 (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
+		}
+
+		*buffer = convert_8888_to_0565(_pixman_gradient_walker_pixel (&walker, t));
+	    }
+
+	    ++buffer;
+
+	    v.vector[0] += unit.vector[0];
+	    v.vector[1] += unit.vector[1];
+	    v.vector[2] += unit.vector[2];
+	}
+    }
+
+    iter->y++;
+
+    return iter->buffer;
+}
+
 static uint32_t *
 linear_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
 {
     uint32_t *buffer = linear_get_scanline_narrow (iter, NULL);
 
     pixman_expand ((uint64_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
 
     return buffer;
 }
 
 void
 _pixman_linear_gradient_iter_init (pixman_image_t *image, pixman_iter_t  *iter)
 {
     if (linear_gradient_is_horizontal (
 	    iter->image, iter->x, iter->y, iter->width, iter->height))
     {
-	if (iter->flags & ITER_NARROW)
+	if (iter->flags & ITER_16)
+	    linear_get_scanline_16 (iter, NULL);
+	else if (iter->flags & ITER_NARROW)
 	    linear_get_scanline_narrow (iter, NULL);
 	else
 	    linear_get_scanline_wide (iter, NULL);
 
 	iter->get_scanline = _pixman_iter_get_scanline_noop;
     }
     else
     {
-	if (iter->flags & ITER_NARROW)
+	if (iter->flags & ITER_16)
+	    iter->get_scanline = linear_get_scanline_16;
+	else if (iter->flags & ITER_NARROW)
 	    iter->get_scanline = linear_get_scanline_narrow;
 	else
 	    iter->get_scanline = linear_get_scanline_wide;
     }
 }
 
 PIXMAN_EXPORT pixman_image_t *
 pixman_image_create_linear_gradient (pixman_point_fixed_t *        p1,
diff --git a/gfx/cairo/libpixman/src/pixman-private.h b/gfx/cairo/libpixman/src/pixman-private.h
--- a/gfx/cairo/libpixman/src/pixman-private.h
+++ b/gfx/cairo/libpixman/src/pixman-private.h
@@ -152,24 +152,28 @@ struct bits_image
     int                        height;
     uint32_t *                 bits;
     uint32_t *                 free_me;
     int                        rowstride;  /* in number of uint32_t's */
 
     fetch_scanline_t           get_scanline_32;
     fetch_scanline_t           get_scanline_64;
 
+    fetch_scanline_t           fetch_scanline_16;
+
     fetch_scanline_t           fetch_scanline_32;
     fetch_pixel_32_t	       fetch_pixel_32;
     store_scanline_t           store_scanline_32;
 
     fetch_scanline_t           fetch_scanline_64;
     fetch_pixel_64_t	       fetch_pixel_64;
     store_scanline_t           store_scanline_64;
 
+    store_scanline_t           store_scanline_16;
+
     /* Used for indirect access to the bits */
     pixman_read_memory_func_t  read_func;
     pixman_write_memory_func_t write_func;
 };
 
 union pixman_image
 {
     image_type_t       type;
@@ -202,17 +206,24 @@ typedef enum
      * destination.
      *
      * When he destination is xRGB, this is useful knowledge, because then
      * we can treat it as if it were ARGB, which means in some cases we can
      * avoid copying it to a temporary buffer.
      */
     ITER_LOCALIZED_ALPHA =	(1 << 1),
     ITER_IGNORE_ALPHA =		(1 << 2),
-    ITER_IGNORE_RGB =		(1 << 3)
+    ITER_IGNORE_RGB =		(1 << 3),
+
+    /* With the addition of ITER_16 we now have two flags that to represent
+     * 3 pipelines. This means that there can be an invalid state when
+     * both ITER_NARROW and ITER_16 are set. In this case
+     * ITER_16 overrides NARROW and we should use the 16 bit pipeline.
+     * Note: ITER_16 still has a 32 bit mask, which is a bit weird. */
+    ITER_16 =			(1 << 4)
 } iter_flags_t;
 
 struct pixman_iter_t
 {
     /* These are initialized by _pixman_implementation_{src,dest}_init */
     pixman_image_t *		image;
     uint32_t *			buffer;
     int				x, y;
@@ -429,16 +440,17 @@ typedef pixman_bool_t (*pixman_fill_func
 					     int                      x,
 					     int                      y,
 					     int                      width,
 					     int                      height,
 					     uint32_t                 xor);
 typedef void (*pixman_iter_init_func_t) (pixman_implementation_t *imp,
                                          pixman_iter_t           *iter);
 
+void _pixman_setup_combiner_functions_16 (pixman_implementation_t *imp);
 void _pixman_setup_combiner_functions_32 (pixman_implementation_t *imp);
 void _pixman_setup_combiner_functions_64 (pixman_implementation_t *imp);
 
 typedef struct
 {
     pixman_op_t             op;
     pixman_format_code_t    src_format;
     uint32_t		    src_flags;
@@ -459,32 +471,34 @@ struct pixman_implementation_t
     pixman_fill_func_t		fill;
     pixman_iter_init_func_t     src_iter_init;
     pixman_iter_init_func_t     dest_iter_init;
 
     pixman_combine_32_func_t	combine_32[PIXMAN_N_OPERATORS];
     pixman_combine_32_func_t	combine_32_ca[PIXMAN_N_OPERATORS];
     pixman_combine_64_func_t	combine_64[PIXMAN_N_OPERATORS];
     pixman_combine_64_func_t	combine_64_ca[PIXMAN_N_OPERATORS];
+    pixman_combine_64_func_t	combine_16[PIXMAN_N_OPERATORS];
 };
 
 uint32_t
 _pixman_image_get_solid (pixman_implementation_t *imp,
 			 pixman_image_t *         image,
                          pixman_format_code_t     format);
 
 pixman_implementation_t *
 _pixman_implementation_create (pixman_implementation_t *delegate,
 			       const pixman_fast_path_t *fast_paths);
 
 pixman_combine_32_func_t
 _pixman_implementation_lookup_combiner (pixman_implementation_t *imp,
 					pixman_op_t		 op,
 					pixman_bool_t		 component_alpha,
-					pixman_bool_t		 wide);
+					pixman_bool_t		 wide,
+					pixman_bool_t		 rgb16);
 
 pixman_bool_t
 _pixman_implementation_blt (pixman_implementation_t *imp,
                             uint32_t *               src_bits,
                             uint32_t *               dst_bits,
                             int                      src_stride,
                             int                      dst_stride,
                             int                      src_bpp,
@@ -613,16 +627,17 @@ uint32_t *
 #define FAST_PATH_Y_UNIT_ZERO			(1 << 18)
 #define FAST_PATH_BILINEAR_FILTER		(1 << 19)
 #define FAST_PATH_ROTATE_90_TRANSFORM		(1 << 20)
 #define FAST_PATH_ROTATE_180_TRANSFORM		(1 << 21)
 #define FAST_PATH_ROTATE_270_TRANSFORM		(1 << 22)
 #define FAST_PATH_SAMPLES_COVER_CLIP_NEAREST	(1 << 23)
 #define FAST_PATH_SAMPLES_COVER_CLIP_BILINEAR	(1 << 24)
 #define FAST_PATH_BITS_IMAGE			(1 << 25)
+#define FAST_PATH_16_FORMAT			(1 << 26)
 
 #define FAST_PATH_PAD_REPEAT						\
     (FAST_PATH_NO_NONE_REPEAT		|				\
      FAST_PATH_NO_NORMAL_REPEAT		|				\
      FAST_PATH_NO_REFLECT_REPEAT)
 
 #define FAST_PATH_NORMAL_REPEAT						\
     (FAST_PATH_NO_NONE_REPEAT		|				\
diff --git a/gfx/cairo/libpixman/src/pixman-radial-gradient.c b/gfx/cairo/libpixman/src/pixman-radial-gradient.c
--- a/gfx/cairo/libpixman/src/pixman-radial-gradient.c
+++ b/gfx/cairo/libpixman/src/pixman-radial-gradient.c
@@ -395,35 +395,289 @@ radial_get_scanline_narrow (pixman_iter_
 	    v.vector[2] += unit.vector[2];
 	}
     }
 
     iter->y++;
     return iter->buffer;
 }
 
+static uint16_t convert_8888_to_0565(uint32_t color)
+{
+    return CONVERT_8888_TO_0565(color);
+}
+
+static uint32_t *
+radial_get_scanline_16 (pixman_iter_t *iter, const uint32_t *mask)
+{
+    /*
+     * Implementation of radial gradients following the PDF specification.
+     * See section 8.7.4.5.4 Type 3 (Radial) Shadings of the PDF Reference
+     * Manual (PDF 32000-1:2008 at the time of this writing).
+     *
+     * In the radial gradient problem we are given two circles (c₁,r₁) and
+     * (c₂,r₂) that define the gradient itself.
+     *
+     * Mathematically the gradient can be defined as the family of circles
+     *
+     *     ((1-t)·c₁ + t·(c₂), (1-t)·r₁ + t·r₂)
+     *
+     * excluding those circles whose radius would be < 0. When a point
+     * belongs to more than one circle, the one with a bigger t is the only
+     * one that contributes to its color. When a point does not belong
+     * to any of the circles, it is transparent black, i.e. RGBA (0, 0, 0, 0).
+     * Further limitations on the range of values for t are imposed when
+     * the gradient is not repeated, namely t must belong to [0,1].
+     *
+     * The graphical result is the same as drawing the valid (radius > 0)
+     * circles with increasing t in [-inf, +inf] (or in [0,1] if the gradient
+     * is not repeated) using SOURCE operator composition.
+     *
+     * It looks like a cone pointing towards the viewer if the ending circle
+     * is smaller than the starting one, a cone pointing inside the page if
+     * the starting circle is the smaller one and like a cylinder if they
+     * have the same radius.
+     *
+     * What we actually do is, given the point whose color we are interested
+     * in, compute the t values for that point, solving for t in:
+     *
+     *     length((1-t)·c₁ + t·(c₂) - p) = (1-t)·r₁ + t·r₂
+     *
+     * Let's rewrite it in a simpler way, by defining some auxiliary
+     * variables:
+     *
+     *     cd = c₂ - c₁
+     *     pd = p - c₁
+     *     dr = r₂ - r₁
+     *     length(t·cd - pd) = r₁ + t·dr
+     *
+     * which actually means
+     *
+     *     hypot(t·cdx - pdx, t·cdy - pdy) = r₁ + t·dr
+     *
+     * or
+     *
+     *     ⎷((t·cdx - pdx)² + (t·cdy - pdy)²) = r₁ + t·dr.
+     *
+     * If we impose (as stated earlier) that r₁ + t·dr >= 0, it becomes:
+     *
+     *     (t·cdx - pdx)² + (t·cdy - pdy)² = (r₁ + t·dr)²
+     *
+     * where we can actually expand the squares and solve for t:
+     *
+     *     t²cdx² - 2t·cdx·pdx + pdx² + t²cdy² - 2t·cdy·pdy + pdy² =
+     *       = r₁² + 2·r₁·t·dr + t²·dr²
+     *
+     *     (cdx² + cdy² - dr²)t² - 2(cdx·pdx + cdy·pdy + r₁·dr)t +
+     *         (pdx² + pdy² - r₁²) = 0
+     *
+     *     A = cdx² + cdy² - dr²
+     *     B = pdx·cdx + pdy·cdy + r₁·dr
+     *     C = pdx² + pdy² - r₁²
+     *     At² - 2Bt + C = 0
+     *
+     * The solutions (unless the equation degenerates because of A = 0) are:
+     *
+     *     t = (B ± ⎷(B² - A·C)) / A
+     *
+     * The solution we are going to prefer is the bigger one, unless the
+     * radius associated to it is negative (or it falls outside the valid t
+     * range).
+     *
+     * Additional observations (useful for optimizations):
+     * A does not depend on p
+     *
+     * A < 0 <=> one of the two circles completely contains the other one
+     *   <=> for every p, the radiuses associated with the two t solutions
+     *       have opposite sign
+     */
+    pixman_image_t *image = iter->image;
+    int x = iter->x;
+    int y = iter->y;
+    int width = iter->width;
+    uint16_t *buffer = iter->buffer;
+
+    gradient_t *gradient = (gradient_t *)image;
+    radial_gradient_t *radial = (radial_gradient_t *)image;
+    uint16_t *end = buffer + width;
+    pixman_gradient_walker_t walker;
+    pixman_vector_t v, unit;
+
+    /* reference point is the center of the pixel */
+    v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2;
+    v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2;
+    v.vector[2] = pixman_fixed_1;
+
+    _pixman_gradient_walker_init (&walker, gradient, image->common.repeat);
+
+    if (image->common.transform)
+    {
+	if (!pixman_transform_point_3d (image->common.transform, &v))
+	    return iter->buffer;
+
+	unit.vector[0] = image->common.transform->matrix[0][0];
+	unit.vector[1] = image->common.transform->matrix[1][0];
+	unit.vector[2] = image->common.transform->matrix[2][0];
+    }
+    else
+    {
+	unit.vector[0] = pixman_fixed_1;
+	unit.vector[1] = 0;
+	unit.vector[2] = 0;
+    }
+
+    if (unit.vector[2] == 0 && v.vector[2] == pixman_fixed_1)
+    {
+	/*
+	 * Given:
+	 *
+	 * t = (B ± ⎷(B² - A·C)) / A
+	 *
+	 * where
+	 *
+	 * A = cdx² + cdy² - dr²
+	 * B = pdx·cdx + pdy·cdy + r₁·dr
+	 * C = pdx² + pdy² - r₁²
+	 * det = B² - A·C
+	 *
+	 * Since we have an affine transformation, we know that (pdx, pdy)
+	 * increase linearly with each pixel,
+	 *
+	 * pdx = pdx₀ + n·ux,
+	 * pdy = pdy₀ + n·uy,
+	 *
+	 * we can then express B, C and det through multiple differentiation.
+	 */
+	pixman_fixed_32_32_t b, db, c, dc, ddc;
+
+	/* warning: this computation may overflow */
+	v.vector[0] -= radial->c1.x;
+	v.vector[1] -= radial->c1.y;
+
+	/*
+	 * B and C are computed and updated exactly.
+	 * If fdot was used instead of dot, in the worst case it would
+	 * lose 11 bits of precision in each of the multiplication and
+	 * summing up would zero out all the bit that were preserved,
+	 * thus making the result 0 instead of the correct one.
+	 * This would mean a worst case of unbound relative error or
+	 * about 2^10 absolute error
+	 */
+	b = dot (v.vector[0], v.vector[1], radial->c1.radius,
+		 radial->delta.x, radial->delta.y, radial->delta.radius);
+	db = dot (unit.vector[0], unit.vector[1], 0,
+		  radial->delta.x, radial->delta.y, 0);
+
+	c = dot (v.vector[0], v.vector[1],
+		 -((pixman_fixed_48_16_t) radial->c1.radius),
+		 v.vector[0], v.vector[1], radial->c1.radius);
+	dc = dot (2 * (pixman_fixed_48_16_t) v.vector[0] + unit.vector[0],
+		  2 * (pixman_fixed_48_16_t) v.vector[1] + unit.vector[1],
+		  0,
+		  unit.vector[0], unit.vector[1], 0);
+	ddc = 2 * dot (unit.vector[0], unit.vector[1], 0,
+		       unit.vector[0], unit.vector[1], 0);
+
+	while (buffer < end)
+	{
+	    if (!mask || *mask++)
+	    {
+		*buffer = convert_8888_to_0565(
+			  radial_compute_color (radial->a, b, c,
+						radial->inva,
+						radial->delta.radius,
+						radial->mindr,
+						&walker,
+						image->common.repeat));
+	    }
+
+	    b += db;
+	    c += dc;
+	    dc += ddc;
+	    ++buffer;
+	}
+    }
+    else
+    {
+	/* projective */
+	/* Warning:
+	 * error propagation guarantees are much looser than in the affine case
+	 */
+	while (buffer < end)
+	{
+	    if (!mask || *mask++)
+	    {
+		if (v.vector[2] != 0)
+		{
+		    double pdx, pdy, invv2, b, c;
+
+		    invv2 = 1. * pixman_fixed_1 / v.vector[2];
+
+		    pdx = v.vector[0] * invv2 - radial->c1.x;
+		    /*    / pixman_fixed_1 */
+
+		    pdy = v.vector[1] * invv2 - radial->c1.y;
+		    /*    / pixman_fixed_1 */
+
+		    b = fdot (pdx, pdy, radial->c1.radius,
+			      radial->delta.x, radial->delta.y,
+			      radial->delta.radius);
+		    /*  / pixman_fixed_1 / pixman_fixed_1 */
+
+		    c = fdot (pdx, pdy, -radial->c1.radius,
+			      pdx, pdy, radial->c1.radius);
+		    /*  / pixman_fixed_1 / pixman_fixed_1 */
+
+		    *buffer = convert_8888_to_0565 (
+			      radial_compute_color (radial->a, b, c,
+						    radial->inva,
+						    radial->delta.radius,
+						    radial->mindr,
+						    &walker,
+						    image->common.repeat));
+		}
+		else
+		{
+		    *buffer = 0;
+		}
+	    }
+
+	    ++buffer;
+
+	    v.vector[0] += unit.vector[0];
+	    v.vector[1] += unit.vector[1];
+	    v.vector[2] += unit.vector[2];
+	}
+    }
+
+    iter->y++;
+    return iter->buffer;
+}
 static uint32_t *
 radial_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
 {
     uint32_t *buffer = radial_get_scanline_narrow (iter, NULL);
 
     pixman_expand ((uint64_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
 
     return buffer;
 }
 
 void
 _pixman_radial_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter)
 {
-    if (iter->flags & ITER_NARROW)
+    if (iter->flags & ITER_16)
+	iter->get_scanline = radial_get_scanline_16;
+    else if (iter->flags & ITER_NARROW)
 	iter->get_scanline = radial_get_scanline_narrow;
     else
 	iter->get_scanline = radial_get_scanline_wide;
 }
 
+
 PIXMAN_EXPORT pixman_image_t *
 pixman_image_create_radial_gradient (pixman_point_fixed_t *        inner,
                                      pixman_point_fixed_t *        outer,
                                      pixman_fixed_t                inner_radius,
                                      pixman_fixed_t                outer_radius,
                                      const pixman_gradient_stop_t *stops,
                                      int                           n_stops)
 {
back to top