summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cairo-image-surface.c273
1 files changed, 233 insertions, 40 deletions
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index ef5e238d..f484466a 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -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,