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) {