[cairo-spans] Implement a span renderer for cairo_image_surface_t.

This implementation first produces an A8 alpha mask and then
pixman_image_composites the result to the destination with the source.
Clipping is handled by pixman when it is region clipping or by
cairo-surface-fallback when it is something more complex.
This commit is contained in:
M Joonas Pihlaja 2008-07-11 13:10:20 +03:00
parent 7994fc06ad
commit 85b81a3e59

View file

@ -1078,6 +1078,15 @@ _cairo_image_surface_fill_rectangles (void *abstract_surface,
return status;
}
static cairo_format_t
_cairo_mask_format_from_antialias (cairo_antialias_t antialias)
{
if (antialias == CAIRO_ANTIALIAS_NONE)
return CAIRO_FORMAT_A1;
return CAIRO_FORMAT_A8;
}
static cairo_int_status_t
_cairo_image_surface_composite_trapezoids (cairo_operator_t op,
const cairo_pattern_t *pattern,
@ -1095,13 +1104,10 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t op,
cairo_surface_attributes_t attributes;
cairo_image_surface_t *dst = abstract_dst;
cairo_image_surface_t *src;
cairo_int_status_t status;
pixman_image_t *mask;
pixman_format_code_t format;
uint32_t *mask_data;
cairo_int_status_t status;
cairo_image_surface_t *mask = NULL;
pixman_trapezoid_t stack_traps[CAIRO_STACK_ARRAY_LENGTH (pixman_trapezoid_t)];
pixman_trapezoid_t *pixman_traps = stack_traps;
int mask_stride;
int i;
if (height == 0 || width == 0)
@ -1165,40 +1171,19 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t op,
if (unlikely (status))
goto CLEANUP_SOURCE;
switch (antialias) {
case CAIRO_ANTIALIAS_NONE:
format = PIXMAN_a1;
mask_stride = ((width + 31) / 8) & ~0x03;
break;
case CAIRO_ANTIALIAS_GRAY:
case CAIRO_ANTIALIAS_SUBPIXEL:
case CAIRO_ANTIALIAS_DEFAULT:
default:
format = PIXMAN_a8;
mask_stride = (width + 3) & ~3;
break;
}
/* The image must be initially transparent */
mask_data = calloc (mask_stride, height);
if (unlikely (mask_data == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
mask = (cairo_image_surface_t *)
cairo_image_surface_create (
_cairo_mask_format_from_antialias (antialias),
width, height);
if (cairo_surface_status (&mask->base) != CAIRO_STATUS_SUCCESS)
goto CLEANUP_SOURCE;
}
mask = pixman_image_create_bits (format, width, height,
mask_data, mask_stride);
if (unlikely (mask == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP_IMAGE_DATA;
}
pixman_add_trapezoids (mask, - dst_x, - dst_y,
pixman_add_trapezoids (mask->pixman_image, - dst_x, - dst_y,
num_traps, pixman_traps);
pixman_image_composite (_pixman_operator (op),
src->pixman_image,
mask,
mask->pixman_image,
dst->pixman_image,
src_x + attributes.x_offset,
src_y + attributes.y_offset,
@ -1208,15 +1193,13 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t op,
if (! _cairo_operator_bounded_by_mask (op))
status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base,
&attributes, src->width, src->height,
&attributes,
src->width, src->height,
width, height,
src_x, src_y,
0, 0,
dst_x, dst_y, width, height);
pixman_image_unref (mask);
CLEANUP_IMAGE_DATA:
free (mask_data);
cairo_surface_destroy (&mask->base);
CLEANUP_SOURCE:
_cairo_pattern_release_surface (pattern, &src->base, &attributes);
@ -1228,6 +1211,216 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t op,
return status;
}
typedef struct _cairo_image_surface_span_renderer {
cairo_span_renderer_t base;
cairo_operator_t op;
const cairo_pattern_t *pattern;
cairo_antialias_t antialias;
cairo_image_surface_t *src;
cairo_surface_attributes_t src_attributes;
cairo_image_surface_t *mask;
cairo_image_surface_t *dst;
cairo_composite_rectangles_t composite_rectangles;
} cairo_image_surface_span_renderer_t;
static cairo_status_t
_cairo_image_surface_span_renderer_render_row (
void *abstract_renderer,
int y,
const cairo_half_open_span_t *spans,
unsigned num_spans)
{
cairo_image_surface_span_renderer_t *renderer = abstract_renderer;
int xmin = renderer->composite_rectangles.mask.x;
int xmax = xmin + renderer->composite_rectangles.width;
uint8_t *row;
int prev_x = xmin;
int prev_alpha = 0;
unsigned i;
/* Make sure we're within y-range. */
y -= renderer->composite_rectangles.mask.y;
if (y < 0 || y >= renderer->composite_rectangles.height)
return CAIRO_STATUS_SUCCESS;
row = (uint8_t*)(renderer->mask->data) + y*(size_t)renderer->mask->stride - xmin;
/* Find the first span within x-range. */
for (i=0; i < num_spans && spans[i].x < xmin; i++) {}
if (i>0)
prev_alpha = spans[i-1].coverage;
/* Set the intermediate spans. */
for (; i < num_spans; i++) {
int x = spans[i].x;
if (x >= xmax)
break;
if (prev_alpha != 0) {
/* We implement setting rendering the most common single
* pixel wide span case to avoid the overhead of a memset
* call. Open coding setting longer spans didn't show a
* noticeable improvement over memset. */
if (x == prev_x + 1) {
row[prev_x] = prev_alpha;
}
else {
memset(row + prev_x, prev_alpha, x - prev_x);
}
}
prev_x = x;
prev_alpha = spans[i].coverage;
}
if (prev_alpha != 0 && prev_x < xmax) {
memset(row + prev_x, prev_alpha, xmax - prev_x);
}
return CAIRO_STATUS_SUCCESS;
}
static void
_cairo_image_surface_span_renderer_destroy (void *abstract_renderer)
{
cairo_image_surface_span_renderer_t *renderer = abstract_renderer;
if (!renderer) return;
if (renderer->src != NULL) {
_cairo_pattern_release_surface (renderer->pattern,
&renderer->src->base,
&renderer->src_attributes);
}
if (renderer->mask != NULL)
cairo_surface_destroy (&renderer->mask->base);
free (renderer);
}
static cairo_status_t
_cairo_image_surface_span_renderer_finish (void *abstract_renderer)
{
cairo_image_surface_span_renderer_t *renderer = abstract_renderer;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
if (renderer->src == NULL || renderer->mask == NULL)
return CAIRO_STATUS_SUCCESS;
status = cairo_surface_status (&renderer->mask->base);
if (status == CAIRO_STATUS_SUCCESS) {
cairo_composite_rectangles_t *rects = &renderer->composite_rectangles;
cairo_image_surface_t *src = renderer->src;
cairo_image_surface_t *dst = renderer->dst;
cairo_surface_attributes_t *src_attributes = &renderer->src_attributes;
int width = rects->width;
int height = rects->height;
pixman_image_composite (_pixman_operator (renderer->op),
src->pixman_image,
renderer->mask->pixman_image,
dst->pixman_image,
rects->src.x + src_attributes->x_offset,
rects->src.y + src_attributes->y_offset,
0, 0, /* mask.x, mask.y */
rects->dst.x, rects->dst.y,
width, height);
if (! _cairo_operator_bounded_by_mask (renderer->op))
status = _cairo_surface_composite_shape_fixup_unbounded (
&dst->base,
src_attributes,
src->width, src->height,
rects->width, rects->height,
rects->src.x, rects->src.y,
0, 0, /* mask.x, mask.y */
rects->dst.x, rects->dst.y,
rects->width, rects->height);
}
if (status != CAIRO_STATUS_SUCCESS)
return _cairo_span_renderer_set_error (abstract_renderer,
status);
return CAIRO_STATUS_SUCCESS;
}
static cairo_bool_t
_cairo_image_surface_check_span_renderer (cairo_operator_t op,
const cairo_pattern_t *pattern,
void *abstract_dst,
cairo_antialias_t antialias,
const cairo_composite_rectangles_t *rects)
{
(void) op;
(void) pattern;
(void) abstract_dst;
(void) antialias;
(void) rects;
return TRUE;
}
static cairo_span_renderer_t *
_cairo_image_surface_create_span_renderer (cairo_operator_t op,
const cairo_pattern_t *pattern,
void *abstract_dst,
cairo_antialias_t antialias,
const cairo_composite_rectangles_t *rects)
{
cairo_image_surface_t *dst = abstract_dst;
cairo_image_surface_span_renderer_t *renderer
= calloc(1, sizeof(*renderer));
cairo_status_t status;
int width = rects->width;
int height = rects->height;
if (renderer == NULL)
return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY);
renderer->base.destroy = _cairo_image_surface_span_renderer_destroy;
renderer->base.finish = _cairo_image_surface_span_renderer_finish;
renderer->base.render_row =
_cairo_image_surface_span_renderer_render_row;
renderer->op = op;
renderer->pattern = pattern;
renderer->antialias = antialias;
renderer->dst = dst;
renderer->composite_rectangles = *rects;
status = _cairo_pattern_acquire_surface (
renderer->pattern, &renderer->dst->base,
rects->src.x, rects->src.y,
width, height,
(cairo_surface_t **) &renderer->src,
&renderer->src_attributes);
if (status)
goto unwind;
status = _cairo_image_surface_set_attributes (
renderer->src, &renderer->src_attributes,
rects->dst.x + width/2, rects->dst.y + height/2);
if (status)
goto unwind;
/* TODO: support rendering to A1 surfaces (or: go add span
* compositing to pixman.) */
renderer->mask = (cairo_image_surface_t *)
cairo_image_surface_create (CAIRO_FORMAT_A8,
width, height);
status = cairo_surface_status (&renderer->mask->base);
unwind:
if (status != CAIRO_STATUS_SUCCESS) {
_cairo_image_surface_span_renderer_destroy (renderer);
return _cairo_span_renderer_create_in_error (status);
}
return &renderer->base;
}
cairo_int_status_t
_cairo_image_surface_set_clip_region (void *abstract_surface,
cairo_region_t *region)
@ -1303,8 +1496,8 @@ const cairo_surface_backend_t _cairo_image_surface_backend = {
_cairo_image_surface_composite,
_cairo_image_surface_fill_rectangles,
_cairo_image_surface_composite_trapezoids,
NULL, /* create_span_renderer */
NULL, /* check_span_renderer */
_cairo_image_surface_create_span_renderer,
_cairo_image_surface_check_span_renderer,
NULL, /* copy_page */
NULL, /* show_page */
_cairo_image_surface_set_clip_region,