span_renderer_init (cairo_abstract_span_renderer_t	*_r,
		    const cairo_composite_rectangles_t *composite,
		    cairo_antialias_t			 antialias,
		    cairo_bool_t			 needs_clip)
{
    cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *)_r;
    cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface;
    const cairo_pattern_t *source = &composite->source_pattern.base;
    cairo_operator_t op = composite->op;
    cairo_int_status_t status;

    TRACE ((stderr, "%s: antialias=%d, needs_clip=%d\n", __FUNCTION__,
	    antialias, needs_clip));

    if (needs_clip)
	return CAIRO_INT_STATUS_UNSUPPORTED;

    r->composite = composite;
    r->mask = NULL;
    r->src = NULL;
    r->base.finish = NULL;

    status = mono_renderer_init (r, composite, antialias, needs_clip);
    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
	return status;

    status = inplace_renderer_init (r, composite, antialias, needs_clip);
    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
	return status;

    r->bpp = 0;

    if (op == CAIRO_OPERATOR_CLEAR) {
#if PIXMAN_HAS_OP_LERP
	op = PIXMAN_OP_LERP_CLEAR;
#else
	source = &_cairo_pattern_white.base;
	op = PIXMAN_OP_OUT_REVERSE;
#endif
    } else if (dst->base.is_clear &&
	       (op == CAIRO_OPERATOR_SOURCE ||
		op == CAIRO_OPERATOR_OVER ||
		op == CAIRO_OPERATOR_ADD)) {
	op = PIXMAN_OP_SRC;
    } else if (op == CAIRO_OPERATOR_SOURCE) {
	if (_cairo_pattern_is_opaque (&composite->source_pattern.base,
				      &composite->source_sample_area))
	{
	    op = PIXMAN_OP_OVER;
	}
	else
	{
#if PIXMAN_HAS_OP_LERP
	    op = PIXMAN_OP_LERP_SRC;
#else
	    return CAIRO_INT_STATUS_UNSUPPORTED;
#endif
	}
    } else {
	op = _pixman_operator (op);
    }
    r->op = op;

    r->src = _pixman_image_for_pattern (dst, source, FALSE,
					&composite->unbounded,
					&composite->source_sample_area,
					&r->u.mask.src_x, &r->u.mask.src_y);
    if (unlikely (r->src == NULL))
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);

    r->opacity = 1.0;
    if (composite->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) {
	r->opacity = composite->mask_pattern.solid.color.alpha;
    } else {
	pixman_image_t *mask;
	int mask_x, mask_y;

	mask = _pixman_image_for_pattern (dst,
					  &composite->mask_pattern.base,
					  TRUE,
					  &composite->unbounded,
					  &composite->mask_sample_area,
					  &mask_x, &mask_y);
	if (unlikely (mask == NULL))
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);

	/* XXX Component-alpha? */
	if ((dst->base.content & CAIRO_CONTENT_COLOR) == 0 &&
	    _cairo_pattern_is_opaque (source, &composite->source_sample_area))
	{
	    pixman_image_unref (r->src);
	    r->src = mask;
	    r->u.mask.src_x = mask_x;
	    r->u.mask.src_y = mask_y;
	    mask = NULL;
	}

	if (mask) {
	    pixman_image_unref (mask);
	    return CAIRO_INT_STATUS_UNSUPPORTED;
	}
    }

    r->u.mask.extents = composite->unbounded;
    r->u.mask.stride = (r->u.mask.extents.width + 3) & ~3;
    if (r->u.mask.extents.height * r->u.mask.stride > SZ_BUF) {
	r->mask = pixman_image_create_bits (PIXMAN_a8,
					    r->u.mask.extents.width,
					    r->u.mask.extents.height,
					    NULL, 0);

	r->base.render_rows = _cairo_image_spans;
	r->base.finish = NULL;
    } else {
	r->mask = pixman_image_create_bits (PIXMAN_a8,
					    r->u.mask.extents.width,
					    r->u.mask.extents.height,
					    (uint32_t *)r->_buf, r->u.mask.stride);

	r->base.render_rows = _cairo_image_spans_and_zero;
	r->base.finish = _cairo_image_finish_spans_and_zero;
    }
    if (unlikely (r->mask == NULL))
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);

    r->u.mask.data = (uint8_t *) pixman_image_get_data (r->mask);
    r->u.mask.stride = pixman_image_get_stride (r->mask);

    r->u.mask.extents.height += r->u.mask.extents.y;
    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


lerp (void			*_dst,
      cairo_surface_t		*abstract_src,
      cairo_surface_t		*abstract_mask,
      int			src_x,
      int			src_y,
      int			mask_x,
      int			mask_y,
      int			dst_x,
      int			dst_y,
      unsigned int		width,
      unsigned int		height)
{
    cairo_image_surface_t *dst = _dst;
    cairo_image_source_t *src = (cairo_image_source_t *)abstract_src;
    cairo_image_source_t *mask = (cairo_image_source_t *)abstract_mask;

    TRACE ((stderr, "%s\n", __FUNCTION__));

#if PIXMAN_HAS_OP_LERP
    pixman_image_composite32 (PIXMAN_OP_LERP_SRC,
			      src->pixman_image, mask->pixman_image, dst->pixman_image,
			      src_x,  src_y,
			      mask_x, mask_y,
			      dst_x,  dst_y,
			      width,  height);
#else
    /* Punch the clip out of the destination */
    TRACE ((stderr, "%s - OUT_REVERSE (mask=%d/%p, dst=%d/%p)\n",
	    __FUNCTION__,
	    mask->base.unique_id, mask->pixman_image,
	    dst->base.unique_id, dst->pixman_image));
    pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
			      mask->pixman_image, NULL, dst->pixman_image,
			      mask_x, mask_y,
			      0,      0,
			      dst_x,  dst_y,
			      width,  height);

    /* Now add the two results together */
    TRACE ((stderr, "%s - ADD (src=%d/%p, mask=%d/%p, dst=%d/%p)\n",
	    __FUNCTION__,
	    src->base.unique_id, src->pixman_image,
	    mask->base.unique_id, mask->pixman_image,
	    dst->base.unique_id, dst->pixman_image));
    pixman_image_composite32 (PIXMAN_OP_ADD,
			      src->pixman_image, mask->pixman_image, dst->pixman_image,
			      src_x,  src_y,
			      mask_x, mask_y,
			      dst_x,  dst_y,
			      width,  height);
#endif

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


_fill32_spans (void *abstract_renderer, int y, int h,
	       const cairo_half_open_span_t *spans, unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;

    if (num_spans == 0)
	return CAIRO_STATUS_SUCCESS;

    if (likely(h == 1)) {
	do {
	    if (spans[0].coverage) {
		int len = spans[1].x - spans[0].x;
		if (len > 32) {
		    pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), r->bpp,
				 spans[0].x, y, len, 1, r->u.fill.pixel);
		} else {
		    uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4);
		    while (len-- > 0)
			*d++ = r->u.fill.pixel;
		}
	    }
	    spans++;
	} while (--num_spans > 1);
    } else {
	do {
	    if (spans[0].coverage) {
		if (spans[1].x - spans[0].x > 16) {
		    pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), r->bpp,
				 spans[0].x, y, spans[1].x - spans[0].x, h,
				 r->u.fill.pixel);
		} else {
		    int yy = y, hh = h;
		    do {
			int len = spans[1].x - spans[0].x;
			uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4);
			while (len-- > 0)
			    *d++ = r->u.fill.pixel;
			yy++;
		    } while (--hh);
		}
	    }
	    spans++;
	} while (--num_spans > 1);
    }

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


_pixman_image_add_tristrip (pixman_image_t *image,
			    int dst_x, int dst_y,
			    cairo_tristrip_t *strip)
{
    pixman_triangle_t tri;
    pixman_point_fixed_t *p[3] = {&tri.p1, &tri.p2, &tri.p3 };
    int n;

    set_point (p[0], &strip->points[0]);
    set_point (p[1], &strip->points[1]);
    set_point (p[2], &strip->points[2]);
    pixman_add_triangles (image, -dst_x, -dst_y, 1, &tri);
    for (n = 3; n < strip->num_points; n++) {
	set_point (p[n%3], &strip->points[n]);
	pixman_add_triangles (image, -dst_x, -dst_y, 1, &tri);
    }
}

Fix mask usage in image-compositor


_mono_spans (void *abstract_renderer, int y, int h,
	     const cairo_half_open_span_t *spans, unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;

    if (num_spans == 0)
	return CAIRO_STATUS_SUCCESS;

    do {
	if (spans[0].coverage) {
	    pixman_image_composite32 (r->op,
				      r->src, NULL, r->u.composite.dst,
				      spans[0].x + r->u.composite.src_x,  y + r->u.composite.src_y,
				      0, 0,
				      spans[0].x, y,
				      spans[1].x - spans[0].x, h);
	}
	spans++;
    } while (--num_spans > 1);

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


composite (void			*_dst,
	   cairo_operator_t	op,
	   cairo_surface_t	*abstract_src,
	   cairo_surface_t	*abstract_mask,
	   int			src_x,
	   int			src_y,
	   int			mask_x,
	   int			mask_y,
	   int			dst_x,
	   int			dst_y,
	   unsigned int		width,
	   unsigned int		height)
{
    cairo_image_source_t *src = (cairo_image_source_t *)abstract_src;
    cairo_image_source_t *mask = (cairo_image_source_t *)abstract_mask;

    TRACE ((stderr, "%s\n", __FUNCTION__));

    if (mask) {
	pixman_image_composite32 (_pixman_operator (op),
				  src->pixman_image, mask->pixman_image, to_pixman_image (_dst),
				  src_x, src_y,
				  mask_x, mask_y,
				  dst_x, dst_y,
				  width, height);
    } else {
	pixman_image_composite32 (_pixman_operator (op),
				  src->pixman_image, NULL, to_pixman_image (_dst),
				  src_x, src_y,
				  0, 0,
				  dst_x, dst_y,
				  width, height);
    }

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


project_line_x_onto_16_16 (const cairo_line_t *line,
			   cairo_fixed_t top,
			   cairo_fixed_t bottom,
			   pixman_line_fixed_t *out)
{
    /* XXX use fixed-point arithmetic? */
    cairo_point_double_t p1, p2;
    double m;

    p1.x = _cairo_fixed_to_double (line->p1.x);
    p1.y = _cairo_fixed_to_double (line->p1.y);

    p2.x = _cairo_fixed_to_double (line->p2.x);
    p2.y = _cairo_fixed_to_double (line->p2.y);

    m = (p2.x - p1.x) / (p2.y - p1.y);
    out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y));
    out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y));
}

Fix mask usage in image-compositor


_fill_spans (void *abstract_renderer, int y, int h,
	     const cairo_half_open_span_t *spans, unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;

    if (num_spans == 0)
	return CAIRO_STATUS_SUCCESS;

    do {
	if (spans[0].coverage) {
		pixman_fill ((uint32_t *) r->data, r->stride, r->bpp,
			     spans[0].x, y,
			     spans[1].x - spans[0].x, h,
			     r->pixel);
	}
	spans++;
    } while (--num_spans > 1);

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


get_glyph_cache (void)
{
    if (!global_glyph_cache)
	global_glyph_cache = pixman_glyph_cache_create ();

    return global_glyph_cache;
}

Fix mask usage in image-compositor


_mono_finish_unbounded_spans (void *abstract_renderer)
{
    cairo_image_span_renderer_t *r = abstract_renderer;

    if (r->u.composite.mask_y < r->composite->unbounded.y + r->composite->unbounded.height) {
	pixman_image_composite32 (PIXMAN_OP_CLEAR,
				  r->src, NULL, r->u.composite.dst,
				  r->composite->unbounded.x + r->u.composite.src_x,  r->u.composite.mask_y + r->u.composite.src_y,
				  0, 0,
				  r->composite->unbounded.x, r->u.composite.mask_y,
				  r->composite->unbounded.width,
				  r->composite->unbounded.y + r->composite->unbounded.height - r->u.composite.mask_y);
    }

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


span_renderer_fini (cairo_abstract_span_renderer_t *_r,
		    cairo_int_status_t status)
{
    cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *) _r;

    TRACE ((stderr, "%s\n", __FUNCTION__));

    if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
	if (r->base.finish)
	    r->base.finish (r);
    }
    if (likely (status == CAIRO_INT_STATUS_SUCCESS && r->bpp == 0)) {
	const cairo_composite_rectangles_t *composite = r->composite;

	pixman_image_composite32 (r->op, r->src, r->mask,
				  to_pixman_image (composite->surface),
				  composite->unbounded.x + r->u.mask.src_x,
				  composite->unbounded.y + r->u.mask.src_y,
				  0, 0,
				  composite->unbounded.x,
				  composite->unbounded.y,
				  composite->unbounded.width,
				  composite->unbounded.height);
    }

    if (r->src)
	pixman_image_unref (r->src);
    if (r->mask)
	pixman_image_unref (r->mask);
}

Fix mask usage in image-compositor


color_to_uint32 (const cairo_color_t *color)
{
    return
        (color->alpha_short >> 8 << 24) |
        (color->red_short >> 8 << 16)   |
        (color->green_short & 0xff00)   |
        (color->blue_short >> 8);
}

Fix mask usage in image-compositor


fill_boxes (void		*_dst,
	    cairo_operator_t	 op,
	    const cairo_color_t	*color,
	    cairo_boxes_t	*boxes)
{
    cairo_image_surface_t *dst = _dst;
    struct _cairo_boxes_chunk *chunk;
    uint32_t pixel;
    int i;

    TRACE ((stderr, "%s x %d\n", __FUNCTION__, boxes->num_boxes));

    if (fill_reduces_to_source (op, color, dst, &pixel)) {
	for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
	    for (i = 0; i < chunk->count; i++) {
		int x = _cairo_fixed_integer_part (chunk->base[i].p1.x);
		int y = _cairo_fixed_integer_part (chunk->base[i].p1.y);
		int w = _cairo_fixed_integer_part (chunk->base[i].p2.x) - x;
		int h = _cairo_fixed_integer_part (chunk->base[i].p2.y) - y;
		pixman_fill ((uint32_t *) dst->data,
			     dst->stride / sizeof (uint32_t),
			     PIXMAN_FORMAT_BPP (dst->pixman_format),
			     x, y, w, h, pixel);
	    }
	}
    }
    else
    {
	pixman_image_t *src = _pixman_image_for_color (color);
	if (unlikely (src == NULL))
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);

	op = _pixman_operator (op);
	for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
	    for (i = 0; i < chunk->count; i++) {
		int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
		int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
		int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
		int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
		pixman_image_composite32 (op,
					  src, NULL, dst->pixman_image,
					  0, 0,
					  0, 0,
					  x1, y1,
					  x2-x1, y2-y1);
	    }
	}

	pixman_image_unref (src);
    }

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


_cairo_image_unbounded_spans (void *abstract_renderer,
			      int y, int height,
			      const cairo_half_open_span_t *spans,
			      unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;

    assert (y + height <= r->extents.height);
    if (y > r->extents.y) {
	pixman_image_compositor_blt (r->compositor,
				     r->extents.x, r->extents.y,
				     r->extents.width, y - r->extents.y,
				     0);
    }

    if (num_spans == 0) {
	pixman_image_compositor_blt (r->compositor,
				     r->extents.x, y,
				     r->extents.width,  height,
				     0);
    } else {
	if (spans[0].x != r->extents.x) {
	    pixman_image_compositor_blt (r->compositor,
					 r->extents.x, y,
					 spans[0].x - r->extents.x,
					 height,
					 0);
	}

	do {
	    assert (spans[0].x < r->extents.x + r->extents.width);
	    pixman_image_compositor_blt (r->compositor,
					 spans[0].x, y,
					 spans[1].x - spans[0].x, height,
					 r->opacity * spans[0].coverage);
	    spans++;
	} while (--num_spans > 1);

	if (spans[0].x != r->extents.x + r->extents.width) {
	    assert (spans[0].x < r->extents.x + r->extents.width);
	    pixman_image_compositor_blt (r->compositor,
					 spans[0].x,     y,
					 r->extents.x + r->extents.width - spans[0].x, height,
					 0);
	}
    }

    r->extents.y = y + height;
    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


_fill_xrgb32_lerp_spans (void *abstract_renderer, int y, int h,
			 const cairo_half_open_span_t *spans, unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;

    if (num_spans == 0)
	return CAIRO_STATUS_SUCCESS;

    if (likely(h == 1)) {
	do {
	    uint8_t a = mul8_8 (spans[0].coverage, r->bpp);
	    if (a) {
		int len = spans[1].x - spans[0].x;
		uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4);
		while (len-- > 0) {
		    *d = lerp8x4 (r->u.fill.pixel, a, *d);
		    d++;
		}
	    }
	    spans++;
	} while (--num_spans > 1);
    } else {
	do {
	    uint8_t a = mul8_8 (spans[0].coverage, r->bpp);
	    if (a) {
		int yy = y, hh = h;
		do {
		    int len = spans[1].x - spans[0].x;
		    uint32_t *d = (uint32_t *)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4);
		    while (len-- > 0) {
			*d = lerp8x4 (r->u.fill.pixel, a, *d);
			d++;
		    }
		    yy++;
		} while (--hh);
	    }
	    spans++;
	} while (--num_spans > 1);
    }

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


_cairo_image_bounded_spans (void *abstract_renderer,
			    int y, int height,
			    const cairo_half_open_span_t *spans,
			    unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;

    if (num_spans == 0)
	return CAIRO_STATUS_SUCCESS;

    do {
	if (spans[0].coverage) {
	    pixman_image_compositor_blt (r->compositor,
					 spans[0].x, y,
					 spans[1].x - spans[0].x, height,
					 r->opacity * spans[0].coverage);
	}
	spans++;
    } while (--num_spans > 1);

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


composite_glyphs (void				*_dst,
		  cairo_operator_t		 op,
		  cairo_surface_t		*_src,
		  int				 src_x,
		  int				 src_y,
		  int				 dst_x,
		  int				 dst_y,
		  cairo_composite_glyphs_info_t *info)
{
    cairo_scaled_glyph_t *glyph_cache[64];
    pixman_image_t *dst, *src;
    cairo_status_t status;
    int i;

    TRACE ((stderr, "%s\n", __FUNCTION__));

    if (info->num_glyphs == 1)
	return composite_one_glyph(_dst, op, _src, src_x, src_y, dst_x, dst_y, info);

    if (info->use_mask)
	return composite_glyphs_via_mask(_dst, op, _src, src_x, src_y, dst_x, dst_y, info);

    op = _pixman_operator (op);
    dst = to_pixman_image (_dst);
    src = ((cairo_image_source_t *)_src)->pixman_image;

    memset (glyph_cache, 0, sizeof (glyph_cache));
    status = CAIRO_STATUS_SUCCESS;

    for (i = 0; i < info->num_glyphs; i++) {
	int x, y;
	cairo_image_surface_t *glyph_surface;
	cairo_scaled_glyph_t *scaled_glyph;
	unsigned long glyph_index = info->glyphs[i].index;
	int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache);

	scaled_glyph = glyph_cache[cache_index];
	if (scaled_glyph == NULL ||
	    _cairo_scaled_glyph_index (scaled_glyph) != glyph_index)
	{
	    status = _cairo_scaled_glyph_lookup (info->font, glyph_index,
						 CAIRO_SCALED_GLYPH_INFO_SURFACE,
						 &scaled_glyph);

	    if (unlikely (status))
		break;

	    glyph_cache[cache_index] = scaled_glyph;
	}

	glyph_surface = scaled_glyph->surface;
	if (glyph_surface->width && glyph_surface->height) {
	    /* round glyph locations to the nearest pixel */
	    /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
	    x = _cairo_lround (info->glyphs[i].x -
			       glyph_surface->base.device_transform.x0);
	    y = _cairo_lround (info->glyphs[i].y -
			       glyph_surface->base.device_transform.y0);

	    pixman_image_composite32 (op, src, glyph_surface->pixman_image, dst,
                                      x + src_x,  y + src_y,
                                      0, 0,
                                      x - dst_x, y - dst_y,
				      glyph_surface->width,
				      glyph_surface->height);
	}
    }

    return status;
}

Fix mask usage in image-compositor


_cairo_image_finish_spans_and_zero (void *abstract_renderer)
{
    cairo_image_span_renderer_t *r = abstract_renderer;

    if (r->u.mask.extents.y < r->u.mask.extents.height)
	memset (r->u.mask.data, 0, (r->u.mask.extents.height - r->u.mask.extents.y) * r->u.mask.stride);

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


color_to_pixel (const cairo_color_t	*color,
                pixman_format_code_t	 format,
                uint32_t		*pixel)
{
    uint32_t c;

    if (!(format == PIXMAN_a8r8g8b8     ||
          format == PIXMAN_x8r8g8b8     ||
          format == PIXMAN_a8b8g8r8     ||
          format == PIXMAN_x8b8g8r8     ||
          format == PIXMAN_b8g8r8a8     ||
          format == PIXMAN_b8g8r8x8     ||
          format == PIXMAN_r5g6b5       ||
          format == PIXMAN_b5g6r5       ||
          format == PIXMAN_a8))
    {
	return FALSE;
    }

    c = color_to_uint32 (color);

    if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) {
	c = ((c & 0xff000000) >>  0) |
	    ((c & 0x00ff0000) >> 16) |
	    ((c & 0x0000ff00) >>  0) |
	    ((c & 0x000000ff) << 16);
    }

    if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_BGRA) {
	c = ((c & 0xff000000) >> 24) |
	    ((c & 0x00ff0000) >>  8) |
	    ((c & 0x0000ff00) <<  8) |
	    ((c & 0x000000ff) << 24);
    }

    if (format == PIXMAN_a8) {
	c = c >> 24;
    } else if (format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5) {
	c = ((((c) >> 3) & 0x001f) |
	     (((c) >> 5) & 0x07e0) |
	     (((c) >> 8) & 0xf800));
    }

    *pixel = c;
    return TRUE;
}

Fix mask usage in image-compositor


acquire (void *abstract_dst)
{
    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


line_exceeds_16_16 (const cairo_line_t *line)
{
    return
	line->p1.x <= CAIRO_FIXED_16_16_MIN ||
	line->p1.x >= CAIRO_FIXED_16_16_MAX ||

	line->p2.x <= CAIRO_FIXED_16_16_MIN ||
	line->p2.x >= CAIRO_FIXED_16_16_MAX ||

	line->p1.y <= CAIRO_FIXED_16_16_MIN ||
	line->p1.y >= CAIRO_FIXED_16_16_MAX ||

	line->p2.y <= CAIRO_FIXED_16_16_MIN ||
	line->p2.y >= CAIRO_FIXED_16_16_MAX;
}

Fix mask usage in image-compositor


_fill8_spans (void *abstract_renderer, int y, int h,
	       const cairo_half_open_span_t *spans, unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;

    if (num_spans == 0)
	return CAIRO_STATUS_SUCCESS;

    if (likely(h == 1)) {
	do {
	    if (spans[0].coverage) {
		int len = spans[1].x - spans[0].x;
		uint8_t *d = r->u.fill.data + r->u.fill.stride*y + spans[0].x;
		if (len == 1)
		    *d = r->u.fill.pixel;
		else
		    memset(d, r->u.fill.pixel, len);
	    }
	    spans++;
	} while (--num_spans > 1);
    } else {
	do {
	    if (spans[0].coverage) {
		int yy = y, hh = h;
		do {
		    int len = spans[1].x - spans[0].x;
		    uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x;
		    if (len == 1)
			*d = r->u.fill.pixel;
		    else
			memset(d, r->u.fill.pixel, len);
		    yy++;
		} while (--hh);
	    }
	    spans++;
	} while (--num_spans > 1);
    }

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


add8x2_8x2 (uint32_t a, uint32_t b)
{
    uint32_t t = a + b;
    t |= RB_MASK_PLUS_ONE - ((t >> G_SHIFT) & RB_MASK);
    return t & RB_MASK;
}

Fix mask usage in image-compositor


_cairo_image_spans_compositor_get (void)
{
    static cairo_atomic_once_t once = CAIRO_ATOMIC_ONCE_INIT;
    static cairo_spans_compositor_t spans;
    static cairo_compositor_t shape;

    if (_cairo_atomic_init_once_enter(&once)) {
	_cairo_shape_mask_compositor_init (&shape,
					   _cairo_image_traps_compositor_get());
	shape.glyphs = NULL;

	_cairo_spans_compositor_init (&spans, &shape);

	spans.flags = 0;
#if PIXMAN_HAS_OP_LERP
	spans.flags |= CAIRO_SPANS_COMPOSITOR_HAS_LERP;
#endif

	//spans.acquire = acquire;
	//spans.release = release;
	spans.fill_boxes = fill_boxes;
	spans.draw_image_boxes = draw_image_boxes;
	//spans.copy_boxes = copy_boxes;
	spans.pattern_to_surface = _cairo_image_source_create_for_pattern;
	//spans.check_composite_boxes = check_composite_boxes;
	spans.composite_boxes = composite_boxes;
	//spans.check_span_renderer = check_span_renderer;
	spans.renderer_init = span_renderer_init;
	spans.renderer_fini = span_renderer_fini;

	_cairo_atomic_init_once_leave(&once);
    }

    return &spans.base;
}

Fix mask usage in image-compositor


fill_rectangles (void			*_dst,
		 cairo_operator_t	 op,
		 const cairo_color_t	*color,
		 cairo_rectangle_int_t	*rects,
		 int			 num_rects)
{
    cairo_image_surface_t *dst = _dst;
    uint32_t pixel;
    int i;

    TRACE ((stderr, "%s\n", __FUNCTION__));

    if (fill_reduces_to_source (op, color, dst, &pixel)) {
	for (i = 0; i < num_rects; i++) {
	    pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t),
			 PIXMAN_FORMAT_BPP (dst->pixman_format),
			 rects[i].x, rects[i].y,
			 rects[i].width, rects[i].height,
			 pixel);
	}
    } else {
	pixman_image_t *src = _pixman_image_for_color (color);
	if (unlikely (src == NULL))
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);

	op = _pixman_operator (op);
	for (i = 0; i < num_rects; i++) {
	    pixman_image_composite32 (op,
				      src, NULL, dst->pixman_image,
				      0, 0,
				      0, 0,
				      rects[i].x, rects[i].y,
				      rects[i].width, rects[i].height);
	}

	pixman_image_unref (src);
    }

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


_cairo_image_mask_compositor_get (void)
{
    static cairo_atomic_once_t once = CAIRO_ATOMIC_ONCE_INIT;
    static cairo_mask_compositor_t compositor;

    if (_cairo_atomic_init_once_enter(&once)) {
	_cairo_mask_compositor_init (&compositor,
				     _cairo_image_traps_compositor_get ());
	compositor.acquire = acquire;
	compositor.release = release;
	compositor.set_clip_region = set_clip_region;
	compositor.pattern_to_surface = _cairo_image_source_create_for_pattern;
	compositor.draw_image_boxes = draw_image_boxes;
	compositor.fill_rectangles = fill_rectangles;
	compositor.fill_boxes = fill_boxes;
	compositor.check_composite = check_composite;
	compositor.composite = composite;
	//compositor.lerp = lerp;
	//compositor.check_composite_boxes = check_composite_boxes;
	compositor.composite_boxes = composite_boxes;
	compositor.check_composite_glyphs = check_composite_glyphs;
	compositor.composite_glyphs = composite_glyphs;

	_cairo_atomic_init_once_leave(&once);
    }

    return &compositor.base;
}

Fix mask usage in image-compositor


_cairo_image_clipped_spans (void *abstract_renderer,
			    int y, int height,
			    const cairo_half_open_span_t *spans,
			    unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;

    assert (num_spans);

    do {
	if (! spans[0].inverse)
	    pixman_image_compositor_blt (r->compositor,
					 spans[0].x, y,
					 spans[1].x - spans[0].x, height,
					 r->opacity * spans[0].coverage);
	spans++;
    } while (--num_spans > 1);

    r->extents.y = y + height;
    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


mul8_8 (uint8_t a, uint8_t b)
{
    uint16_t t = a * (uint16_t)b + ONE_HALF;
    return ((t >> G_SHIFT) + t) >> G_SHIFT;
}

Fix mask usage in image-compositor


release (void *abstract_dst)
{
    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


_mono_unbounded_spans (void *abstract_renderer, int y, int h,
		       const cairo_half_open_span_t *spans, unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;

    if (num_spans == 0) {
	pixman_image_composite32 (PIXMAN_OP_CLEAR,
				  r->src, NULL, r->u.composite.dst,
				  spans[0].x + r->u.composite.src_x,  y + r->u.composite.src_y,
				  0, 0,
				  r->composite->unbounded.x, y,
				  r->composite->unbounded.width, h);
	r->u.composite.mask_y = y + h;
	return CAIRO_STATUS_SUCCESS;
    }

    if (y != r->u.composite.mask_y) {
	pixman_image_composite32 (PIXMAN_OP_CLEAR,
				  r->src, NULL, r->u.composite.dst,
				  spans[0].x + r->u.composite.src_x,  y + r->u.composite.src_y,
				  0, 0,
				  r->composite->unbounded.x, r->u.composite.mask_y,
				  r->composite->unbounded.width, y - r->u.composite.mask_y);
    }

    if (spans[0].x != r->composite->unbounded.x) {
	    pixman_image_composite32 (PIXMAN_OP_CLEAR,
				      r->src, NULL, r->u.composite.dst,
				      spans[0].x + r->u.composite.src_x,  y + r->u.composite.src_y,
				      0, 0,
				      r->composite->unbounded.x, y,
				      spans[0].x - r->composite->unbounded.x, h);
    }

    do {
	int op = spans[0].coverage ? r->op : PIXMAN_OP_CLEAR;
	pixman_image_composite32 (op,
				  r->src, NULL, r->u.composite.dst,
				  spans[0].x + r->u.composite.src_x,  y + r->u.composite.src_y,
				  0, 0,
				  spans[0].x, y,
				  spans[1].x - spans[0].x, h);
	spans++;
    } while (--num_spans > 1);

    if (spans[0].x != r->composite->unbounded.x + r->composite->unbounded.width) {
	    pixman_image_composite32 (PIXMAN_OP_CLEAR,
				      r->src, NULL, r->u.composite.dst,
				      spans[0].x + r->u.composite.src_x,  y + r->u.composite.src_y,
				      0, 0,
				      spans[0].x, y,
				      r->composite->unbounded.x + r->composite->unbounded.width - spans[0].x, h);
    }

    r->u.composite.mask_y = y + h;
    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


fill_reduces_to_source (cairo_operator_t op,
			const cairo_color_t *color,
			const cairo_image_surface_t *dst,
			uint32_t *pixel)
{
    if (__fill_reduces_to_source (op, color, dst)) {
	return color_to_pixel (color, dst->pixman_format, pixel);
    }

    return FALSE;
}

Fix mask usage in image-compositor


_inplace_spans (void *abstract_renderer,
		int y, int h,
		const cairo_half_open_span_t *spans,
		unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;
    uint8_t *mask;
    int x0, x1;

    if (num_spans == 0)
	return CAIRO_STATUS_SUCCESS;

    if (num_spans == 2 && spans[0].coverage == 0xff) {
	pixman_image_composite32 (r->op, r->src, NULL, r->u.composite.dst,
				  spans[0].x + r->u.composite.src_x,
				  y + r->u.composite.src_y,
				  0, 0,
				  spans[0].x, y,
				  spans[1].x - spans[0].x, h);
	return CAIRO_STATUS_SUCCESS;
    }

    mask = (uint8_t *)pixman_image_get_data (r->mask);
    x1 = x0 = spans[0].x;
    do {
	int len = spans[1].x - spans[0].x;
	*mask++ = spans[0].coverage;
	if (len > 1) {
	    if (len >= r->u.composite.run_length && spans[0].coverage == 0xff) {
		if (x1 != x0) {
		    pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst,
					      x0 + r->u.composite.src_x,
					      y + r->u.composite.src_y,
					      0, 0,
					      x0, y,
					      x1 - x0, h);
		}
		pixman_image_composite32 (r->op, r->src, NULL, r->u.composite.dst,
					  spans[0].x + r->u.composite.src_x,
					  y + r->u.composite.src_y,
					  0, 0,
					  spans[0].x, y,
					  len, h);
		mask = (uint8_t *)pixman_image_get_data (r->mask);
		x0 = spans[1].x;
	    } else if (spans[0].coverage == 0x0 &&
		       x1 - x0 > r->u.composite.run_length) {
		pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst,
					  x0 + r->u.composite.src_x,
					  y + r->u.composite.src_y,
					  0, 0,
					  x0, y,
					  x1 - x0, h);
		mask = (uint8_t *)pixman_image_get_data (r->mask);
		x0 = spans[1].x;
	    }else {
		memset (mask, spans[0].coverage, --len);
		mask += len;
	    }
	}
	x1 = spans[1].x;
	spans++;
    } while (--num_spans > 1);

    if (x1 != x0) {
	pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst,
				  x0 + r->u.composite.src_x,
				  y + r->u.composite.src_y,
				  0, 0,
				  x0, y,
				  x1 - x0, h);
    }

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


_pixman_image_add_traps (pixman_image_t *image,
			 int dst_x, int dst_y,
			 cairo_traps_t *traps)
{
    cairo_trapezoid_t *t = traps->traps;
    int num_traps = traps->num_traps;
    while (num_traps--) {
	pixman_trapezoid_t trap;

	/* top/bottom will be clamped to surface bounds */
	trap.top = _cairo_fixed_to_16_16 (t->top);
	trap.bottom = _cairo_fixed_to_16_16 (t->bottom);

	/* However, all the other coordinates will have been left untouched so
	 * as not to introduce numerical error. Recompute them if they
	 * exceed the 16.16 limits.
	 */
	if (unlikely (line_exceeds_16_16 (&t->left))) {
	    project_line_x_onto_16_16 (&t->left, t->top, t->bottom, &trap.left);
	    trap.left.p1.y = trap.top;
	    trap.left.p2.y = trap.bottom;
	} else {
	    trap.left.p1.x = _cairo_fixed_to_16_16 (t->left.p1.x);
	    trap.left.p1.y = _cairo_fixed_to_16_16 (t->left.p1.y);
	    trap.left.p2.x = _cairo_fixed_to_16_16 (t->left.p2.x);
	    trap.left.p2.y = _cairo_fixed_to_16_16 (t->left.p2.y);
	}

	if (unlikely (line_exceeds_16_16 (&t->right))) {
	    project_line_x_onto_16_16 (&t->right, t->top, t->bottom, &trap.right);
	    trap.right.p1.y = trap.top;
	    trap.right.p2.y = trap.bottom;
	} else {
	    trap.right.p1.x = _cairo_fixed_to_16_16 (t->right.p1.x);
	    trap.right.p1.y = _cairo_fixed_to_16_16 (t->right.p1.y);
	    trap.right.p2.x = _cairo_fixed_to_16_16 (t->right.p2.x);
	    trap.right.p2.y = _cairo_fixed_to_16_16 (t->right.p2.y);
	}

	pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y);
	t++;
    }
}

Fix mask usage in image-compositor


_fill16_spans (void *abstract_renderer, int y, int h,
	       const cairo_half_open_span_t *spans, unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;

    if (num_spans == 0)
	return CAIRO_STATUS_SUCCESS;

    if (likely(h == 1)) {
	do {
	    if (spans[0].coverage) {
		int len = spans[1].x - spans[0].x;
		uint16_t *d = (uint16_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*2);
		while (len-- > 0)
		    *d++ = r->u.fill.pixel;
	    }
	    spans++;
	} while (--num_spans > 1);
    } else {
	do {
	    if (spans[0].coverage) {
		int yy = y, hh = h;
		do {
		    int len = spans[1].x - spans[0].x;
		    uint16_t *d = (uint16_t*)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*2);
		    while (len-- > 0)
			*d++ = r->u.fill.pixel;
		    yy++;
		} while (--hh);
	    }
	    spans++;
	} while (--num_spans > 1);
    }

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


_cairo_image_compositor_reset_static_data (void)
{
}

Fix mask usage in image-compositor


_cairo_image_finish_unbounded_spans (void *abstract_renderer)
{
    cairo_image_span_renderer_t *r = abstract_renderer;

    if (r->extents.y < r->extents.height) {
	pixman_image_compositor_blt (r->compositor,
				     r->extents.x, r->extents.y,
				     r->extents.width,
				     r->extents.height - r->extents.y,
				     0);
    }

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


mul8x2_8 (uint32_t a, uint8_t b)
{
    uint32_t t = (a & RB_MASK) * b + RB_ONE_HALF;
    return ((t + ((t >> G_SHIFT) & RB_MASK)) >> G_SHIFT) & RB_MASK;
}

Fix mask usage in image-compositor


_inplace_opacity_spans (void *abstract_renderer, int y, int h,
			const cairo_half_open_span_t *spans,
			unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;
    uint8_t *mask;
    int x0, x1;

    if (num_spans == 0)
	return CAIRO_STATUS_SUCCESS;

    mask = (uint8_t *)pixman_image_get_data (r->mask);
    x1 = x0 = spans[0].x;
    do {
	int len = spans[1].x - spans[0].x;
	uint8_t m = mul8_8(spans[0].coverage, r->bpp);
	*mask++ = m;
	if (len > 1) {
	    if (m == 0 &&
		x1 - x0 > r->u.composite.run_length) {
		pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst,
					  x0 + r->u.composite.src_x,
					  y + r->u.composite.src_y,
					  0, 0,
					  x0, y,
					  x1 - x0, h);
		mask = (uint8_t *)pixman_image_get_data (r->mask);
		x0 = spans[1].x;
	    }else {
		memset (mask, m, --len);
		mask += len;
	    }
	}
	x1 = spans[1].x;
	spans++;
    } while (--num_spans > 1);

    if (x1 != x0) {
	pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst,
				  x0 + r->u.composite.src_x,
				  y + r->u.composite.src_y,
				  0, 0,
				  x0, y,
				  x1 - x0, h);
    }

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


_cairo_image_spans (void *abstract_renderer,
		    int y, int height,
		    const cairo_half_open_span_t *spans,
		    unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;
    uint8_t *mask, *row;
    int len;

    if (num_spans == 0)
	return CAIRO_STATUS_SUCCESS;

    mask = r->u.mask.data + (y - r->u.mask.extents.y) * r->u.mask.stride;
    mask += spans[0].x - r->u.mask.extents.x;
    row = mask;

    do {
	len = spans[1].x - spans[0].x;
	if (spans[0].coverage) {
	    *row++ = r->opacity * spans[0].coverage;
	    if (--len)
		memset (row, row[-1], len);
	}
	row += len;
	spans++;
    } while (--num_spans > 1);

    len = row - mask;
    row = mask;
    while (--height) {
	mask += r->u.mask.stride;
	memcpy (mask, row, len);
    }

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


inplace_renderer_init (cairo_image_span_renderer_t	*r,
		       const cairo_composite_rectangles_t *composite,
		       cairo_antialias_t		 antialias,
		       cairo_bool_t			 needs_clip)
{
    cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface;
    uint8_t *buf;

    if (composite->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
	return CAIRO_INT_STATUS_UNSUPPORTED;

    r->base.render_rows = NULL;
    r->bpp = composite->mask_pattern.solid.color.alpha_short >> 8;

    if (composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) {
	const cairo_color_t *color;

	color = &composite->source_pattern.solid.color;
	if (composite->op == CAIRO_OPERATOR_CLEAR)
	    color = CAIRO_COLOR_TRANSPARENT;

	if (fill_reduces_to_source (composite->op, color, dst, &r->u.fill.pixel)) {
	    /* Use plain C for the fill operations as the span length is
	     * typically small, too small to payback the startup overheads of
	     * using SSE2 etc.
	     */
	    if (r->bpp == 0xff) {
		switch (dst->format) {
		case CAIRO_FORMAT_A8:
		    r->base.render_rows = _fill_a8_lerp_opaque_spans;
		    break;
		case CAIRO_FORMAT_RGB24:
		case CAIRO_FORMAT_ARGB32:
		    r->base.render_rows = _fill_xrgb32_lerp_opaque_spans;
		    break;
		case CAIRO_FORMAT_A1:
		case CAIRO_FORMAT_RGB16_565:
		case CAIRO_FORMAT_RGB30:
		case CAIRO_FORMAT_RGB96F:
		case CAIRO_FORMAT_RGBA128F:
		case CAIRO_FORMAT_INVALID:
		default: break;
		}
	    } else {
		switch (dst->format) {
		case CAIRO_FORMAT_A8:
		    r->base.render_rows = _fill_a8_lerp_spans;
		    break;
		case CAIRO_FORMAT_RGB24:
		case CAIRO_FORMAT_ARGB32:
		    r->base.render_rows = _fill_xrgb32_lerp_spans;
		    break;
		case CAIRO_FORMAT_A1:
		case CAIRO_FORMAT_RGB16_565:
		case CAIRO_FORMAT_RGB30:
		case CAIRO_FORMAT_RGB96F:
		case CAIRO_FORMAT_RGBA128F:
		case CAIRO_FORMAT_INVALID:
		default: break;
		}
	    }
	    r->u.fill.data = dst->data;
	    r->u.fill.stride = dst->stride;
	}
    } else if ((dst->format == CAIRO_FORMAT_ARGB32 || dst->format == CAIRO_FORMAT_RGB24) &&
	       (composite->op == CAIRO_OPERATOR_SOURCE ||
		(composite->op == CAIRO_OPERATOR_OVER &&
		 (dst->base.is_clear || (dst->base.content & CAIRO_CONTENT_ALPHA) == 0))) &&
	       composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE &&
	       composite->source_pattern.surface.surface->backend->type == CAIRO_SURFACE_TYPE_IMAGE &&
	       to_image_surface(composite->source_pattern.surface.surface)->format == dst->format)
    {
       cairo_image_surface_t *src =
	   to_image_surface(composite->source_pattern.surface.surface);
       int tx, ty;

	if (_cairo_matrix_is_integer_translation(&composite->source_pattern.base.matrix,
						 &tx, &ty) &&
	    composite->bounded.x + tx >= 0 &&
	    composite->bounded.y + ty >= 0 &&
	    composite->bounded.x + composite->bounded.width + tx <= src->width &&
	    composite->bounded.y + composite->bounded.height + ty <= src->height) {

	    assert(PIXMAN_FORMAT_BPP(dst->pixman_format) == 32);
	    r->u.blit.stride = dst->stride;
	    r->u.blit.data = dst->data;
	    r->u.blit.src_stride = src->stride;
	    r->u.blit.src_data = src->data + src->stride * ty + tx * 4;
	    r->base.render_rows = _blit_xrgb32_lerp_spans;
	}
    }
    if (r->base.render_rows == NULL) {
	const cairo_pattern_t *src = &composite->source_pattern.base;
	unsigned int width;

	if (composite->is_bounded == 0)
	    return CAIRO_INT_STATUS_UNSUPPORTED;

	r->base.render_rows = r->bpp == 0xff ? _inplace_spans : _inplace_opacity_spans;
	width = (composite->bounded.width + 3) & ~3;

	r->u.composite.run_length = 8;
	if (src->type == CAIRO_PATTERN_TYPE_LINEAR ||
	    src->type == CAIRO_PATTERN_TYPE_RADIAL)
		r->u.composite.run_length = 256;
	if (dst->base.is_clear &&
	    (composite->op == CAIRO_OPERATOR_SOURCE ||
	     composite->op == CAIRO_OPERATOR_OVER ||
	     composite->op == CAIRO_OPERATOR_ADD)) {
	    r->op = PIXMAN_OP_SRC;
	} else if (composite->op == CAIRO_OPERATOR_SOURCE) {
	    r->base.render_rows = r->bpp == 0xff ? _inplace_src_spans : _inplace_src_opacity_spans;
	    r->u.composite.mask_y = r->composite->unbounded.y;
	    width = (composite->unbounded.width + 3) & ~3;
	} else if (composite->op == CAIRO_OPERATOR_CLEAR) {
	    r->op = PIXMAN_OP_OUT_REVERSE;
	    src = NULL;
	} else {
	    r->op = _pixman_operator (composite->op);
	}

	r->src = _pixman_image_for_pattern (dst, src, FALSE,
					    &composite->bounded,
					    &composite->source_sample_area,
					    &r->u.composite.src_x, &r->u.composite.src_y);
	if (unlikely (r->src == NULL))
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);

	/* Create an effectively unbounded mask by repeating the single line */
	buf = r->_buf;
	if (width > SZ_BUF) {
	    buf = _cairo_malloc (width);
	    if (unlikely (buf == NULL)) {
		pixman_image_unref (r->src);
		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
	    }
	}
	r->mask = pixman_image_create_bits (PIXMAN_a8,
					    width, composite->unbounded.height,
					    (uint32_t *)buf, 0);
	if (unlikely (r->mask == NULL)) {
	    pixman_image_unref (r->src);
	    if (buf != r->_buf)
		free (buf);
	    return _cairo_error(CAIRO_STATUS_NO_MEMORY);
	}

	if (buf != r->_buf)
	    pixman_image_set_destroy_function (r->mask, free_pixels, buf);

	r->u.composite.dst = dst->pixman_image;
    }

    return CAIRO_INT_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


_fill_xrgb32_lerp_opaque_spans (void *abstract_renderer, int y, int h,
				const cairo_half_open_span_t *spans, unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;

    if (num_spans == 0)
	return CAIRO_STATUS_SUCCESS;

    if (likely(h == 1)) {
	do {
	    uint8_t a = spans[0].coverage;
	    if (a) {
		int len = spans[1].x - spans[0].x;
		uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4);
		if (a == 0xff) {
		    if (len > 31) {
			pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), 32,
				     spans[0].x, y, len, 1, r->u.fill.pixel);
		    } else {
			uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4);
			while (len-- > 0)
			    *d++ = r->u.fill.pixel;
		    }
		} else while (len-- > 0) {
		    *d = lerp8x4 (r->u.fill.pixel, a, *d);
		    d++;
		}
	    }
	    spans++;
	} while (--num_spans > 1);
    } else {
	do {
	    uint8_t a = spans[0].coverage;
	    if (a) {
		if (a == 0xff) {
		    if (spans[1].x - spans[0].x > 16) {
			pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), 32,
				     spans[0].x, y, spans[1].x - spans[0].x, h,
				     r->u.fill.pixel);
		    } else {
			int yy = y, hh = h;
			do {
			    int len = spans[1].x - spans[0].x;
			    uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4);
			    while (len-- > 0)
				*d++ = r->u.fill.pixel;
			    yy++;
			} while (--hh);
		    }
		} else {
		    int yy = y, hh = h;
		    do {
			int len = spans[1].x - spans[0].x;
			uint32_t *d = (uint32_t *)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4);
			while (len-- > 0) {
			    *d = lerp8x4 (r->u.fill.pixel, a, *d);
			    d++;
			}
			yy++;
		    } while (--hh);
		}
	    }
	    spans++;
	} while (--num_spans > 1);
    }

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


composite_tristrip (void			*_dst,
		    cairo_operator_t	op,
		    cairo_surface_t	*abstract_src,
		    int			src_x,
		    int			src_y,
		    int			dst_x,
		    int			dst_y,
		    const cairo_rectangle_int_t *extents,
		    cairo_antialias_t	antialias,
		    cairo_tristrip_t	*strip)
{
    cairo_image_surface_t *dst = (cairo_image_surface_t *) _dst;
    cairo_image_source_t *src = (cairo_image_source_t *) abstract_src;
    pixman_image_t *mask;
    pixman_format_code_t format;

    TRACE ((stderr, "%s\n", __FUNCTION__));

    if (strip->num_points < 3)
	return CAIRO_STATUS_SUCCESS;

    if (1) { /* pixman doesn't eliminate self-intersecting triangles/edges */
	    cairo_int_status_t status;
	    cairo_traps_t traps;
	    int n;

	    _cairo_traps_init (&traps);
	    for (n = 0; n < strip->num_points; n++) {
		    cairo_point_t p[4];

		    p[0] = strip->points[0];
		    p[1] = strip->points[1];
		    p[2] = strip->points[2];
		    p[3] = strip->points[0];

		    _cairo_traps_tessellate_convex_quad (&traps, p);
	    }
	    status = composite_traps (_dst, op, abstract_src,
				      src_x, src_y,
				      dst_x, dst_y,
				      extents, antialias, &traps);
	    _cairo_traps_fini (&traps);

	    return status;
    }

    format = antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8;
    if (dst->pixman_format == format &&
	(abstract_src == NULL ||
	 (op == CAIRO_OPERATOR_ADD && src->is_opaque_solid)))
    {
	_pixman_image_add_tristrip (dst->pixman_image, dst_x, dst_y, strip);
	return CAIRO_STATUS_SUCCESS;
    }

    mask = pixman_image_create_bits (format,
				     extents->width, extents->height,
				     NULL, 0);
    if (unlikely (mask == NULL))
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);

    _pixman_image_add_tristrip (mask, extents->x, extents->y, strip);
    pixman_image_composite32 (_pixman_operator (op),
                              src->pixman_image, mask, dst->pixman_image,
                              extents->x + src_x, extents->y + src_y,
                              0, 0,
                              extents->x - dst_x, extents->y - dst_y,
                              extents->width, extents->height);

    pixman_image_unref (mask);

    return  CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


composite_glyphs (void				*_dst,
		  cairo_operator_t		 op,
		  cairo_surface_t		*_src,
		  int				 src_x,
		  int				 src_y,
		  int				 dst_x,
		  int				 dst_y,
		  cairo_composite_glyphs_info_t *info)
{
    cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
    pixman_glyph_cache_t *glyph_cache;
    pixman_glyph_t pglyphs_stack[CAIRO_STACK_ARRAY_LENGTH (pixman_glyph_t)];
    pixman_glyph_t *pglyphs = pglyphs_stack;
    pixman_glyph_t *pg;
    int i;

    TRACE ((stderr, "%s\n", __FUNCTION__));

    CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex);

    glyph_cache = get_glyph_cache();
    if (unlikely (glyph_cache == NULL)) {
	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
	goto out_unlock;
    }

    pixman_glyph_cache_freeze (glyph_cache);

    if (info->num_glyphs > ARRAY_LENGTH (pglyphs_stack)) {
	pglyphs = _cairo_malloc_ab (info->num_glyphs, sizeof (pixman_glyph_t));
	if (unlikely (pglyphs == NULL)) {
	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
	    goto out_thaw;
	}
    }

    pg = pglyphs;
    for (i = 0; i < info->num_glyphs; i++) {
	unsigned long index = info->glyphs[i].index;
	const void *glyph;
        int xphase, yphase;

        xphase = PHASE(info->glyphs[i].x);
        yphase = PHASE(info->glyphs[i].y);

	index = index | (xphase << 24) | (yphase << 26);

	glyph = pixman_glyph_cache_lookup (glyph_cache, info->font, (void *)index);
	if (!glyph) {
	    cairo_scaled_glyph_t *scaled_glyph;
	    cairo_image_surface_t *glyph_surface;

	    /* This call can actually end up recursing, so we have to
	     * drop the mutex around it.
	     */
	    CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex);
	    status = _cairo_scaled_glyph_lookup (info->font, index,
						 CAIRO_SCALED_GLYPH_INFO_SURFACE,
						 &scaled_glyph);
	    CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex);

	    if (unlikely (status))
		goto out_thaw;

	    glyph_surface = scaled_glyph->surface;
	    glyph = pixman_glyph_cache_insert (glyph_cache, info->font, (void *)index,
					       glyph_surface->base.device_transform.x0,
					       glyph_surface->base.device_transform.y0,
					       glyph_surface->pixman_image);
	    if (unlikely (!glyph)) {
		status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
		goto out_thaw;
	    }
	}

	pg->x = POSITION (info->glyphs[i].x);
	pg->y = POSITION (info->glyphs[i].y);
	pg->glyph = glyph;
	pg++;
    }

    if (info->use_mask) {
	pixman_format_code_t mask_format;

	mask_format = pixman_glyph_get_mask_format (glyph_cache, pg - pglyphs, pglyphs);

	pixman_composite_glyphs (_pixman_operator (op),
				 ((cairo_image_source_t *)_src)->pixman_image,
				 to_pixman_image (_dst),
				 mask_format,
				 info->extents.x + src_x, info->extents.y + src_y,
				 info->extents.x, info->extents.y,
				 info->extents.x - dst_x, info->extents.y - dst_y,
				 info->extents.width, info->extents.height,
				 glyph_cache, pg - pglyphs, pglyphs);
    } else {
	pixman_composite_glyphs_no_mask (_pixman_operator (op),
					 ((cairo_image_source_t *)_src)->pixman_image,
					 to_pixman_image (_dst),
					 src_x, src_y,
					 - dst_x, - dst_y,
					 glyph_cache, pg - pglyphs, pglyphs);
    }

out_thaw:
    pixman_glyph_cache_thaw (glyph_cache);

    if (pglyphs != pglyphs_stack)
	free(pglyphs);

out_unlock:
    CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex);
    return status;
}

Fix mask usage in image-compositor


_cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font,
				cairo_scaled_glyph_t *scaled_glyph)
{
}

Fix mask usage in image-compositor


_cairo_image_traps_compositor_get (void)
{
    static cairo_atomic_once_t once = CAIRO_ATOMIC_ONCE_INIT;
    static cairo_traps_compositor_t compositor;

    if (_cairo_atomic_init_once_enter(&once)) {
	_cairo_traps_compositor_init(&compositor,
				     &__cairo_no_compositor);
	compositor.acquire = acquire;
	compositor.release = release;
	compositor.set_clip_region = set_clip_region;
	compositor.pattern_to_surface = _cairo_image_source_create_for_pattern;
	compositor.draw_image_boxes = draw_image_boxes;
	//compositor.copy_boxes = copy_boxes;
	compositor.fill_boxes = fill_boxes;
	compositor.check_composite = check_composite;
	compositor.composite = composite;
	compositor.lerp = lerp;
	//compositor.check_composite_boxes = check_composite_boxes;
	compositor.composite_boxes = composite_boxes;
	//compositor.check_composite_traps = check_composite_traps;
	compositor.composite_traps = composite_traps;
	//compositor.check_composite_tristrip = check_composite_traps;
#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,22,0)
	compositor.composite_tristrip = composite_tristrip;
#endif
	compositor.check_composite_glyphs = check_composite_glyphs;
	compositor.composite_glyphs = composite_glyphs;

	_cairo_atomic_init_once_leave(&once);
    }

    return &compositor.base;
}

Fix mask usage in image-compositor


composite_glyphs_via_mask (void				*_dst,
			   cairo_operator_t		 op,
			   cairo_surface_t		*_src,
			   int				 src_x,
			   int				 src_y,
			   int				 dst_x,
			   int				 dst_y,
			   cairo_composite_glyphs_info_t *info)
{
    cairo_scaled_glyph_t *glyph_cache[64];
    pixman_image_t *white = _pixman_image_for_color (CAIRO_COLOR_WHITE);
    cairo_scaled_glyph_t *scaled_glyph;
    uint8_t buf[2048];
    pixman_image_t *mask;
    pixman_format_code_t format;
    cairo_status_t status;
    int i;

    TRACE ((stderr, "%s\n", __FUNCTION__));

    if (unlikely (white == NULL))
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);

    /* XXX convert the glyphs to common formats a8/a8r8g8b8 to hit
     * optimised paths through pixman. Should we increase the bit
     * depth of the target surface, we should reconsider the appropriate
     * mask formats.
     */

    status = _cairo_scaled_glyph_lookup (info->font,
					 info->glyphs[0].index,
					 CAIRO_SCALED_GLYPH_INFO_SURFACE,
					 &scaled_glyph);
    if (unlikely (status)) {
	pixman_image_unref (white);
	return status;
    }

    memset (glyph_cache, 0, sizeof (glyph_cache));
    glyph_cache[info->glyphs[0].index % ARRAY_LENGTH (glyph_cache)] = scaled_glyph;

    format = PIXMAN_a8;
    i = (info->extents.width + 3) & ~3;
    if (scaled_glyph->surface->base.content & CAIRO_CONTENT_COLOR) {
	format = PIXMAN_a8r8g8b8;
	i = info->extents.width * 4;
    }

    if (i * info->extents.height > (int) sizeof (buf)) {
	mask = pixman_image_create_bits (format,
					info->extents.width,
					info->extents.height,
					NULL, 0);
    } else {
	memset (buf, 0, i * info->extents.height);
	mask = pixman_image_create_bits (format,
					info->extents.width,
					info->extents.height,
					(uint32_t *)buf, i);
    }
    if (unlikely (mask == NULL)) {
	pixman_image_unref (white);
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    }

    status = CAIRO_STATUS_SUCCESS;
    for (i = 0; i < info->num_glyphs; i++) {
	unsigned long glyph_index = info->glyphs[i].index;
	int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache);
	cairo_image_surface_t *glyph_surface;
	int x, y;

	scaled_glyph = glyph_cache[cache_index];
	if (scaled_glyph == NULL ||
	    _cairo_scaled_glyph_index (scaled_glyph) != glyph_index)
	{
	    status = _cairo_scaled_glyph_lookup (info->font, glyph_index,
						 CAIRO_SCALED_GLYPH_INFO_SURFACE,
						 &scaled_glyph);

	    if (unlikely (status)) {
		pixman_image_unref (mask);
		pixman_image_unref (white);
		return status;
	    }

	    glyph_cache[cache_index] = scaled_glyph;
	}

	glyph_surface = scaled_glyph->surface;
	if (glyph_surface->width && glyph_surface->height) {
	    if (glyph_surface->base.content & CAIRO_CONTENT_COLOR &&
		format == PIXMAN_a8) {
		pixman_image_t *ca_mask;

		format = PIXMAN_a8r8g8b8;
		ca_mask = pixman_image_create_bits (format,
						    info->extents.width,
						    info->extents.height,
						    NULL, 0);
		if (unlikely (ca_mask == NULL)) {
		    pixman_image_unref (mask);
		    pixman_image_unref (white);
		    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
		}

		pixman_image_composite32 (PIXMAN_OP_SRC,
					  white, mask, ca_mask,
					  0, 0,
					  0, 0,
					  0, 0,
					  info->extents.width,
					  info->extents.height);
		pixman_image_unref (mask);
		mask = ca_mask;
	    }

	    /* round glyph locations to the nearest pixel */
	    /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
	    x = _cairo_lround (info->glyphs[i].x -
			       glyph_surface->base.device_transform.x0);
	    y = _cairo_lround (info->glyphs[i].y -
			       glyph_surface->base.device_transform.y0);

	    if (glyph_surface->pixman_format == format) {
		pixman_image_composite32 (PIXMAN_OP_ADD,
					  glyph_surface->pixman_image, NULL, mask,
					  0, 0,
					  0, 0,
					  x - info->extents.x, y - info->extents.y,
					  glyph_surface->width,
					  glyph_surface->height);
	    } else {
		pixman_image_composite32 (PIXMAN_OP_ADD,
					  white, glyph_surface->pixman_image, mask,
					  0, 0,
					  0, 0,
					  x - info->extents.x, y - info->extents.y,
					  glyph_surface->width,
					  glyph_surface->height);
	    }
	}
    }

    if (format == PIXMAN_a8r8g8b8)
	pixman_image_set_component_alpha (mask, TRUE);

    pixman_image_composite32 (_pixman_operator (op),
			      ((cairo_image_source_t *)_src)->pixman_image,
			      mask,
			      to_pixman_image (_dst),
			      info->extents.x + src_x, info->extents.y + src_y,
			      0, 0,
			      info->extents.x - dst_x, info->extents.y - dst_y,
			      info->extents.width, info->extents.height);
    pixman_image_unref (mask);
    pixman_image_unref (white);

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


_blit_spans (void *abstract_renderer, int y, int h,
	     const cairo_half_open_span_t *spans, unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;
    int cpp;

    if (num_spans == 0)
	return CAIRO_STATUS_SUCCESS;

    cpp = r->bpp/8;
    if (likely (h == 1)) {
	uint8_t *src = r->u.blit.src_data + y*r->u.blit.src_stride;
	uint8_t *dst = r->u.blit.data + y*r->u.blit.stride;
	do {
	    if (spans[0].coverage) {
		void *s = src + spans[0].x*cpp;
		void *d = dst + spans[0].x*cpp;
		int len = (spans[1].x - spans[0].x) * cpp;
		switch (len) {
		case 1:
		    *(uint8_t *)d = *(uint8_t *)s;
		    break;
		case 2:
		    *(uint16_t *)d = *(uint16_t *)s;
		    break;
		case 4:
		    *(uint32_t *)d = *(uint32_t *)s;
		    break;
#if HAVE_UINT64_T
		case 8:
		    *(uint64_t *)d = *(uint64_t *)s;
		    break;
#endif
		default:
		    memcpy(d, s, len);
		    break;
		}
	    }
	    spans++;
	} while (--num_spans > 1);
    } else {
	do {
	    if (spans[0].coverage) {
		int yy = y, hh = h;
		do {
		    void *src = r->u.blit.src_data + yy*r->u.blit.src_stride + spans[0].x*cpp;
		    void *dst = r->u.blit.data + yy*r->u.blit.stride + spans[0].x*cpp;
		    int len = (spans[1].x - spans[0].x) * cpp;
		    switch (len) {
		    case 1:
			*(uint8_t *)dst = *(uint8_t *)src;
			break;
		    case 2:
			*(uint16_t *)dst = *(uint16_t *)src;
			break;
		    case 4:
			*(uint32_t *)dst = *(uint32_t *)src;
			break;
#if HAVE_UINT64_T
		    case 8:
			*(uint64_t *)dst = *(uint64_t *)src;
			break;
#endif
		    default:
			memcpy(dst, src, len);
			break;
		    }
		    yy++;
		} while (--hh);
	    }
	    spans++;
	} while (--num_spans > 1);
    }

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


_inplace_src_opacity_spans (void *abstract_renderer, int y, int h,
			    const cairo_half_open_span_t *spans,
			    unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;
    uint8_t *mask;
    int x0;

    if (num_spans == 0)
	return CAIRO_STATUS_SUCCESS;

    x0 = spans[0].x;
    mask = (uint8_t *)pixman_image_get_data (r->mask);
    do {
	int len = spans[1].x - spans[0].x;
	uint8_t m = mul8_8(spans[0].coverage, r->bpp);
	if (m == 0) {
	    if (spans[0].x != x0) {
#if PIXMAN_HAS_OP_LERP
		pixman_image_composite32 (PIXMAN_OP_LERP_SRC,
					  r->src, r->mask, r->u.composite.dst,
					  x0 + r->u.composite.src_x,
					  y + r->u.composite.src_y,
					  0, 0,
					  x0, y,
					  spans[0].x - x0, h);
#else
		pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
					  r->mask, NULL, r->u.composite.dst,
					  0, 0,
					  0, 0,
					  x0, y,
					  spans[0].x - x0, h);
		pixman_image_composite32 (PIXMAN_OP_ADD,
					  r->src, r->mask, r->u.composite.dst,
					  x0 + r->u.composite.src_x,
					  y + r->u.composite.src_y,
					  0, 0,
					  x0, y,
					  spans[0].x - x0, h);
#endif
	    }

	    mask = (uint8_t *)pixman_image_get_data (r->mask);
	    x0 = spans[1].x;
	} else {
	    *mask++ = m;
	    if (len > 1) {
		memset (mask, m, --len);
		mask += len;
	    }
	}
	spans++;
    } while (--num_spans > 1);

    if (spans[0].x != x0) {
#if PIXMAN_HAS_OP_LERP
	pixman_image_composite32 (PIXMAN_OP_LERP_SRC,
				  r->src, r->mask, r->u.composite.dst,
				  x0 + r->u.composite.src_x,
				  y + r->u.composite.src_y,
				  0, 0,
				  x0, y,
				  spans[0].x - x0, h);
#else
	pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
				  r->mask, NULL, r->u.composite.dst,
				  0, 0,
				  0, 0,
				  x0, y,
				  spans[0].x - x0, h);
	pixman_image_composite32 (PIXMAN_OP_ADD,
				  r->src, r->mask, r->u.composite.dst,
				  x0 + r->u.composite.src_x,
				  y + r->u.composite.src_y,
				  0, 0,
				  x0, y,
				  spans[0].x - x0, h);
#endif
    }

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


_inplace_src_spans (void *abstract_renderer, int y, int h,
		    const cairo_half_open_span_t *spans,
		    unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;
    uint8_t *m, *base = (uint8_t*)pixman_image_get_data(r->mask);
    int x0;

    if (num_spans == 0)
	return CAIRO_STATUS_SUCCESS;

    x0 = spans[0].x;
    m = base;
    do {
	int len = spans[1].x - spans[0].x;
	if (len >= r->u.composite.run_length && spans[0].coverage == 0xff) {
	    if (spans[0].x != x0) {
#if PIXMAN_HAS_OP_LERP
		pixman_image_composite32 (PIXMAN_OP_LERP_SRC,
					  r->src, r->mask, r->u.composite.dst,
					  x0 + r->u.composite.src_x,
					  y + r->u.composite.src_y,
					  0, 0,
					  x0, y,
					  spans[0].x - x0, h);
#else
		pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
					  r->mask, NULL, r->u.composite.dst,
					  0, 0,
					  0, 0,
					  x0, y,
					  spans[0].x - x0, h);
		pixman_image_composite32 (PIXMAN_OP_ADD,
					  r->src, r->mask, r->u.composite.dst,
					  x0 + r->u.composite.src_x,
					  y + r->u.composite.src_y,
					  0, 0,
					  x0, y,
					  spans[0].x - x0, h);
#endif
	    }

	    pixman_image_composite32 (PIXMAN_OP_SRC,
				      r->src, NULL, r->u.composite.dst,
				      spans[0].x + r->u.composite.src_x,
				      y + r->u.composite.src_y,
				      0, 0,
				      spans[0].x, y,
				      spans[1].x - spans[0].x, h);

	    m = base;
	    x0 = spans[1].x;
	} else if (spans[0].coverage == 0x0) {
	    if (spans[0].x != x0) {
#if PIXMAN_HAS_OP_LERP
		pixman_image_composite32 (PIXMAN_OP_LERP_SRC,
					  r->src, r->mask, r->u.composite.dst,
					  x0 + r->u.composite.src_x,
					  y + r->u.composite.src_y,
					  0, 0,
					  x0, y,
					  spans[0].x - x0, h);
#else
		pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
					  r->mask, NULL, r->u.composite.dst,
					  0, 0,
					  0, 0,
					  x0, y,
					  spans[0].x - x0, h);
		pixman_image_composite32 (PIXMAN_OP_ADD,
					  r->src, r->mask, r->u.composite.dst,
					  x0 + r->u.composite.src_x,
					  y + r->u.composite.src_y,
					  0, 0,
					  x0, y,
					  spans[0].x - x0, h);
#endif
	    }

	    m = base;
	    x0 = spans[1].x;
	} else {
	    *m++ = spans[0].coverage;
	    if (len > 1) {
		memset (m, spans[0].coverage, --len);
		m += len;
	    }
	}
	spans++;
    } while (--num_spans > 1);

    if (spans[0].x != x0) {
#if PIXMAN_HAS_OP_LERP
	pixman_image_composite32 (PIXMAN_OP_LERP_SRC,
				  r->src, r->mask, r->u.composite.dst,
				  x0 + r->u.composite.src_x,
				  y + r->u.composite.src_y,
				  0, 0,
				  x0, y,
				  spans[0].x - x0, h);
#else
	pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
				  r->mask, NULL, r->u.composite.dst,
				  0, 0,
				  0, 0,
				  x0, y,
				  spans[0].x - x0, h);
	pixman_image_composite32 (PIXMAN_OP_ADD,
				  r->src, r->mask, r->u.composite.dst,
				  x0 + r->u.composite.src_x,
				  y + r->u.composite.src_y,
				  0, 0,
				  x0, y,
				  spans[0].x - x0, h);
#endif
    }

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


check_composite_glyphs (const cairo_composite_rectangles_t *extents,
			cairo_scaled_font_t *scaled_font,
			cairo_glyph_t *glyphs,
			int *num_glyphs)
{
    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


_cairo_image_compositor_reset_static_data (void)
{
    CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex);

    if (global_glyph_cache)
	pixman_glyph_cache_destroy (global_glyph_cache);
    global_glyph_cache = NULL;

    CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex);
}

Fix mask usage in image-compositor


mono_renderer_init (cairo_image_span_renderer_t	*r,
		    const cairo_composite_rectangles_t *composite,
		    cairo_antialias_t			 antialias,
		    cairo_bool_t			 needs_clip)
{
    cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface;

    if (antialias != CAIRO_ANTIALIAS_NONE)
	return CAIRO_INT_STATUS_UNSUPPORTED;

    if (!_cairo_pattern_is_opaque_solid (&composite->mask_pattern.base))
	return CAIRO_INT_STATUS_UNSUPPORTED;

    r->base.render_rows = NULL;
    if (composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) {
	const cairo_color_t *color;

	color = &composite->source_pattern.solid.color;
	if (composite->op == CAIRO_OPERATOR_CLEAR)
	    color = CAIRO_COLOR_TRANSPARENT;

	if (fill_reduces_to_source (composite->op, color, dst, &r->u.fill.pixel)) {
	    /* Use plain C for the fill operations as the span length is
	     * typically small, too small to payback the startup overheads of
	     * using SSE2 etc.
	     */
	    switch (PIXMAN_FORMAT_BPP(dst->pixman_format)) {
	    case 8: r->base.render_rows = _fill8_spans; break;
	    case 16: r->base.render_rows = _fill16_spans; break;
	    case 32: r->base.render_rows = _fill32_spans; break;
	    default: break;
	    }
	    r->u.fill.data = dst->data;
	    r->u.fill.stride = dst->stride;
	}
    } else if ((composite->op == CAIRO_OPERATOR_SOURCE ||
		(composite->op == CAIRO_OPERATOR_OVER &&
		 (dst->base.is_clear || (dst->base.content & CAIRO_CONTENT_ALPHA) == 0))) &&
	       composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE &&
	       composite->source_pattern.surface.surface->backend->type == CAIRO_SURFACE_TYPE_IMAGE &&
	       to_image_surface(composite->source_pattern.surface.surface)->format == dst->format)
    {
       cairo_image_surface_t *src =
	   to_image_surface(composite->source_pattern.surface.surface);
       int tx, ty;

	if (_cairo_matrix_is_integer_translation(&composite->source_pattern.base.matrix,
						 &tx, &ty) &&
	    composite->bounded.x + tx >= 0 &&
	    composite->bounded.y + ty >= 0 &&
	    composite->bounded.x + composite->bounded.width +  tx <= src->width &&
	    composite->bounded.y + composite->bounded.height + ty <= src->height) {

	    r->u.blit.stride = dst->stride;
	    r->u.blit.data = dst->data;
	    r->u.blit.src_stride = src->stride;
	    r->u.blit.src_data = src->data + src->stride * ty + tx * 4;
	    r->base.render_rows = _blit_spans;
	}
    }

    if (r->base.render_rows == NULL) {
	r->src = _pixman_image_for_pattern (dst, &composite->source_pattern.base, FALSE,
					    &composite->unbounded,
					    &composite->source_sample_area,
					    &r->u.composite.src_x, &r->u.composite.src_y);
	if (unlikely (r->src == NULL))
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);

	r->u.composite.dst = to_pixman_image (composite->surface);
	r->op = _pixman_operator (composite->op);
	if (composite->is_bounded == 0) {
	    r->base.render_rows = _mono_unbounded_spans;
	    r->base.finish = _mono_finish_unbounded_spans;
	    r->u.composite.mask_y = composite->unbounded.y;
	} else
	    r->base.render_rows = _mono_spans;
    }
    r->bpp = PIXMAN_FORMAT_BPP(dst->pixman_format);

    return CAIRO_INT_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


_fill_a8_lerp_spans (void *abstract_renderer, int y, int h,
		     const cairo_half_open_span_t *spans, unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;

    if (num_spans == 0)
	return CAIRO_STATUS_SUCCESS;

    if (likely(h == 1)) {
	do {
	    uint8_t a = mul8_8 (spans[0].coverage, r->bpp);
	    if (a) {
		int len = spans[1].x - spans[0].x;
		uint8_t *d = r->u.fill.data + r->u.fill.stride*y + spans[0].x;
		uint16_t p = (uint16_t)a * r->u.fill.pixel + 0x7f;
		uint16_t ia = ~a;
		while (len-- > 0) {
		    uint16_t t = *d*ia + p;
		    *d++ = (t + (t>>8)) >> 8;
		}
	    }
	    spans++;
	} while (--num_spans > 1);
    } else {
	do {
	    uint8_t a = mul8_8 (spans[0].coverage, r->bpp);
	    if (a) {
		int yy = y, hh = h;
		uint16_t p = (uint16_t)a * r->u.fill.pixel + 0x7f;
		uint16_t ia = ~a;
		do {
		    int len = spans[1].x - spans[0].x;
		    uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x;
		    while (len-- > 0) {
			uint16_t t = *d*ia + p;
			*d++ = (t + (t>>8)) >> 8;
		    }
		    yy++;
		} while (--hh);
	    }
	    spans++;
	} while (--num_spans > 1);
    }

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


composite_one_glyph (void				*_dst,
		     cairo_operator_t			 op,
		     cairo_surface_t			*_src,
		     int				 src_x,
		     int				 src_y,
		     int				 dst_x,
		     int				 dst_y,
		     cairo_composite_glyphs_info_t	 *info)
{
    cairo_image_surface_t *glyph_surface;
    cairo_scaled_glyph_t *scaled_glyph;
    cairo_status_t status;
    int x, y;

    TRACE ((stderr, "%s\n", __FUNCTION__));

    status = _cairo_scaled_glyph_lookup (info->font,
					 info->glyphs[0].index,
					 CAIRO_SCALED_GLYPH_INFO_SURFACE,
					 &scaled_glyph);

    if (unlikely (status))
	return status;

    glyph_surface = scaled_glyph->surface;
    if (glyph_surface->width == 0 || glyph_surface->height == 0)
	return CAIRO_INT_STATUS_NOTHING_TO_DO;

    /* round glyph locations to the nearest pixel */
    /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
    x = _cairo_lround (info->glyphs[0].x -
		       glyph_surface->base.device_transform.x0);
    y = _cairo_lround (info->glyphs[0].y -
		       glyph_surface->base.device_transform.y0);

    pixman_image_composite32 (_pixman_operator (op),
			      ((cairo_image_source_t *)_src)->pixman_image,
			      glyph_surface->pixman_image,
			      to_pixman_image (_dst),
			      x + src_x,  y + src_y,
			      0, 0,
			      x - dst_x, y - dst_y,
			      glyph_surface->width,
			      glyph_surface->height);

    return CAIRO_INT_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


_cairo_image_spans_and_zero (void *abstract_renderer,
			     int y, int height,
			     const cairo_half_open_span_t *spans,
			     unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;
    uint8_t *mask;
    int len;

    mask = r->u.mask.data;
    if (y > r->u.mask.extents.y) {
	len = (y - r->u.mask.extents.y) * r->u.mask.stride;
	memset (mask, 0, len);
	mask += len;
    }

    r->u.mask.extents.y = y + height;
    r->u.mask.data = mask + height * r->u.mask.stride;
    if (num_spans == 0) {
	memset (mask, 0, height * r->u.mask.stride);
    } else {
	uint8_t *row = mask;

	if (spans[0].x != r->u.mask.extents.x) {
	    len = spans[0].x - r->u.mask.extents.x;
	    memset (row, 0, len);
	    row += len;
	}

	do {
	    len = spans[1].x - spans[0].x;
	    *row++ = r->opacity * spans[0].coverage;
	    if (len > 1) {
		memset (row, row[-1], --len);
		row += len;
	    }
	    spans++;
	} while (--num_spans > 1);

	if (spans[0].x != r->u.mask.extents.x + r->u.mask.extents.width) {
	    len = r->u.mask.extents.x + r->u.mask.extents.width - spans[0].x;
	    memset (row, 0, len);
	}

	row = mask;
	while (--height) {
	    mask += r->u.mask.stride;
	    memcpy (mask, row, r->u.mask.extents.width);
	}
    }

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


static void free_pixels (pixman_image_t *image, void *data)
{
	free (data);
}

Fix mask usage in image-compositor


set_clip_region (void *_surface,
		 cairo_region_t *region)
{
    cairo_image_surface_t *surface = _surface;
    pixman_region32_t *rgn = region ? &region->rgn : NULL;

    if (! pixman_image_set_clip_region32 (surface->pixman_image, rgn))
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


check_composite (const cairo_composite_rectangles_t *extents)
{
    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


composite_boxes (void			*_dst,
		 cairo_operator_t	op,
		 cairo_surface_t	*abstract_src,
		 cairo_surface_t	*abstract_mask,
		 int			src_x,
		 int			src_y,
		 int			mask_x,
		 int			mask_y,
		 int			dst_x,
		 int			dst_y,
		 cairo_boxes_t		*boxes,
		 const cairo_rectangle_int_t  *extents)
{
    pixman_image_t *dst = to_pixman_image (_dst);
    pixman_image_t *src = ((cairo_image_source_t *)abstract_src)->pixman_image;
    pixman_image_t *mask = abstract_mask ? ((cairo_image_source_t *)abstract_mask)->pixman_image : NULL;
    pixman_image_t *free_src = NULL;
    struct _cairo_boxes_chunk *chunk;
    int i;

    /* XXX consider using a region? saves multiple prepare-composite */
    TRACE ((stderr, "%s x %d\n", __FUNCTION__, boxes->num_boxes));

    if (((cairo_surface_t *)_dst)->is_clear &&
	(op == CAIRO_OPERATOR_SOURCE ||
	 op == CAIRO_OPERATOR_OVER ||
	 op == CAIRO_OPERATOR_ADD)) {
	op = PIXMAN_OP_SRC;
    } else if (mask) {
	if (op == CAIRO_OPERATOR_CLEAR) {
#if PIXMAN_HAS_OP_LERP
	    op = PIXMAN_OP_LERP_CLEAR;
#else
	    free_src = src = _pixman_image_for_color (CAIRO_COLOR_WHITE);
	    if (unlikely (src == NULL))
		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
	    op = PIXMAN_OP_OUT_REVERSE;
#endif
	} else if (op == CAIRO_OPERATOR_SOURCE) {
#if PIXMAN_HAS_OP_LERP
	    op = PIXMAN_OP_LERP_SRC;
#else
	    return CAIRO_INT_STATUS_UNSUPPORTED;
#endif
	} else {
	    op = _pixman_operator (op);
	}
    } else {
	op = _pixman_operator (op);
    }

    for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
	for (i = 0; i < chunk->count; i++) {
	    int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
	    int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
	    int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
	    int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);

	    pixman_image_composite32 (op, src, mask, dst,
				      x1 + src_x, y1 + src_y,
				      x1 + mask_x, y1 + mask_y,
				      x1 + dst_x, y1 + dst_y,
				      x2 - x1, y2 - y1);
	}
    }

    if (free_src)
	pixman_image_unref (free_src);

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


to_pixman_image (cairo_surface_t *s)
{
    return ((cairo_image_surface_t *)s)->pixman_image;
}

Fix mask usage in image-compositor


draw (cairo_t *cr, int width, int height)
{
    cairo_set_source_rgb (cr, 0., 0., 0.);
    cairo_paint (cr);

    cairo_set_source_rgb (cr, 1., 1., 1.);
    cairo_set_line_width (cr, 1.);

    cairo_pattern_t *p = cairo_pattern_create_linear (0, 0, width, height);
    cairo_pattern_add_color_stop_rgb (p, 0, 0.99, 1, 1);
    cairo_pattern_add_color_stop_rgb (p, 1, 1, 1, 1);
    cairo_set_source (cr, p);

    cairo_move_to (cr, 0.5, -1);
    for (int i = 0; i < width; i+=3) {
	cairo_rel_line_to (cr, 2, 2);
	cairo_rel_line_to (cr, 1, -2);
    }

    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
    cairo_stroke (cr);

    cairo_pattern_destroy(p);

    return CAIRO_TEST_SUCCESS;
}

Fix mask usage in image-compositor


composite_traps (void			*_dst,
		 cairo_operator_t	op,
		 cairo_surface_t	*abstract_src,
		 int			src_x,
		 int			src_y,
		 int			dst_x,
		 int			dst_y,
		 const cairo_rectangle_int_t *extents,
		 cairo_antialias_t	antialias,
		 cairo_traps_t		*traps)
{
    cairo_image_surface_t *dst = (cairo_image_surface_t *) _dst;
    cairo_image_source_t *src = (cairo_image_source_t *) abstract_src;
    cairo_int_status_t status;
    pixman_image_t *mask;
    pixman_format_code_t format;

    TRACE ((stderr, "%s\n", __FUNCTION__));

    /* pixman doesn't eliminate self-intersecting trapezoids/edges */
    status = _cairo_bentley_ottmann_tessellate_traps (traps,
						      CAIRO_FILL_RULE_WINDING);
    if (status != CAIRO_INT_STATUS_SUCCESS)
	    return status;

    /* Special case adding trapezoids onto a mask surface; we want to avoid
     * creating an intermediate temporary mask unnecessarily.
     *
     * We make the assumption here that the portion of the trapezoids
     * contained within the surface is bounded by [dst_x,dst_y,width,height];
     * the Cairo core code passes bounds based on the trapezoid extents.
     */
    format = antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8;
    if (dst->pixman_format == format &&
	(abstract_src == NULL ||
	 (op == CAIRO_OPERATOR_ADD && src->is_opaque_solid)))
    {
	_pixman_image_add_traps (dst->pixman_image, dst_x, dst_y, traps);
	return CAIRO_STATUS_SUCCESS;
    }

    mask = pixman_image_create_bits (format,
				     extents->width, extents->height,
				     NULL, 0);
    if (unlikely (mask == NULL))
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);

    _pixman_image_add_traps (mask, extents->x, extents->y, traps);
    pixman_image_composite32 (_pixman_operator (op),
                              src->pixman_image, mask, dst->pixman_image,
                              extents->x + src_x, extents->y + src_y,
                              0, 0,
                              extents->x - dst_x, extents->y - dst_y,
                              extents->width, extents->height);

    pixman_image_unref (mask);

    return  CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


_cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font,
				cairo_scaled_glyph_t *scaled_glyph)
{
    CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex);

    if (global_glyph_cache) {
	pixman_glyph_cache_remove (
	    global_glyph_cache, scaled_font,
	    (void *)scaled_glyph->hash_entry.hash);
    }

    CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex);
}

Fix mask usage in image-compositor


_fill_a8_lerp_opaque_spans (void *abstract_renderer, int y, int h,
			    const cairo_half_open_span_t *spans, unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;

    if (num_spans == 0)
	return CAIRO_STATUS_SUCCESS;

    if (likely(h == 1)) {
	uint8_t *d = r->u.fill.data + r->u.fill.stride*y;
	do {
	    uint8_t a = spans[0].coverage;
	    if (a) {
		int len = spans[1].x - spans[0].x;
		if (a == 0xff) {
		    memset(d + spans[0].x, r->u.fill.pixel, len);
		} else {
		    uint8_t s = mul8_8(a, r->u.fill.pixel);
		    uint8_t *dst = d + spans[0].x;
		    a = ~a;
		    while (len-- > 0) {
			uint8_t t = mul8_8(*dst, a);
			*dst++ = t + s;
		    }
		}
	    }
	    spans++;
	} while (--num_spans > 1);
    } else {
	do {
	    uint8_t a = spans[0].coverage;
	    if (a) {
		int yy = y, hh = h;
		if (a == 0xff) {
		    do {
			int len = spans[1].x - spans[0].x;
			uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x;
			memset(d, r->u.fill.pixel, len);
			yy++;
		    } while (--hh);
		} else {
		    uint8_t s = mul8_8(a, r->u.fill.pixel);
		    a = ~a;
		    do {
			int len = spans[1].x - spans[0].x;
			uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x;
			while (len-- > 0) {
			    uint8_t t = mul8_8(*d, a);
			    *d++ = t + s;
			}
			yy++;
		    } while (--hh);
		}
	    }
	    spans++;
	} while (--num_spans > 1);
    }

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


_pixman_operator (cairo_operator_t op)
{
    switch ((int) op) {
    case CAIRO_OPERATOR_CLEAR:
	return PIXMAN_OP_CLEAR;

    case CAIRO_OPERATOR_SOURCE:
	return PIXMAN_OP_SRC;
    case CAIRO_OPERATOR_OVER:
	return PIXMAN_OP_OVER;
    case CAIRO_OPERATOR_IN:
	return PIXMAN_OP_IN;
    case CAIRO_OPERATOR_OUT:
	return PIXMAN_OP_OUT;
    case CAIRO_OPERATOR_ATOP:
	return PIXMAN_OP_ATOP;

    case CAIRO_OPERATOR_DEST:
	return PIXMAN_OP_DST;
    case CAIRO_OPERATOR_DEST_OVER:
	return PIXMAN_OP_OVER_REVERSE;
    case CAIRO_OPERATOR_DEST_IN:
	return PIXMAN_OP_IN_REVERSE;
    case CAIRO_OPERATOR_DEST_OUT:
	return PIXMAN_OP_OUT_REVERSE;
    case CAIRO_OPERATOR_DEST_ATOP:
	return PIXMAN_OP_ATOP_REVERSE;

    case CAIRO_OPERATOR_XOR:
	return PIXMAN_OP_XOR;
    case CAIRO_OPERATOR_ADD:
	return PIXMAN_OP_ADD;
    case CAIRO_OPERATOR_SATURATE:
	return PIXMAN_OP_SATURATE;

    case CAIRO_OPERATOR_MULTIPLY:
	return PIXMAN_OP_MULTIPLY;
    case CAIRO_OPERATOR_SCREEN:
	return PIXMAN_OP_SCREEN;
    case CAIRO_OPERATOR_OVERLAY:
	return PIXMAN_OP_OVERLAY;
    case CAIRO_OPERATOR_DARKEN:
	return PIXMAN_OP_DARKEN;
    case CAIRO_OPERATOR_LIGHTEN:
	return PIXMAN_OP_LIGHTEN;
    case CAIRO_OPERATOR_COLOR_DODGE:
	return PIXMAN_OP_COLOR_DODGE;
    case CAIRO_OPERATOR_COLOR_BURN:
	return PIXMAN_OP_COLOR_BURN;
    case CAIRO_OPERATOR_HARD_LIGHT:
	return PIXMAN_OP_HARD_LIGHT;
    case CAIRO_OPERATOR_SOFT_LIGHT:
	return PIXMAN_OP_SOFT_LIGHT;
    case CAIRO_OPERATOR_DIFFERENCE:
	return PIXMAN_OP_DIFFERENCE;
    case CAIRO_OPERATOR_EXCLUSION:
	return PIXMAN_OP_EXCLUSION;
    case CAIRO_OPERATOR_HSL_HUE:
	return PIXMAN_OP_HSL_HUE;
    case CAIRO_OPERATOR_HSL_SATURATION:
	return PIXMAN_OP_HSL_SATURATION;
    case CAIRO_OPERATOR_HSL_COLOR:
	return PIXMAN_OP_HSL_COLOR;
    case CAIRO_OPERATOR_HSL_LUMINOSITY:
	return PIXMAN_OP_HSL_LUMINOSITY;

    default:
	ASSERT_NOT_REACHED;
	return PIXMAN_OP_OVER;
    }
}

Fix mask usage in image-compositor


set_point (pixman_point_fixed_t *p, cairo_point_t *c)
{
    p->x = _cairo_fixed_to_16_16 (c->x);
    p->y = _cairo_fixed_to_16_16 (c->y);
}

Fix mask usage in image-compositor


span_renderer_fini (cairo_abstract_span_renderer_t *_r,
		    cairo_int_status_t status)
{
    cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *) _r;

    TRACE ((stderr, "%s\n", __FUNCTION__));

    if (status == CAIRO_INT_STATUS_SUCCESS && r->base.finish)
	r->base.finish (r);

    if (r->compositor)
	pixman_image_compositor_destroy (r->compositor);

    if (r->src)
	pixman_image_unref (r->src);
    if (r->mask)
	pixman_image_unref (r->mask);
}

Fix mask usage in image-compositor


lerp8x4 (uint32_t src, uint8_t a, uint32_t dst)
{
    return (add8x2_8x2 (mul8x2_8 (src, a),
			mul8x2_8 (dst, ~a)) |
	    add8x2_8x2 (mul8x2_8 (src >> G_SHIFT, a),
			mul8x2_8 (dst >> G_SHIFT, ~a)) << G_SHIFT);
}

Fix mask usage in image-compositor


_cairo_image_bounded_opaque_spans (void *abstract_renderer,
				   int y, int height,
				   const cairo_half_open_span_t *spans,
				   unsigned num_spans)
{
    cairo_image_span_renderer_t *r = abstract_renderer;

    if (num_spans == 0)
	return CAIRO_STATUS_SUCCESS;

    do {
	if (spans[0].coverage)
	    pixman_image_compositor_blt (r->compositor,
					 spans[0].x, y,
					 spans[1].x - spans[0].x, height,
					 spans[0].coverage);
	spans++;
    } while (--num_spans > 1);

    return CAIRO_STATUS_SUCCESS;
}

Fix mask usage in image-compositor


__fill_reduces_to_source (cairo_operator_t op,
			  const cairo_color_t *color,
			  const cairo_image_surface_t *dst)
{
    if (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_CLEAR)
	return TRUE;
    if (op == CAIRO_OPERATOR_OVER && CAIRO_COLOR_IS_OPAQUE (color))
	return TRUE;
    if (dst->base.is_clear)
	return op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD;

    return FALSE;
}

Fix mask usage in image-compositor


QPaintEngineExPrivate::QPaintEngineExPrivate()
    : dasher(&stroker),
      strokeHandler(nullptr),
      activeStroker(nullptr),
      strokerPen(Qt::NoPen)
{
}

Improve fix for avoiding huge number of tiny dashes

Some pathological cases were not caught by the previous fix.

Fixes: QTBUG-95239
Pick-to: 6.2 6.1 5.15
Change-Id: I0337ee3923ff93ccb36c4d7b810a9c0667354cc5
Reviewed-by: Robert Löhning <robert.loehning@qt.io>


void QPaintEngineEx::drawLines(const QLine *lines, int lineCount)
{
    int elementCount = lineCount << 1;
    while (elementCount > 0) {
        int count = qMin(elementCount, 32);

        qreal pts[64];
        int count2 = count<<1;
        for (int i=0; i<count2; ++i)
            pts[i] = ((const int *) lines)[i];

        QVectorPath path(pts, count, qpaintengineex_line_types_16, QVectorPath::LinesHint);
        stroke(path, state()->pen);

        elementCount -= 32;
        lines += 16;
    }
}


QPainterState *QPaintEngineEx::createState(QPainterState *orig) const
{
    if (!orig)
        return new QPainterState;
    return new QPainterState(orig);
}


void QPaintEngineEx::fillRect(const QRectF &r, const QColor &color)
{
    fillRect(r, QBrush(color));
}


void QPaintEngineEx::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s)
{
    QBrush brush(state()->pen.color(), pixmap);
    QTransform xform = QTransform::fromTranslate(r.x() - s.x(), r.y() - s.y());
    if (!qFuzzyCompare(pixmap.devicePixelRatio(), qreal(1.0)))
        xform.scale(1.0/pixmap.devicePixelRatio(), 1.0/pixmap.devicePixelRatio());
    brush.setTransform(xform);

    qreal pts[] = { r.x(), r.y(),
                    r.x() + r.width(), r.y(),
                    r.x() + r.width(), r.y() + r.height(),
                    r.x(), r.y() + r.height() };

    QVectorPath path(pts, 4, nullptr, QVectorPath::RectangleHint);
    fill(path, brush);
}


void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &inPen)
{
#ifdef QT_DEBUG_DRAW
    qDebug() << "QPaintEngineEx::stroke()" << pen;
#endif

    Q_D(QPaintEngineEx);

    if (path.isEmpty())
        return;

    if (!d->strokeHandler) {
        d->strokeHandler = new StrokeHandler(path.elementCount()+4);
        d->stroker.setMoveToHook(qpaintengineex_moveTo);
        d->stroker.setLineToHook(qpaintengineex_lineTo);
        d->stroker.setCubicToHook(qpaintengineex_cubicTo);
    }

    QRectF clipRect;
    QPen pen = inPen;
    if (pen.style() > Qt::SolidLine) {
        QRectF cpRect = path.controlPointRect();
        const QTransform &xf = state()->matrix;
        if (pen.isCosmetic()) {
            clipRect = d->exDeviceRect;
            cpRect.translate(xf.dx(), xf.dy());
        } else {
            clipRect = xf.inverted().mapRect(QRectF(d->exDeviceRect));
        }
        // Check to avoid generating unwieldy amount of dashes that will not be visible anyway
        QRectF extentRect = cpRect & clipRect;
        qreal extent = qMax(extentRect.width(), extentRect.height());
        qreal patternLength = 0;
        const QList<qreal> pattern = pen.dashPattern();
        const int patternSize = qMin(pattern.size(), 32);
        for (int i = 0; i < patternSize; i++)
            patternLength += qMax(pattern.at(i), qreal(0));
        if (pen.widthF())
            patternLength *= pen.widthF();
        if (qFuzzyIsNull(patternLength)) {
            pen.setStyle(Qt::NoPen);
        } else if (qFuzzyIsNull(extent) || extent / patternLength > 10000) {
            // approximate stream of tiny dashes with semi-transparent solid line
            pen.setStyle(Qt::SolidLine);
            QColor color(pen.color());
            color.setAlpha(color.alpha() / 2);
            pen.setColor(color);
        }
    }

    if (!qpen_fast_equals(pen, d->strokerPen)) {
        d->strokerPen = pen;
        d->stroker.setJoinStyle(pen.joinStyle());
        d->stroker.setCapStyle(pen.capStyle());
        d->stroker.setMiterLimit(pen.miterLimit());
        qreal penWidth = pen.widthF();
        if (penWidth == 0)
            d->stroker.setStrokeWidth(1);
        else
            d->stroker.setStrokeWidth(penWidth);

        Qt::PenStyle style = pen.style();
        if (style == Qt::SolidLine) {
            d->activeStroker = &d->stroker;
        } else if (style == Qt::NoPen) {
            d->activeStroker = nullptr;
        } else {
            d->dasher.setDashPattern(pen.dashPattern());
            d->dasher.setDashOffset(pen.dashOffset());
            d->activeStroker = &d->dasher;
        }
    }

    if (!d->activeStroker) {
        return;
    }

    if (!clipRect.isNull())
        d->activeStroker->setClipRect(clipRect);

    if (d->activeStroker == &d->stroker)
        d->stroker.setForceOpen(path.hasExplicitOpen());

    const QPainterPath::ElementType *types = path.elements();
    const qreal *points = path.points();
    int pointCount = path.elementCount();

    const qreal *lastPoint = points + (pointCount<<1);

    d->strokeHandler->types.reset();
    d->strokeHandler->pts.reset();

    // Some engines might decide to optimize for the non-shape hint later on...
    uint flags = QVectorPath::WindingFill;

    if (path.elementCount() > 2)
        flags |= QVectorPath::NonConvexShapeMask;

    if (d->stroker.capStyle() == Qt::RoundCap || d->stroker.joinStyle() == Qt::RoundJoin)
        flags |= QVectorPath::CurvedShapeMask;

    // ### Perspective Xforms are currently not supported...
    if (!pen.isCosmetic()) {
        // We include cosmetic pens in this case to avoid having to
        // change the current transform. Normal transformed,
        // non-cosmetic pens will be transformed as part of fill
        // later, so they are also covered here..
        d->activeStroker->setCurveThresholdFromTransform(state()->matrix);
        d->activeStroker->begin(d->strokeHandler);
        if (types) {
            while (points < lastPoint) {
                switch (*types) {
                case QPainterPath::MoveToElement:
                    d->activeStroker->moveTo(points[0], points[1]);
                    points += 2;
                    ++types;
                    break;
                case QPainterPath::LineToElement:
                    d->activeStroker->lineTo(points[0], points[1]);
                    points += 2;
                    ++types;
                    break;
                case QPainterPath::CurveToElement:
                    d->activeStroker->cubicTo(points[0], points[1],
                                              points[2], points[3],
                                              points[4], points[5]);
                    points += 6;
                    types += 3;
                    flags |= QVectorPath::CurvedShapeMask;
                    break;
                default:
                    break;
                }
            }
            if (path.hasImplicitClose())
                d->activeStroker->lineTo(path.points()[0], path.points()[1]);

        } else {
            d->activeStroker->moveTo(points[0], points[1]);
            points += 2;
            while (points < lastPoint) {
                d->activeStroker->lineTo(points[0], points[1]);
                points += 2;
            }
            if (path.hasImplicitClose())
                d->activeStroker->lineTo(path.points()[0], path.points()[1]);
        }
        d->activeStroker->end();

        if (!d->strokeHandler->types.size()) // an empty path...
            return;

        QVectorPath strokePath(d->strokeHandler->pts.data(),
                               d->strokeHandler->types.size(),
                               d->strokeHandler->types.data(),
                               flags);
        fill(strokePath, pen.brush());
    } else {
        // For cosmetic pens we need a bit of trickery... We to process xform the input points
        if (state()->matrix.type() >= QTransform::TxProject) {
            QPainterPath painterPath = state()->matrix.map(path.convertToPainterPath());
            d->activeStroker->strokePath(painterPath, d->strokeHandler, QTransform());
        } else {
            d->activeStroker->setCurveThresholdFromTransform(QTransform());
            d->activeStroker->begin(d->strokeHandler);
            if (types) {
                while (points < lastPoint) {
                    switch (*types) {
                    case QPainterPath::MoveToElement: {
                        QPointF pt = (*(const QPointF *) points) * state()->matrix;
                        d->activeStroker->moveTo(pt.x(), pt.y());
                        points += 2;
                        ++types;
                        break;
                    }
                    case QPainterPath::LineToElement: {
                        QPointF pt = (*(const QPointF *) points) * state()->matrix;
                        d->activeStroker->lineTo(pt.x(), pt.y());
                        points += 2;
                        ++types;
                        break;
                    }
                    case QPainterPath::CurveToElement: {
                        QPointF c1 = ((const QPointF *) points)[0] * state()->matrix;
                        QPointF c2 = ((const QPointF *) points)[1] * state()->matrix;
                        QPointF e =  ((const QPointF *) points)[2] * state()->matrix;
                        d->activeStroker->cubicTo(c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y());
                        points += 6;
                        types += 3;
                        flags |= QVectorPath::CurvedShapeMask;
                        break;
                    }
                    default:
                        break;
                    }
                }
                if (path.hasImplicitClose()) {
                    QPointF pt = * ((const QPointF *) path.points()) * state()->matrix;
                    d->activeStroker->lineTo(pt.x(), pt.y());
                }

            } else {
                QPointF p = ((const QPointF *)points)[0] * state()->matrix;
                d->activeStroker->moveTo(p.x(), p.y());
                points += 2;
                while (points < lastPoint) {
                    QPointF p = ((const QPointF *)points)[0] * state()->matrix;
                    d->activeStroker->lineTo(p.x(), p.y());
                    points += 2;
                }
                if (path.hasImplicitClose())
                    d->activeStroker->lineTo(p.x(), p.y());
            }
            d->activeStroker->end();
        }

        QVectorPath strokePath(d->strokeHandler->pts.data(),
                               d->strokeHandler->types.size(),
                               d->strokeHandler->types.data(),
                               flags);

        QTransform xform = state()->matrix;
        state()->matrix = QTransform();
        transformChanged();

        QBrush brush = pen.brush();
        if (qbrush_style(brush) != Qt::SolidPattern)
            brush.setTransform(brush.transform() * xform);

        fill(strokePath, brush);

        state()->matrix = xform;
        transformChanged();
    }
}


QPaintEngineEx::QPaintEngineEx(QPaintEngineExPrivate &data)
    : QPaintEngine(data, AllFeatures)
{
    extended = true;
}


void QPaintEngineEx::drawEllipse(const QRectF &r)
{
    qreal pts[26]; // QPointF[13] without constructors...
    union {
        qreal *ptr;
        QPointF *points;
    } x;
    x.ptr = pts;

    int point_count = 0;
    x.points[0] = qt_curves_for_arc(r, 0, -360, x.points + 1, &point_count);
    if (point_count == 0)
        return;
    QVectorPath vp((qreal *) pts, point_count + 1, qpaintengineex_ellipse_types, QVectorPath::EllipseHint);
    draw(vp);
}


void QPaintEngineEx::drawLines(const QLineF *lines, int lineCount)
{
    int elementCount = lineCount << 1;
    while (elementCount > 0) {
        int count = qMin(elementCount, 32);

        QVectorPath path((const qreal *) lines, count, qpaintengineex_line_types_16,
                         QVectorPath::LinesHint);
        stroke(path, state()->pen);

        elementCount -= 32;
        lines += 16;
    }
}


void QPaintEngineEx::updateState(const QPaintEngineState &)
{
    // do nothing...
}


void QPaintEngineEx::clip(const QRect &r, Qt::ClipOperation op)
{
    qreal right = r.x() + r.width();
    qreal bottom = r.y() + r.height();
    qreal pts[] = { qreal(r.x()), qreal(r.y()),
                    right, qreal(r.y()),
                    right, bottom,
                    qreal(r.x()), bottom,
                    qreal(r.x()), qreal(r.y()) };
    QVectorPath vp(pts, 5, nullptr, QVectorPath::RectangleHint);
    clip(vp, op);
}


QVectorPath::~QVectorPath()
{
    if (m_hints & ShouldUseCacheHint) {
        CacheEntry *e = m_cache;
        while (e) {
            if (e->data)
                e->cleanup(e->engine, e->data);
            CacheEntry *n = e->next;
            delete e;
            e = n;
        }
    }
}


void QPaintEngineEx::drawRects(const QRectF *rects, int rectCount)
{
    for (int i=0; i<rectCount; ++i) {
        const QRectF &r = rects[i];
        qreal right = r.x() + r.width();
        qreal bottom = r.y() + r.height();
        qreal pts[] = { r.x(), r.y(),
                        right, r.y(),
                        right, bottom,
                        r.x(), bottom,
                        r.x(), r.y() };
        QVectorPath vp(pts, 5, nullptr, QVectorPath::RectangleHint);
        draw(vp);
    }
}


void QPaintEngineEx::drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount,
                                         const QPixmap &pixmap, QPainter::PixmapFragmentHints /*hints*/)
{
    if (pixmap.isNull())
        return;

    qreal oldOpacity = state()->opacity;
    QTransform oldTransform = state()->matrix;

    for (int i = 0; i < fragmentCount; ++i) {
        QTransform transform = oldTransform;
        transform.translate(fragments[i].x, fragments[i].y);
        transform.rotate(fragments[i].rotation);
        state()->opacity = oldOpacity * fragments[i].opacity;
        state()->matrix = transform;
        opacityChanged();
        transformChanged();

        qreal w = fragments[i].scaleX * fragments[i].width;
        qreal h = fragments[i].scaleY * fragments[i].height;
        QRectF sourceRect(fragments[i].sourceLeft, fragments[i].sourceTop,
                          fragments[i].width, fragments[i].height);
        drawPixmap(QRectF(-0.5 * w, -0.5 * h, w, h), pixmap, sourceRect);
    }

    state()->opacity = oldOpacity;
    state()->matrix = oldTransform;
    opacityChanged();
    transformChanged();
}


static void qpaintengineex_lineTo(qreal x, qreal y, void *data) {
    ((StrokeHandler *) data)->pts.add(x);
    ((StrokeHandler *) data)->pts.add(y);
    ((StrokeHandler *) data)->types.add(QPainterPath::LineToElement);
}


void QPaintEngineEx::draw(const QVectorPath &path)
{
    const QBrush &brush = state()->brush;
    if (qbrush_style(brush) != Qt::NoBrush)
        fill(path, brush);

    const QPen &pen = state()->pen;
    if (qpen_style(pen) != Qt::NoPen && qbrush_style(qpen_brush(pen)) != Qt::NoBrush)
        stroke(path, pen);
}


void QPaintEngineEx::drawRects(const QRect *rects, int rectCount)
{
    for (int i=0; i<rectCount; ++i) {
        const QRect &r = rects[i];
        // ### Is there a one off here?
        qreal right = r.x() + r.width();
        qreal bottom = r.y() + r.height();
        qreal pts[] = { qreal(r.x()), qreal(r.y()),
                        right, qreal(r.y()),
                        right, bottom,
                        qreal(r.x()), bottom,
                        qreal(r.x()), qreal(r.y()) };
        QVectorPath vp(pts, 5, nullptr, QVectorPath::RectangleHint);
        draw(vp);
    }
}


const QVectorPath &qtVectorPathForPath(const QPainterPath &path)
{
    Q_ASSERT(path.d_func());
    return path.d_func()->vectorPath();
}


void QPaintEngineEx::drawStaticTextItem(QStaticTextItem *staticTextItem)
{
    QPainterPath path;
    path.setFillRule(Qt::WindingFill);

    if (staticTextItem->numGlyphs == 0)
        return;

    QFontEngine *fontEngine = staticTextItem->fontEngine();
    fontEngine->addGlyphsToPath(staticTextItem->glyphs, staticTextItem->glyphPositions,
                                staticTextItem->numGlyphs, &path, { });
    if (!path.isEmpty()) {
        QPainterState *s = state();
        QPainter::RenderHints oldHints = s->renderHints;
        bool changedHints = false;
        if (bool(oldHints & QPainter::TextAntialiasing)
            && !bool(fontEngine->fontDef.styleStrategy & QFont::NoAntialias)
            && !bool(oldHints & QPainter::Antialiasing)) {
            s->renderHints |= QPainter::Antialiasing;
            renderHintsChanged();
            changedHints = true;
        }

        fill(qtVectorPathForPath(path), s->pen.brush());

        if (changedHints) {
            s->renderHints = oldHints;
            renderHintsChanged();
        }
    }
}


void QPaintEngineEx::fillRect(const QRectF &r, const QBrush &brush)
{
    qreal pts[] = { r.x(), r.y(), r.x() + r.width(), r.y(),
                    r.x() + r.width(), r.y() + r.height(), r.x(), r.y() + r.height() };
    QVectorPath vp(pts, 4, nullptr, QVectorPath::RectangleHint);
    fill(vp, brush);
}


void QPaintEngineEx::clip(const QPainterPath &path, Qt::ClipOperation op)
{
    if (path.isEmpty()) {
        QVectorPath vp(nullptr, 0);
        clip(vp, op);
    } else {
        clip(qtVectorPathForPath(path), op);
    }
}