mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-05 15:48:00 +02:00
[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:
parent
7994fc06ad
commit
85b81a3e59
1 changed files with 233 additions and 40 deletions
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue