diff --git a/gfx/cairo/README b/gfx/cairo/README index d830828746ad..4390467c092f 100644 --- a/gfx/cairo/README +++ b/gfx/cairo/README @@ -68,6 +68,34 @@ premultiply-alpha-solid-gradients.patch: bug 539165; multiply the solid color by xlib-initialize-members.path: bug 548793; initialize XRender version if the server doesn't have the extension +remove-comma: remove a comma from enum + +d2d.patch: add d2d support + +fix-zero-len-graident.patch: fix zero length gradients + +fix-clip-copy.patch: fix clip copying + +fix-clip-region-simplification.patch: fixes a bug in clip region simplifications + +expand-in-stroke-limits.patch: expand the in-stroke limits to avoid a bug + +d2d-dwrite.patch: update the d2d/dwrite stuff + +add-a-stash-of-cairo_t-s.patch: use the stash to avoid malloc/freeing cairo_t's + +bgr.patch: fix image wrapping + +disable-server-graidents.patch: disable server-side gradients + +clip-invariant.patch: make rasterization closer to being clip invariant + +fix-unnecessary-fallback.patch: avoid unnecessary fallback + +handle-a1-upload.patch: handle a1 image uploads through converter + +surface-clipper.patch: remove an incorrect optimization + ==== pixman patches ==== pixman-neon.patch: add ARM NEON optimized compositing functions diff --git a/gfx/cairo/add-a-stash-of-cairo_t-s.patch b/gfx/cairo/add-a-stash-of-cairo_t-s.patch new file mode 100644 index 000000000000..c6fcdd9d881a --- /dev/null +++ b/gfx/cairo/add-a-stash-of-cairo_t-s.patch @@ -0,0 +1,75 @@ +commit dfec2c249915560cedd2b49326c6629ad8a0b0f2 +Author: Jeff Muizelaar +Date: Tue Mar 2 16:01:41 2010 -0500 + + add a stash of cairo_t's + +diff --git a/src/cairo.c b/src/cairo.c +index 3c9d892..4b27b83 100644 +--- a/src/cairo.c ++++ b/src/cairo.c +@@ -119,7 +119,63 @@ _cairo_set_error (cairo_t *cr, cairo_status_t status) + _cairo_status_set_error (&cr->status, _cairo_error (status)); + } + +-#if HAS_ATOMIC_OPS ++#if defined(_MSC_VER) ++#pragma intrinsic(_BitScanForward) ++static __forceinline int ++ffs(int x) ++{ ++ unsigned long i; ++ ++ if (_BitScanForward(&i, x) != 0) ++ return i + 1; ++ ++ return 0; ++} ++#endif ++ ++ ++#if CAIRO_NO_MUTEX ++/* We keep a small stash of contexts to reduce malloc pressure */ ++#define CAIRO_STASH_SIZE 4 ++static struct { ++ cairo_t pool[CAIRO_STASH_SIZE]; ++ int occupied; ++} _context_stash; ++ ++static cairo_t * ++_context_get (void) ++{ ++ int avail, old, new; ++ ++ old = _context_stash.occupied; ++ avail = ffs (~old) - 1; ++ if (avail >= CAIRO_STASH_SIZE) ++ return malloc (sizeof (cairo_t)); ++ ++ new = old | (1 << avail); ++ _context_stash.occupied = new; ++ ++ return &_context_stash.pool[avail]; ++} ++ ++static void ++_context_put (cairo_t *cr) ++{ ++ int old, new, avail; ++ ++ if (cr < &_context_stash.pool[0] || ++ cr >= &_context_stash.pool[CAIRO_STASH_SIZE]) ++ { ++ free (cr); ++ return; ++ } ++ ++ avail = ~(1 << (cr - &_context_stash.pool[0])); ++ old = _context_stash.occupied; ++ new = old & avail; ++ _context_stash.occupied = new; ++} ++#elif HAS_ATOMIC_OPS + /* We keep a small stash of contexts to reduce malloc pressure */ + #define CAIRO_STASH_SIZE 4 + static struct { diff --git a/gfx/cairo/bgr.patch b/gfx/cairo/bgr.patch new file mode 100644 index 000000000000..af72fa237157 --- /dev/null +++ b/gfx/cairo/bgr.patch @@ -0,0 +1,104 @@ +commit d2120bdb06c9aacc470bb346d6bc2071c2e0749d +Author: Jeff Muizelaar +Date: Fri Mar 12 15:32:09 2010 -0500 + + BGR + +diff --git a/src/cairo-surface.c b/src/cairo-surface.c +index 332e3ab..4a1d6a0 100644 +--- a/src/cairo-surface.c ++++ b/src/cairo-surface.c +@@ -1501,7 +1501,9 @@ static void + _wrap_release_source_image (void *data) + { + struct acquire_source_image_data *acquire_data = data; +- _cairo_surface_release_source_image (acquire_data->src, acquire_data->image, acquire_data->image_extra); ++ _cairo_surface_release_source_image (acquire_data->src, ++ acquire_data->image, ++ acquire_data->image_extra); + free(data); + } + +@@ -1515,42 +1517,47 @@ _wrap_image (cairo_surface_t *src, + cairo_image_surface_t *surface; + cairo_status_t status; + +- struct acquire_source_image_data *data = malloc(sizeof(*data)); ++ struct acquire_source_image_data *data = malloc (sizeof (*data)); ++ if (unlikely (data == NULL)) ++ return _cairo_error (CAIRO_STATUS_NO_MEMORY); + data->src = src; + data->image = image; + data->image_extra = image_extra; + +- surface = (cairo_image_surface_t*)cairo_image_surface_create_for_data (image->data, +- image->format, +- image->width, +- image->height, +- image->stride); ++ surface = (cairo_image_surface_t*) ++ _cairo_image_surface_create_with_pixman_format (image->data, ++ image->pixman_format, ++ image->width, ++ image->height, ++ image->stride); + status = surface->base.status; +- if (status) ++ if (status) { ++ free (data); + return status; ++ } + + status = _cairo_user_data_array_set_data (&surface->base.user_data, +- &wrap_image_key, +- data, +- _wrap_release_source_image); ++ &wrap_image_key, ++ data, ++ _wrap_release_source_image); + if (status) { + cairo_surface_destroy (&surface->base); ++ free (data); + return status; + } +-/* +- pixman_image_set_component_alpha (surface->pixman_image, +- pixman_image_get_component_alpha (image->pixman_image)); +-*/ ++ ++ pixman_image_set_component_alpha ( ++ surface->pixman_image, ++ pixman_image_get_component_alpha (image->pixman_image)); ++ + *out = surface; + return CAIRO_STATUS_SUCCESS; + } + +- + /** + * _cairo_surface_clone_similar: + * @surface: a #cairo_surface_t + * @src: the source image +- * @content: target content mask + * @src_x: extent for the rectangle in src we actually care about + * @src_y: extent for the rectangle in src we actually care about + * @width: extent for the rectangle in src we actually care about +@@ -1627,12 +1634,12 @@ _cairo_surface_clone_similar (cairo_surface_t *surface, + _cairo_surface_release_source_image (src, image, image_extra); + } else { + status = +- surface->backend->clone_similar (surface, &image->base, +- src_x, src_y, +- width, height, +- clone_offset_x, +- clone_offset_y, +- clone_out); ++ surface->backend->clone_similar (surface, &image->base, ++ src_x, src_y, ++ width, height, ++ clone_offset_x, ++ clone_offset_y, ++ clone_out); + cairo_surface_destroy(&image->base); + } + } diff --git a/gfx/cairo/cairo/src/Makefile.in b/gfx/cairo/cairo/src/Makefile.in index 3939f195ceba..c15472967847 100644 --- a/gfx/cairo/cairo/src/Makefile.in +++ b/gfx/cairo/cairo/src/Makefile.in @@ -74,7 +74,10 @@ CSRCS = \ cairo-array.c \ cairo-atomic.c \ cairo-bentley-ottmann.c \ - cairo-cache.c \ + cairo-bentley-ottmann-rectilinear.c \ + cairo-bentley-ottmann-rectangular.c \ + cairo-base64-stream.c \ + cairo-cache.c \ cairo-clip.c \ cairo-color.c \ cairo-debug.c \ @@ -92,7 +95,6 @@ CSRCS = \ cairo-image-surface.c \ cairo-lzw.c \ cairo-matrix.c \ - cairo-meta-surface.c \ cairo-misc.c \ cairo-mutex.c \ cairo-output-stream.c \ @@ -106,17 +108,20 @@ CSRCS = \ cairo-pattern.c \ cairo-pen.c \ cairo-polygon.c \ + cairo-recording-surface.c \ cairo-rectangle.c \ cairo-region.c \ cairo-scaled-font.c \ cairo-scaled-font-subsets.c \ - cairo-skiplist.c \ cairo-slope.c \ cairo-spans.c \ cairo-spline.c \ cairo-stroke-style.c \ cairo-surface.c \ + cairo-surface-clipper.c \ cairo-surface-fallback.c \ + cairo-surface-wrapper.c \ + cairo-tee-surface.c \ cairo-tor-scan-converter.c \ cairo-toy-font-face.c \ cairo-traps.c \ diff --git a/gfx/cairo/cairo/src/cairo-analysis-surface-private.h b/gfx/cairo/cairo/src/cairo-analysis-surface-private.h index 28bfd3bf437b..f67caf904ef0 100644 --- a/gfx/cairo/cairo/src/cairo-analysis-surface-private.h +++ b/gfx/cairo/cairo/src/cairo-analysis-surface-private.h @@ -38,13 +38,11 @@ #include "cairoint.h" cairo_private cairo_surface_t * -_cairo_analysis_surface_create (cairo_surface_t *target, - int width, - int height); +_cairo_analysis_surface_create (cairo_surface_t *target); cairo_private void _cairo_analysis_surface_set_ctm (cairo_surface_t *surface, - cairo_matrix_t *ctm); + const cairo_matrix_t *ctm); cairo_private void _cairo_analysis_surface_get_ctm (cairo_surface_t *surface, diff --git a/gfx/cairo/cairo/src/cairo-analysis-surface.c b/gfx/cairo/cairo/src/cairo-analysis-surface.c index 3a1b2f9ff8a6..0ba667d4ae40 100644 --- a/gfx/cairo/cairo/src/cairo-analysis-surface.c +++ b/gfx/cairo/cairo/src/cairo-analysis-surface.c @@ -38,14 +38,13 @@ #include "cairo-analysis-surface-private.h" #include "cairo-paginated-private.h" -#include "cairo-meta-surface-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-region-private.h" typedef struct { cairo_surface_t base; - int width; - int height; - cairo_surface_t *target; + cairo_surface_t *target; cairo_bool_t first_op; cairo_bool_t has_supported; @@ -53,7 +52,6 @@ typedef struct { cairo_region_t supported_region; cairo_region_t fallback_region; - cairo_rectangle_int_t current_clip; cairo_box_t page_bbox; cairo_bool_t has_ctm; @@ -78,9 +76,9 @@ _cairo_analysis_surface_merge_status (cairo_int_status_t status_a, status_b == CAIRO_INT_STATUS_IMAGE_FALLBACK) return CAIRO_INT_STATUS_IMAGE_FALLBACK; - if (status_a == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN || - status_b == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN) - return CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN; + if (status_a == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN || + status_b == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; if (status_a == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY || status_b == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) @@ -94,64 +92,41 @@ _cairo_analysis_surface_merge_status (cairo_int_status_t status_a, } static cairo_int_status_t -_analyze_meta_surface_pattern (cairo_analysis_surface_t *surface, - const cairo_pattern_t *pattern) +_analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, + const cairo_pattern_t *pattern) { - cairo_surface_t *analysis = &surface->base; const cairo_surface_pattern_t *surface_pattern; - cairo_status_t status; cairo_bool_t old_has_ctm; cairo_matrix_t old_ctm, p2d; - cairo_rectangle_int_t old_clip; - cairo_rectangle_int_t meta_extents; - int old_width; - int old_height; + cairo_status_t status; assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE); surface_pattern = (const cairo_surface_pattern_t *) pattern; - assert (_cairo_surface_is_meta (surface_pattern->surface)); + assert (_cairo_surface_is_recording (surface_pattern->surface)); - old_width = surface->width; - old_height = surface->height; - old_clip = surface->current_clip; - status = _cairo_surface_get_extents (surface_pattern->surface, &meta_extents); - if (_cairo_status_is_error (status)) - return status; - - surface->width = meta_extents.width; - surface->height = meta_extents.height; - surface->current_clip.x = 0; - surface->current_clip.y = 0; - surface->current_clip.width = surface->width; - surface->current_clip.height = surface->height; old_ctm = surface->ctm; old_has_ctm = surface->has_ctm; + p2d = pattern->matrix; status = cairo_matrix_invert (&p2d); - /* _cairo_pattern_set_matrix guarantees invertibility */ assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_multiply (&surface->ctm, &p2d, &surface->ctm); - surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm); + surface->has_ctm = ! _cairo_matrix_is_identity (&surface->ctm); - status = _cairo_meta_surface_replay_and_create_regions (surface_pattern->surface, - analysis); - if (status == CAIRO_STATUS_SUCCESS) - status = analysis->status; + status = _cairo_recording_surface_replay_and_create_regions (surface_pattern->surface, + &surface->base); surface->ctm = old_ctm; surface->has_ctm = old_has_ctm; - surface->current_clip = old_clip; - surface->width = old_width; - surface->height = old_height; return status; } static cairo_int_status_t -_add_operation (cairo_analysis_surface_t *surface, - cairo_rectangle_int_t *rect, - cairo_int_status_t backend_status) +_add_operation (cairo_analysis_surface_t *surface, + cairo_rectangle_int_t *rect, + cairo_int_status_t backend_status) { cairo_int_status_t status; cairo_box_t bbox; @@ -174,26 +149,33 @@ _add_operation (cairo_analysis_surface_t *surface, _cairo_box_from_rectangle (&bbox, rect); if (surface->has_ctm) { + int tx, ty; - _cairo_matrix_transform_bounding_box_fixed (&surface->ctm, &bbox, NULL); + if (_cairo_matrix_is_integer_translation (&surface->ctm, &tx, &ty)) { + rect->x += tx; + rect->y += ty; + } else { + _cairo_matrix_transform_bounding_box_fixed (&surface->ctm, + &bbox, NULL); - if (bbox.p1.x == bbox.p2.x || bbox.p1.y == bbox.p2.y) { - /* Even though the operation is not visible we must be - * careful to not allow unsupported operations to be - * replayed to the backend during - * CAIRO_PAGINATED_MODE_RENDER */ - if (backend_status == CAIRO_STATUS_SUCCESS || - backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) - { - return CAIRO_STATUS_SUCCESS; - } - else - { - return CAIRO_INT_STATUS_IMAGE_FALLBACK; + if (bbox.p1.x == bbox.p2.x || bbox.p1.y == bbox.p2.y) { + /* Even though the operation is not visible we must be + * careful to not allow unsupported operations to be + * replayed to the backend during + * CAIRO_PAGINATED_MODE_RENDER */ + if (backend_status == CAIRO_STATUS_SUCCESS || + backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) + { + return CAIRO_STATUS_SUCCESS; + } + else + { + return CAIRO_INT_STATUS_IMAGE_FALLBACK; + } } + + _cairo_box_round_to_rectangle (&bbox, rect); } - - _cairo_box_round_to_rectangle (&bbox, rect); } if (surface->first_op) { @@ -234,8 +216,7 @@ _add_operation (cairo_analysis_surface_t *surface, * this region will be emitted as native operations. */ surface->has_supported = TRUE; - status = cairo_region_union_rectangle (&surface->supported_region, rect); - return status; + return cairo_region_union_rectangle (&surface->supported_region, rect); } /* Add the operation to the unsupported region. This region will @@ -246,7 +227,7 @@ _add_operation (cairo_analysis_surface_t *surface, status = cairo_region_union_rectangle (&surface->fallback_region, rect); /* The status CAIRO_INT_STATUS_IMAGE_FALLBACK is used to indicate - * unsupported operations to the meta surface as using + * unsupported operations to the recording surface as using * CAIRO_INT_STATUS_UNSUPPORTED would cause cairo-surface to * invoke the cairo-surface-fallback path then return * CAIRO_STATUS_SUCCESS. @@ -270,33 +251,7 @@ _cairo_analysis_surface_finish (void *abstract_surface) return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_analysis_surface_intersect_clip_path (void *abstract_surface, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_analysis_surface_t *surface = abstract_surface; - - if (path == NULL) { - surface->current_clip.x = 0; - surface->current_clip.y = 0; - surface->current_clip.width = surface->width; - surface->current_clip.height = surface->height; - } else { - cairo_rectangle_int_t extents; - cairo_bool_t is_empty; - - _cairo_path_fixed_approximate_clip_extents (path, &extents); - is_empty = _cairo_rectangle_intersect (&surface->current_clip, - &extents); - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t +static cairo_bool_t _cairo_analysis_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { @@ -305,76 +260,102 @@ _cairo_analysis_surface_get_extents (void *abstract_surface, return _cairo_surface_get_extents (surface->target, rectangle); } -static cairo_int_status_t -_cairo_analysis_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_rectangle_int_t *paint_extents) +static void +_rectangle_intersect_clip (cairo_rectangle_int_t *extents, cairo_clip_t *clip) { - cairo_analysis_surface_t *surface = abstract_surface; - cairo_status_t status, backend_status; - cairo_rectangle_int_t extents; + const cairo_rectangle_int_t *clip_extents; cairo_bool_t is_empty; - if (!surface->target->backend->paint) - backend_status = CAIRO_INT_STATUS_UNSUPPORTED; - else - backend_status = (*surface->target->backend->paint) (surface->target, op, - source, NULL); + clip_extents = NULL; + if (clip != NULL) + clip_extents = _cairo_clip_get_extents (clip); - if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN) - backend_status = _analyze_meta_surface_pattern (surface, source); + if (clip_extents != NULL) + is_empty = _cairo_rectangle_intersect (extents, clip_extents); +} - status = _cairo_surface_get_extents (&surface->base, &extents); - if (_cairo_status_is_error (status)) - return status; +static void +_cairo_analysis_surface_operation_extents (cairo_analysis_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + cairo_bool_t is_empty; + + is_empty = _cairo_surface_get_extents (&surface->base, extents); if (_cairo_operator_bounded_by_source (op)) { cairo_rectangle_int_t source_extents; - status = _cairo_pattern_get_extents (source, &source_extents); - if (unlikely (status)) - return status; - - is_empty = _cairo_rectangle_intersect (&extents, &source_extents); + _cairo_pattern_get_extents (source, &source_extents); + is_empty = _cairo_rectangle_intersect (extents, &source_extents); } - is_empty = _cairo_rectangle_intersect (&extents, &surface->current_clip); - if (paint_extents) - *paint_extents = extents; - - status = _add_operation (surface, &extents, backend_status); - - return status; + _rectangle_intersect_clip (extents, clip); } static cairo_int_status_t -_cairo_analysis_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_rectangle_int_t *mask_extents) +_cairo_analysis_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; - cairo_int_status_t status, backend_status; + cairo_status_t backend_status; + cairo_rectangle_int_t extents; + + if (surface->target->backend->paint == NULL) { + backend_status = CAIRO_INT_STATUS_UNSUPPORTED; + } else { + backend_status = + surface->target->backend->paint (surface->target, + op, source, clip); + if (_cairo_status_is_error (backend_status)) + return backend_status; + } + + if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) + backend_status = _analyze_recording_surface_pattern (surface, source); + + _cairo_analysis_surface_operation_extents (surface, + op, source, clip, + &extents); + + return _add_operation (surface, &extents, backend_status); +} + +static cairo_int_status_t +_cairo_analysis_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_analysis_surface_t *surface = abstract_surface; + cairo_int_status_t backend_status; cairo_rectangle_int_t extents; cairo_bool_t is_empty; - if (!surface->target->backend->mask) + if (surface->target->backend->mask == NULL) { backend_status = CAIRO_INT_STATUS_UNSUPPORTED; - else - backend_status = (*surface->target->backend->mask) (surface->target, op, - source, mask, NULL); + } else { + backend_status = + surface->target->backend->mask (surface->target, + op, source, mask, clip); + if (_cairo_status_is_error (backend_status)) + return backend_status; + } - if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN) { + if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) { cairo_int_status_t backend_source_status = CAIRO_STATUS_SUCCESS; cairo_int_status_t backend_mask_status = CAIRO_STATUS_SUCCESS; if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t *) source; - if (_cairo_surface_is_meta (surface_pattern->surface)) { + if (_cairo_surface_is_recording (surface_pattern->surface)) { backend_source_status = - _analyze_meta_surface_pattern (surface, source); + _analyze_recording_surface_pattern (surface, source); if (_cairo_status_is_error (backend_source_status)) return backend_source_status; } @@ -382,9 +363,9 @@ _cairo_analysis_surface_mask (void *abstract_surface, if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) mask; - if (_cairo_surface_is_meta (surface_pattern->surface)) { + if (_cairo_surface_is_recording (surface_pattern->surface)) { backend_mask_status = - _analyze_meta_surface_pattern (surface, mask); + _analyze_recording_surface_pattern (surface, mask); if (_cairo_status_is_error (backend_mask_status)) return backend_mask_status; } @@ -395,37 +376,19 @@ _cairo_analysis_surface_mask (void *abstract_surface, backend_mask_status); } - status = _cairo_surface_get_extents (&surface->base, &extents); - if (_cairo_status_is_error (status)) - return status; - - if (_cairo_operator_bounded_by_source (op)) { - cairo_rectangle_int_t source_extents; - - status = _cairo_pattern_get_extents (source, &source_extents); - if (unlikely (status)) - return status; - - is_empty = _cairo_rectangle_intersect (&extents, &source_extents); - } + _cairo_analysis_surface_operation_extents (surface, + op, source, clip, + &extents); if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t mask_extents; - status = _cairo_pattern_get_extents (mask, &mask_extents); - if (unlikely (status)) - return status; - + _cairo_pattern_get_extents (mask, &mask_extents); is_empty = _cairo_rectangle_intersect (&extents, &mask_extents); + } - is_empty = _cairo_rectangle_intersect (&extents, &surface->current_clip); - if (mask_extents) - *mask_extents = extents; - - status = _add_operation (surface, &extents, backend_status); - - return status; + return _add_operation (surface, &extents, backend_status); } static cairo_int_status_t @@ -438,55 +401,61 @@ _cairo_analysis_surface_stroke (void *abstract_surface, cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *stroke_extents) + cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; - cairo_status_t status, backend_status; + cairo_status_t backend_status; cairo_rectangle_int_t extents; cairo_bool_t is_empty; - if (!surface->target->backend->stroke) + if (surface->target->backend->stroke == NULL) { backend_status = CAIRO_INT_STATUS_UNSUPPORTED; - else - backend_status = (*surface->target->backend->stroke) (surface->target, op, - source, path, style, - ctm, ctm_inverse, - tolerance, antialias, NULL); - - if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN) - backend_status = _analyze_meta_surface_pattern (surface, source); - - status = _cairo_surface_get_extents (&surface->base, &extents); - if (_cairo_status_is_error (status)) - return status; - - if (_cairo_operator_bounded_by_source (op)) { - cairo_rectangle_int_t source_extents; - - status = _cairo_pattern_get_extents (source, &source_extents); - if (unlikely (status)) - return status; - - is_empty = _cairo_rectangle_intersect (&extents, &source_extents); + } else { + backend_status = + surface->target->backend->stroke (surface->target, op, + source, path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); + if (_cairo_status_is_error (backend_status)) + return backend_status; } - is_empty = _cairo_rectangle_intersect (&extents, &surface->current_clip); + if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) + backend_status = _analyze_recording_surface_pattern (surface, source); + + _cairo_analysis_surface_operation_extents (surface, + op, source, clip, + &extents); if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t mask_extents; - _cairo_path_fixed_approximate_stroke_extents (path, - style, ctm, - &mask_extents); + /* If the backend can handle the stroke, then mark the approximate + * extents of the operation. However, if we need to fallback in order + * to draw the stroke, then ensure that the fallback is as tight as + * possible -- both to minimise output file size and to ensure good + * quality printed output for neighbouring regions. + */ + if (backend_status == CAIRO_STATUS_SUCCESS) { + _cairo_path_fixed_approximate_stroke_extents (path, + style, ctm, + &mask_extents); + } else { + cairo_status_t status; + + status = _cairo_path_fixed_stroke_extents (path, style, + ctm, ctm_inverse, + tolerance, + &mask_extents); + if (unlikely (status)) + return status; + } is_empty = _cairo_rectangle_intersect (&extents, &mask_extents); } - if (stroke_extents) - *stroke_extents = extents; - status = _add_operation (surface, &extents, backend_status); - - return status; + return _add_operation (surface, &extents, backend_status); } static cairo_int_status_t @@ -497,53 +466,49 @@ _cairo_analysis_surface_fill (void *abstract_surface, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *fill_extents) + cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; - cairo_status_t status, backend_status; + cairo_status_t backend_status; cairo_rectangle_int_t extents; cairo_bool_t is_empty; - if (!surface->target->backend->fill) + if (surface->target->backend->fill == NULL) { backend_status = CAIRO_INT_STATUS_UNSUPPORTED; - else - backend_status = (*surface->target->backend->fill) (surface->target, op, - source, path, fill_rule, - tolerance, antialias, NULL); - - if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN) - backend_status = _analyze_meta_surface_pattern (surface, source); - - status = _cairo_surface_get_extents (&surface->base, &extents); - if (_cairo_status_is_error (status)) - return status; - - if (_cairo_operator_bounded_by_source (op)) { - cairo_rectangle_int_t source_extents; - - status = _cairo_pattern_get_extents (source, &source_extents); - if (unlikely (status)) - return status; - - is_empty = _cairo_rectangle_intersect (&extents, &source_extents); + } else { + backend_status = + surface->target->backend->fill (surface->target, op, + source, path, fill_rule, + tolerance, antialias, + clip); + if (_cairo_status_is_error (backend_status)) + return backend_status; } - is_empty = _cairo_rectangle_intersect (&extents, &surface->current_clip); + if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) + backend_status = _analyze_recording_surface_pattern (surface, source); + + _cairo_analysis_surface_operation_extents (surface, + op, source, clip, + &extents); if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t mask_extents; - _cairo_path_fixed_approximate_fill_extents (path, - &mask_extents); - + /* We want speed for the likely case where the operation can be + * performed natively, but accuracy if we have to resort to + * using images. + */ + if (backend_status == CAIRO_STATUS_SUCCESS) { + _cairo_path_fixed_approximate_fill_extents (path, &mask_extents); + } else { + _cairo_path_fixed_fill_extents (path, fill_rule, tolerance, + &mask_extents); + } is_empty = _cairo_rectangle_intersect (&extents, &mask_extents); } - if (fill_extents) - *fill_extents = extents; - status = _add_operation (surface, &extents, backend_status); - - return status; + return _add_operation (surface, &extents, backend_status); } static cairo_int_status_t @@ -553,8 +518,8 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs, - cairo_rectangle_int_t *show_glyphs_extents) + cairo_clip_t *clip, + int *remaining_glyphs) { cairo_analysis_surface_t *surface = abstract_surface; cairo_status_t status, backend_status; @@ -562,58 +527,56 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface, cairo_bool_t is_empty; /* Adapted from _cairo_surface_show_glyphs */ - if (surface->target->backend->show_glyphs) - backend_status = (*surface->target->backend->show_glyphs) (surface->target, op, - source, - glyphs, num_glyphs, - scaled_font, - remaining_glyphs, NULL); - else if (surface->target->backend->show_text_glyphs) - backend_status = surface->target->backend->show_text_glyphs (surface->target, op, - source, - NULL, 0, - glyphs, num_glyphs, - NULL, 0, - FALSE, - scaled_font, NULL); + if (surface->target->backend->show_glyphs != NULL) { + backend_status = + surface->target->backend->show_glyphs (surface->target, op, + source, + glyphs, num_glyphs, + scaled_font, + clip, + remaining_glyphs); + if (_cairo_status_is_error (backend_status)) + return backend_status; + } + else if (surface->target->backend->show_text_glyphs != NULL) + { + backend_status = + surface->target->backend->show_text_glyphs (surface->target, op, + source, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, + FALSE, + scaled_font, + clip); + if (_cairo_status_is_error (backend_status)) + return backend_status; + } else + { backend_status = CAIRO_INT_STATUS_UNSUPPORTED; - - if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN) - backend_status = _analyze_meta_surface_pattern (surface, source); - - status = _cairo_surface_get_extents (&surface->base, &extents); - if (_cairo_status_is_error (status)) - return status; - - if (_cairo_operator_bounded_by_source (op)) { - cairo_rectangle_int_t source_extents; - - status = _cairo_pattern_get_extents (source, &source_extents); - if (unlikely (status)) - return status; - - is_empty = _cairo_rectangle_intersect (&extents, &source_extents); } - is_empty = _cairo_rectangle_intersect (&extents, &surface->current_clip); + if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) + backend_status = _analyze_recording_surface_pattern (surface, source); + + _cairo_analysis_surface_operation_extents (surface, + op, source, clip, + &extents); if (_cairo_operator_bounded_by_mask (op)) { status = _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs, - &glyph_extents); + &glyph_extents, + NULL); if (unlikely (status)) return status; is_empty = _cairo_rectangle_intersect (&extents, &glyph_extents); } - if (show_glyphs_extents) - *show_glyphs_extents = extents; - status = _add_operation (surface, &extents, backend_status); - - return status; + return _add_operation (surface, &extents, backend_status); } static cairo_bool_t @@ -636,7 +599,7 @@ _cairo_analysis_surface_show_text_glyphs (void *abstract_surface, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, - cairo_rectangle_int_t *show_text_glyphs_extents) + cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; cairo_status_t status, backend_status; @@ -645,61 +608,59 @@ _cairo_analysis_surface_show_text_glyphs (void *abstract_surface, /* Adapted from _cairo_surface_show_glyphs */ backend_status = CAIRO_INT_STATUS_UNSUPPORTED; - if (surface->target->backend->show_text_glyphs) - backend_status = surface->target->backend->show_text_glyphs (surface->target, op, - source, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, cluster_flags, - scaled_font, NULL); - if (backend_status == CAIRO_INT_STATUS_UNSUPPORTED && surface->target->backend->show_glyphs) { + if (surface->target->backend->show_text_glyphs != NULL) { + backend_status = + surface->target->backend->show_text_glyphs (surface->target, op, + source, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + cluster_flags, + scaled_font, + clip); + if (_cairo_status_is_error (backend_status)) + return backend_status; + } + if (backend_status == CAIRO_INT_STATUS_UNSUPPORTED && + surface->target->backend->show_glyphs != NULL) + { int remaining_glyphs = num_glyphs; - backend_status = surface->target->backend->show_glyphs (surface->target, op, - source, - glyphs, num_glyphs, - scaled_font, - &remaining_glyphs, NULL); + backend_status = + surface->target->backend->show_glyphs (surface->target, op, + source, + glyphs, num_glyphs, + scaled_font, + clip, + &remaining_glyphs); + if (_cairo_status_is_error (backend_status)) + return backend_status; + glyphs += num_glyphs - remaining_glyphs; num_glyphs = remaining_glyphs; if (remaining_glyphs == 0) backend_status = CAIRO_STATUS_SUCCESS; } - if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN) - backend_status = _analyze_meta_surface_pattern (surface, source); + if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) + backend_status = _analyze_recording_surface_pattern (surface, source); - status = _cairo_surface_get_extents (&surface->base, &extents); - if (_cairo_status_is_error (status)) - return status; - - if (_cairo_operator_bounded_by_source (op)) { - cairo_rectangle_int_t source_extents; - - status = _cairo_pattern_get_extents (source, &source_extents); - if (unlikely (status)) - return status; - - is_empty = _cairo_rectangle_intersect (&extents, &source_extents); - } - - is_empty = _cairo_rectangle_intersect (&extents, &surface->current_clip); + _cairo_analysis_surface_operation_extents (surface, + op, source, clip, + &extents); if (_cairo_operator_bounded_by_mask (op)) { status = _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs, - &glyph_extents); + &glyph_extents, + NULL); if (unlikely (status)) return status; is_empty = _cairo_rectangle_intersect (&extents, &glyph_extents); } - if (show_text_glyphs_extents) - *show_text_glyphs_extents = extents; - status = _add_operation (surface, &extents, backend_status); - - return status; + return _add_operation (surface, &extents, backend_status); } static const cairo_surface_backend_t cairo_analysis_surface_backend = { @@ -718,8 +679,6 @@ static const cairo_surface_backend_t cairo_analysis_surface_backend = { NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ - NULL, /* set_clip_region */ - _cairo_analysis_surface_intersect_clip_path, _cairo_analysis_surface_get_extents, NULL, /* old_show_glyphs */ NULL, /* get_font_options */ @@ -734,7 +693,6 @@ static const cairo_surface_backend_t cairo_analysis_surface_backend = { _cairo_analysis_surface_show_glyphs, NULL, /* snapshot */ NULL, /* is_similar */ - NULL, /* reset */ NULL, /* fill_stroke */ NULL, /* create_solid_pattern_surface */ NULL, /* can_repaint_solid_pattern_surface */ @@ -743,9 +701,7 @@ static const cairo_surface_backend_t cairo_analysis_surface_backend = { }; cairo_surface_t * -_cairo_analysis_surface_create (cairo_surface_t *target, - int width, - int height) +_cairo_analysis_surface_create (cairo_surface_t *target) { cairo_analysis_surface_t *surface; cairo_status_t status; @@ -763,8 +719,6 @@ _cairo_analysis_surface_create (cairo_surface_t *target, _cairo_surface_init (&surface->base, &cairo_analysis_surface_backend, CAIRO_CONTENT_COLOR_ALPHA); - surface->width = width; - surface->height = height; cairo_matrix_init_identity (&surface->ctm); surface->has_ctm = FALSE; @@ -781,24 +735,12 @@ _cairo_analysis_surface_create (cairo_surface_t *target, surface->page_bbox.p2.x = 0; surface->page_bbox.p2.y = 0; - if (width == -1 && height == -1) { - surface->current_clip.x = CAIRO_RECT_INT_MIN; - surface->current_clip.y = CAIRO_RECT_INT_MIN; - surface->current_clip.width = CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN; - surface->current_clip.height = CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN; - } else { - surface->current_clip.x = 0; - surface->current_clip.y = 0; - surface->current_clip.width = width; - surface->current_clip.height = height; - } - return &surface->base; } void _cairo_analysis_surface_set_ctm (cairo_surface_t *abstract_surface, - cairo_matrix_t *ctm) + const cairo_matrix_t *ctm) { cairo_analysis_surface_t *surface; @@ -808,7 +750,7 @@ _cairo_analysis_surface_set_ctm (cairo_surface_t *abstract_surface, surface = (cairo_analysis_surface_t *) abstract_surface; surface->ctm = *ctm; - surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm); + surface->has_ctm = ! _cairo_matrix_is_identity (&surface->ctm); } void @@ -871,22 +813,18 @@ _return_success (void) } /* These typedefs are just to silence the compiler... */ -typedef cairo_int_status_t -(*_set_clip_region_func) (void *surface, - cairo_region_t *region); - typedef cairo_int_status_t (*_paint_func) (void *surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); typedef cairo_int_status_t (*_mask_func) (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); typedef cairo_int_status_t (*_stroke_func) (void *surface, @@ -898,7 +836,7 @@ typedef cairo_int_status_t cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); typedef cairo_int_status_t (*_fill_func) (void *surface, @@ -908,7 +846,7 @@ typedef cairo_int_status_t cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); typedef cairo_int_status_t (*_show_glyphs_func) (void *surface, @@ -917,8 +855,8 @@ typedef cairo_int_status_t cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip, + int *remaining_glyphs); static const cairo_surface_backend_t cairo_null_surface_backend = { CAIRO_INTERNAL_SURFACE_TYPE_NULL, @@ -937,8 +875,6 @@ static const cairo_surface_backend_t cairo_null_surface_backend = { NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ - (_set_clip_region_func) _return_success, /* set_clip_region */ - NULL, /* intersect_clip_path */ NULL, /* get_extents */ NULL, /* old_show_glyphs */ NULL, /* get_font_options */ @@ -953,7 +889,6 @@ static const cairo_surface_backend_t cairo_null_surface_backend = { (_show_glyphs_func) _return_success, /* show_glyphs */ NULL, /* snapshot */ NULL, /* is_similar */ - NULL, /* reset */ NULL, /* fill_stroke */ NULL, /* create_solid_pattern_surface */ NULL, /* can_repaint_solid_pattern_surface */ diff --git a/gfx/cairo/cairo/src/cairo-arc.c b/gfx/cairo/cairo/src/cairo-arc.c index 2b368099ecda..e99c79b2da81 100644 --- a/gfx/cairo/cairo/src/cairo-arc.c +++ b/gfx/cairo/cairo/src/cairo-arc.c @@ -116,7 +116,7 @@ _arc_segments_needed (double angle, major_axis = _cairo_matrix_transformed_circle_major_axis (ctm, radius); max_angle = _arc_max_angle_for_tolerance_normalized (tolerance / major_axis); - return (int) ceil (angle / max_angle); + return ceil (fabs (angle) / max_angle); } /* We want to draw a single spline approximating a circular arc radius diff --git a/gfx/cairo/cairo/src/cairo-atomic-private.h b/gfx/cairo/cairo/src/cairo-atomic-private.h index 10c014ceb910..8532f6212b44 100644 --- a/gfx/cairo/cairo/src/cairo-atomic-private.h +++ b/gfx/cairo/cairo/src/cairo-atomic-private.h @@ -43,6 +43,12 @@ #include "config.h" #endif +/* The autoconf on OpenBSD 4.5 produces the malformed constant name + * SIZEOF_VOID__ rather than SIZEOF_VOID_P. Work around that here. */ +#if !defined(SIZEOF_VOID_P) && defined(SIZEOF_VOID__) +# define SIZEOF_VOID_P SIZEOF_VOID__ +#endif + CAIRO_BEGIN_DECLS #if HAVE_INTEL_ATOMIC_PRIMITIVES @@ -51,6 +57,9 @@ CAIRO_BEGIN_DECLS typedef int cairo_atomic_int_t; +# define _cairo_atomic_int_get(x) (*x) +# define _cairo_atomic_int_set(x, value) ((*x) = value) + # define _cairo_atomic_int_inc(x) ((void) __sync_fetch_and_add(x, 1)) # define _cairo_atomic_int_dec_and_test(x) (__sync_fetch_and_add(x, -1) == 1) # define _cairo_atomic_int_cmpxchg(x, oldv, newv) __sync_val_compare_and_swap (x, oldv, newv) @@ -70,6 +79,35 @@ typedef long long cairo_atomic_intptr_t; #endif +#if HAVE_LIB_ATOMIC_OPS +#include + +#define HAS_ATOMIC_OPS 1 + +typedef AO_t cairo_atomic_int_t; + +# define _cairo_atomic_int_get(x) (AO_load_full (x)) +# define _cairo_atomic_int_set(x, value) (AO_store_full (x)) + +# define _cairo_atomic_int_inc(x) ((void) AO_fetch_and_add1_full(x)) +# define _cairo_atomic_int_dec_and_test(x) (AO_fetch_and_sub1_full(x) == 1) +# define _cairo_atomic_int_cmpxchg(x, oldv, newv) ((cairo_atomic_int_t) AO_compare_and_swap_full(x, oldv, newv) ? oldv : *x) + +#if SIZEOF_VOID_P==SIZEOF_INT +typedef unsigned int cairo_atomic_intptr_t; +#elif SIZEOF_VOID_P==SIZEOF_LONG +typedef unsigned long cairo_atomic_intptr_t; +#elif SIZEOF_VOID_P==SIZEOF_LONG_LONG +typedef unsigned long long cairo_atomic_intptr_t; +#else +#error No matching integer pointer type +#endif + +# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ + (void*) (cairo_atomic_intptr_t) _cairo_atomic_int_cmpxchg ((cairo_atomic_intptr_t*)(x), (cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv) + +#endif + #ifndef HAS_ATOMIC_OPS @@ -87,9 +125,6 @@ _cairo_atomic_int_cmpxchg (int *x, int oldv, int newv); cairo_private void * _cairo_atomic_ptr_cmpxchg (void **x, void *oldv, void *newv); -#endif - - #ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER # include "cairo-compiler-private.h" @@ -107,14 +142,16 @@ _cairo_atomic_int_set (int *x, int value); #endif +#endif + #define _cairo_atomic_uint_get(x) _cairo_atomic_int_get(x) #define _cairo_atomic_uint_cmpxchg(x, oldv, newv) \ - _cairo_atomic_int_cmpxchg((int *)x, oldv, newv) + _cairo_atomic_int_cmpxchg((cairo_atomic_int_t *)x, oldv, newv) #define _cairo_status_set_error(status, err) do { \ /* hide compiler warnings about cairo_status_t != int (gcc treats its as \ * an unsigned integer instead, and about ignoring the return value. */ \ - int ret__ = _cairo_atomic_int_cmpxchg ((int *) status, CAIRO_STATUS_SUCCESS, err); \ + int ret__ = _cairo_atomic_int_cmpxchg ((cairo_atomic_int_t *) status, CAIRO_STATUS_SUCCESS, err); \ (void) ret__; \ } while (0) diff --git a/gfx/cairo/cairo/src/cairo-base64-stream.c b/gfx/cairo/cairo/src/cairo-base64-stream.c new file mode 100644 index 000000000000..2b211ff0ce37 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-base64-stream.c @@ -0,0 +1,143 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005-2007 Emmanuel Pacaud + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Author(s): + * Kristian Høgsberg + * Chris Wilson + */ + +#include "cairoint.h" +#include "cairo-output-stream-private.h" + +typedef struct _cairo_base64_stream { + cairo_output_stream_t base; + cairo_output_stream_t *output; + unsigned int in_mem; + unsigned int trailing; + unsigned char src[3]; +} cairo_base64_stream_t; + +static char const base64_table[64] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static cairo_status_t +_cairo_base64_stream_write (cairo_output_stream_t *base, + const unsigned char *data, + unsigned int length) +{ + cairo_base64_stream_t * stream = (cairo_base64_stream_t *) base; + unsigned char *src = stream->src; + unsigned int i; + + if (stream->in_mem + length < 3) { + for (i = 0; i < length; i++) { + src[i + stream->in_mem] = *data++; + } + stream->in_mem += length; + return CAIRO_STATUS_SUCCESS; + } + + do { + unsigned char dst[4]; + + for (i = stream->in_mem; i < 3; i++) { + src[i] = *data++; + length--; + } + stream->in_mem = 0; + + dst[0] = base64_table[src[0] >> 2]; + dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4]; + dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6]; + dst[3] = base64_table[src[2] & 0xfc >> 2]; + /* Special case for the last missing bits */ + switch (stream->trailing) { + case 2: + dst[2] = '='; + case 1: + dst[3] = '='; + default: + break; + } + _cairo_output_stream_write (stream->output, dst, 4); + } while (length >= 3); + + for (i = 0; i < length; i++) { + src[i] = *data++; + } + stream->in_mem = length; + + return _cairo_output_stream_get_status (stream->output); +} + +static cairo_status_t +_cairo_base64_stream_close (cairo_output_stream_t *base) +{ + cairo_base64_stream_t *stream = (cairo_base64_stream_t *) base; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + if (stream->in_mem > 0) { + memset (stream->src + stream->in_mem, 0, 3 - stream->in_mem); + stream->trailing = 3 - stream->in_mem; + stream->in_mem = 3; + status = _cairo_base64_stream_write (base, NULL, 0); + } + + return status; +} + +cairo_output_stream_t * +_cairo_base64_stream_create (cairo_output_stream_t *output) +{ + cairo_base64_stream_t *stream; + + if (output->status) + return _cairo_output_stream_create_in_error (output->status); + + stream = malloc (sizeof (cairo_base64_stream_t)); + if (unlikely (stream == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_output_stream_t *) &_cairo_output_stream_nil; + } + + _cairo_output_stream_init (&stream->base, + _cairo_base64_stream_write, + NULL, + _cairo_base64_stream_close); + + stream->output = output; + stream->in_mem = 0; + stream->trailing = 0; + + return &stream->base; +} diff --git a/gfx/cairo/cairo/src/cairo-base85-stream.c b/gfx/cairo/cairo/src/cairo-base85-stream.c index 9d42ef48f051..791e80134032 100644 --- a/gfx/cairo/cairo/src/cairo-base85-stream.c +++ b/gfx/cairo/cairo/src/cairo-base85-stream.c @@ -102,9 +102,6 @@ _cairo_base85_stream_close (cairo_output_stream_t *base) _cairo_output_stream_write (stream->output, five_tuple, stream->pending + 1); } - /* Mark end of base85 data */ - _cairo_output_stream_printf (stream->output, "~>"); - return _cairo_output_stream_get_status (stream->output); } diff --git a/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c b/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c new file mode 100644 index 000000000000..9887b8242f29 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c @@ -0,0 +1,739 @@ +/* + * Copyright © 2004 Carl Worth + * Copyright © 2006 Red Hat, Inc. + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Carl Worth + * + * Contributor(s): + * Carl D. Worth + * Chris Wilson + */ + +/* Provide definitions for standalone compilation */ +#include "cairoint.h" + +#include "cairo-combsort-private.h" +#include "cairo-list-private.h" + +typedef struct _cairo_bo_rectangle cairo_bo_rectangle_t; +typedef struct _cairo_bo_edge cairo_bo_edge_t; + +/* A deferred trapezoid of an edge */ +typedef struct _cairo_bo_trap { + cairo_bo_edge_t *right; + int32_t top; +} cairo_bo_trap_t; + +struct _cairo_bo_edge { + int x; + int dir; + cairo_bo_trap_t deferred_trap; + cairo_list_t link; +}; + +struct _cairo_bo_rectangle { + cairo_bo_edge_t left, right; + int top, bottom; +}; + +/* the parent is always given by index/2 */ +#define PQ_PARENT_INDEX(i) ((i) >> 1) +#define PQ_FIRST_ENTRY 1 + +/* left and right children are index * 2 and (index * 2) +1 respectively */ +#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) + +typedef struct _pqueue { + int size, max_size; + + cairo_bo_rectangle_t **elements; + cairo_bo_rectangle_t *elements_embedded[1024]; +} pqueue_t; + +typedef struct _cairo_bo_sweep_line { + cairo_bo_rectangle_t **rectangles; + pqueue_t stop; + cairo_list_t sweep; + cairo_list_t *current_left, *current_right; + int32_t current_y; + int32_t last_y; +} cairo_bo_sweep_line_t; + +#define DEBUG_TRAPS 0 + +#if DEBUG_TRAPS +static void +dump_traps (cairo_traps_t *traps, const char *filename) +{ + FILE *file; + int n; + + if (getenv ("CAIRO_DEBUG_TRAPS") == NULL) + return; + + file = fopen (filename, "a"); + if (file != NULL) { + for (n = 0; n < traps->num_traps; n++) { + fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n", + traps->traps[n].top, + traps->traps[n].bottom, + traps->traps[n].left.p1.x, + traps->traps[n].left.p1.y, + traps->traps[n].left.p2.x, + traps->traps[n].left.p2.y, + traps->traps[n].right.p1.x, + traps->traps[n].right.p1.y, + traps->traps[n].right.p2.x, + traps->traps[n].right.p2.y); + } + fprintf (file, "\n"); + fclose (file); + } +} +#else +#define dump_traps(traps, filename) +#endif + +static inline int +cairo_bo_rectangle_compare_start (const cairo_bo_rectangle_t *a, + const cairo_bo_rectangle_t *b) +{ + return a->top - b->top; +} + +static inline int +_cairo_bo_rectangle_compare_stop (const cairo_bo_rectangle_t *a, + const cairo_bo_rectangle_t *b) +{ + return a->bottom - b->bottom; +} + +static inline void +_pqueue_init (pqueue_t *pq) +{ + pq->max_size = ARRAY_LENGTH (pq->elements_embedded); + pq->size = 0; + + pq->elements = pq->elements_embedded; + pq->elements[PQ_FIRST_ENTRY] = NULL; +} + +static inline void +_pqueue_fini (pqueue_t *pq) +{ + if (pq->elements != pq->elements_embedded) + free (pq->elements); +} + +static cairo_status_t +_pqueue_grow (pqueue_t *pq) +{ + cairo_bo_rectangle_t **new_elements; + pq->max_size *= 2; + + if (pq->elements == pq->elements_embedded) { + new_elements = _cairo_malloc_ab (pq->max_size, + sizeof (cairo_bo_rectangle_t *)); + if (unlikely (new_elements == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (new_elements, pq->elements_embedded, + sizeof (pq->elements_embedded)); + } else { + new_elements = _cairo_realloc_ab (pq->elements, + pq->max_size, + sizeof (cairo_bo_rectangle_t *)); + if (unlikely (new_elements == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pq->elements = new_elements; + return CAIRO_STATUS_SUCCESS; +} + +static inline cairo_status_t +_pqueue_push (pqueue_t *pq, cairo_bo_rectangle_t *rectangle) +{ + cairo_bo_rectangle_t **elements; + int i, parent; + + if (unlikely (pq->size + 1 == pq->max_size)) { + cairo_status_t status; + + status = _pqueue_grow (pq); + if (unlikely (status)) + return status; + } + + elements = pq->elements; + + for (i = ++pq->size; + i != PQ_FIRST_ENTRY && + _cairo_bo_rectangle_compare_stop (rectangle, + elements[parent = PQ_PARENT_INDEX (i)]) < 0; + i = parent) + { + elements[i] = elements[parent]; + } + + elements[i] = rectangle; + + return CAIRO_STATUS_SUCCESS; +} + +static inline void +_pqueue_pop (pqueue_t *pq) +{ + cairo_bo_rectangle_t **elements = pq->elements; + cairo_bo_rectangle_t *tail; + int child, i; + + tail = elements[pq->size--]; + if (pq->size == 0) { + elements[PQ_FIRST_ENTRY] = NULL; + return; + } + + for (i = PQ_FIRST_ENTRY; + (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; + i = child) + { + if (child != pq->size && + _cairo_bo_rectangle_compare_stop (elements[child+1], + elements[child]) < 0) + { + child++; + } + + if (_cairo_bo_rectangle_compare_stop (elements[child], tail) >= 0) + break; + + elements[i] = elements[child]; + } + elements[i] = tail; +} + +static inline cairo_bo_rectangle_t * +_cairo_bo_rectangle_pop_start (cairo_bo_sweep_line_t *sweep_line) +{ + return *sweep_line->rectangles++; +} + +static inline cairo_bo_rectangle_t * +_cairo_bo_rectangle_peek_stop (cairo_bo_sweep_line_t *sweep_line) +{ + return sweep_line->stop.elements[PQ_FIRST_ENTRY]; +} + +CAIRO_COMBSORT_DECLARE (_cairo_bo_rectangle_sort, + cairo_bo_rectangle_t *, + cairo_bo_rectangle_compare_start) + +static void +_cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line, + cairo_bo_rectangle_t **rectangles, + int num_rectangles) +{ + _cairo_bo_rectangle_sort (rectangles, num_rectangles); + rectangles[num_rectangles] = NULL; + sweep_line->rectangles = rectangles; + + cairo_list_init (&sweep_line->sweep); + sweep_line->current_left = &sweep_line->sweep; + sweep_line->current_right = &sweep_line->sweep; + sweep_line->current_y = INT32_MIN; + sweep_line->last_y = INT32_MIN; + + _pqueue_init (&sweep_line->stop); +} + +static void +_cairo_bo_sweep_line_fini (cairo_bo_sweep_line_t *sweep_line) +{ + _pqueue_fini (&sweep_line->stop); +} + +static inline cairo_bo_edge_t * +link_to_edge (cairo_list_t *elt) +{ + return cairo_container_of (elt, cairo_bo_edge_t, link); +} + +static cairo_status_t +_cairo_bo_edge_end_trap (cairo_bo_edge_t *left, + int32_t bot, + cairo_traps_t *traps) +{ + cairo_bo_trap_t *trap = &left->deferred_trap; + + /* Only emit (trivial) non-degenerate trapezoids with positive height. */ + if (likely (trap->top < bot)) { + cairo_line_t _left = { + { left->x, trap->top }, + { left->x, bot }, + }, _right = { + { trap->right->x, trap->top }, + { trap->right->x, bot }, + }; + _cairo_traps_add_trap (traps, trap->top, bot, &_left, &_right); + } + + trap->right = NULL; + + return _cairo_traps_status (traps); +} + +/* Start a new trapezoid at the given top y coordinate, whose edges + * are `edge' and `edge->next'. If `edge' already has a trapezoid, + * then either add it to the traps in `traps', if the trapezoid's + * right edge differs from `edge->next', or do nothing if the new + * trapezoid would be a continuation of the existing one. */ +static inline cairo_status_t +_cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left, + cairo_bo_edge_t *right, + int top, + cairo_traps_t *traps) +{ + cairo_status_t status; + + if (left->deferred_trap.right == right) + return CAIRO_STATUS_SUCCESS; + + if (left->deferred_trap.right != NULL) { + if (right != NULL && left->deferred_trap.right->x == right->x) { + /* continuation on right, so just swap edges */ + left->deferred_trap.right = right; + return CAIRO_STATUS_SUCCESS; + } + + status = _cairo_bo_edge_end_trap (left, top, traps); + if (unlikely (status)) + return status; + } + + if (right != NULL && left->x != right->x) { + left->deferred_trap.top = top; + left->deferred_trap.right = right; + } + + return CAIRO_STATUS_SUCCESS; +} + +static inline cairo_status_t +_active_edges_to_traps (cairo_bo_sweep_line_t *sweep, + cairo_fill_rule_t fill_rule, + cairo_traps_t *traps) +{ + int top = sweep->current_y; + cairo_list_t *pos = &sweep->sweep; + cairo_status_t status; + + if (sweep->last_y == sweep->current_y) + return CAIRO_STATUS_SUCCESS; + + if (fill_rule == CAIRO_FILL_RULE_WINDING) { + do { + cairo_bo_edge_t *left, *right; + int in_out; + + pos = pos->next; + if (pos == &sweep->sweep) + break; + + left = link_to_edge (pos); + in_out = left->dir; + + /* Check if there is a co-linear edge with an existing trap */ + if (left->deferred_trap.right == NULL) { + right = link_to_edge (pos->next); + while (unlikely (right->x == left->x)) { + if (right->deferred_trap.right != NULL) { + /* continuation on left */ + left->deferred_trap = right->deferred_trap; + right->deferred_trap.right = NULL; + break; + } + + if (right->link.next == &sweep->sweep) + break; + right = link_to_edge (right->link.next); + } + } + + /* Greedily search for the closing edge, so that we generate the + * maximal span width with the minimal number of trapezoids. + */ + + right = link_to_edge (left->link.next); + do { + /* End all subsumed traps */ + if (right->deferred_trap.right != NULL) { + status = _cairo_bo_edge_end_trap (right, top, traps); + if (unlikely (status)) + return status; + } + + in_out += right->dir; + if (in_out == 0) { + /* skip co-linear edges */ + if (likely (right->link.next == &sweep->sweep || + right->x != link_to_edge (right->link.next)->x)) + { + break; + } + } + + right = link_to_edge (right->link.next); + } while (TRUE); + + status = _cairo_bo_edge_start_or_continue_trap (left, right, + top, traps); + if (unlikely (status)) + return status; + + pos = &right->link; + } while (TRUE); + } else { + cairo_bo_edge_t *left, *right; + do { + int in_out = 0; + + pos = pos->next; + if (pos == &sweep->sweep) + break; + + left = link_to_edge (pos); + + pos = pos->next; + do { + right = link_to_edge (pos); + if (right->deferred_trap.right != NULL) { + status = _cairo_bo_edge_end_trap (right, top, traps); + if (unlikely (status)) + return status; + } + + if ((in_out++ & 1) == 0) { + cairo_list_t *next; + cairo_bool_t skip = FALSE; + + /* skip co-linear edges */ + next = pos->next; + if (next != &sweep->sweep) + skip = right->x == link_to_edge (next)->x; + + if (! skip) + break; + } + pos = pos->next; + } while (TRUE); + + right = pos == &sweep->sweep ? NULL : link_to_edge (pos); + status = _cairo_bo_edge_start_or_continue_trap (left, right, + top, traps); + if (unlikely (status)) + return status; + } while (right != NULL); + } + + sweep->last_y = sweep->current_y; + return CAIRO_STATUS_SUCCESS; +} + +static inline cairo_status_t +_cairo_bo_sweep_line_delete_edge (cairo_bo_sweep_line_t *sweep_line, + cairo_bo_edge_t *edge, + cairo_traps_t *traps) +{ + if (edge->deferred_trap.right != NULL) { + cairo_bo_edge_t *next = link_to_edge (edge->link.next); + if (&next->link != &sweep_line->sweep && next->x == edge->x) { + next->deferred_trap = edge->deferred_trap; + } else { + cairo_status_t status; + + status = _cairo_bo_edge_end_trap (edge, + sweep_line->current_y, + traps); + if (unlikely (status)) + return status; + } + } + + if (sweep_line->current_left == &edge->link) + sweep_line->current_left = edge->link.prev; + + if (sweep_line->current_right == &edge->link) + sweep_line->current_right = edge->link.next; + + cairo_list_del (&edge->link); + + return CAIRO_STATUS_SUCCESS; +} + +static inline cairo_status_t +_cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t *sweep_line, + cairo_bo_rectangle_t *rectangle, + cairo_fill_rule_t fill_rule, + cairo_traps_t *traps) +{ + cairo_status_t status; + + if (rectangle->bottom != sweep_line->current_y) { + status = _active_edges_to_traps (sweep_line, fill_rule, traps); + if (unlikely (status)) + return status; + + sweep_line->current_y = rectangle->bottom; + } + + status = _cairo_bo_sweep_line_delete_edge (sweep_line, + &rectangle->left, + traps); + if (unlikely (status)) + return status; + + status = _cairo_bo_sweep_line_delete_edge (sweep_line, + &rectangle->right, + traps); + if (unlikely (status)) + return status; + + _pqueue_pop (&sweep_line->stop); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +validate_sweep_line (cairo_bo_sweep_line_t *sweep_line) +{ + int32_t last_x = INT32_MIN; + cairo_bo_edge_t *edge; + cairo_list_foreach_entry (edge, cairo_bo_edge_t, &sweep_line->sweep, link) { + if (edge->x < last_x) + return FALSE; + last_x = edge->x; + } + return TRUE; +} +static inline cairo_status_t +_cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t *sweep_line, + cairo_bo_rectangle_t *rectangle, + cairo_fill_rule_t fill_rule, + cairo_traps_t *traps) +{ + cairo_list_t *pos; + cairo_status_t status; + + if (rectangle->top != sweep_line->current_y) { + cairo_bo_rectangle_t *stop; + + stop = _cairo_bo_rectangle_peek_stop (sweep_line); + while (stop != NULL && stop->bottom < rectangle->top) { + status = _cairo_bo_sweep_line_delete (sweep_line, stop, + fill_rule, traps); + if (unlikely (status)) + return status; + + stop = _cairo_bo_rectangle_peek_stop (sweep_line); + } + + status = _active_edges_to_traps (sweep_line, fill_rule, traps); + if (unlikely (status)) + return status; + + sweep_line->current_y = rectangle->top; + } + + /* right edge */ + pos = sweep_line->current_right; + if (pos == &sweep_line->sweep) + pos = sweep_line->sweep.prev; + if (pos != &sweep_line->sweep) { + int cmp; + + cmp = link_to_edge (pos)->x - rectangle->right.x; + if (cmp < 0) { + while (pos->next != &sweep_line->sweep && + link_to_edge (pos->next)->x - rectangle->right.x < 0) + { + pos = pos->next; + } + } else if (cmp > 0) { + do { + pos = pos->prev; + } while (pos != &sweep_line->sweep && + link_to_edge (pos)->x - rectangle->right.x > 0); + } + + cairo_list_add (&rectangle->right.link, pos); + } else { + cairo_list_add_tail (&rectangle->right.link, pos); + } + sweep_line->current_right = &rectangle->right.link; + assert (validate_sweep_line (sweep_line)); + + /* left edge */ + pos = sweep_line->current_left; + if (pos == &sweep_line->sweep) + pos = sweep_line->sweep.next; + if (pos != &sweep_line->sweep) { + int cmp; + + if (link_to_edge (pos)->x >= rectangle->right.x) { + pos = rectangle->right.link.prev; + if (pos == &sweep_line->sweep) + goto left_done; + } + + cmp = link_to_edge (pos)->x - rectangle->left.x; + if (cmp < 0) { + while (pos->next != &sweep_line->sweep && + link_to_edge (pos->next)->x - rectangle->left.x < 0) + { + pos = pos->next; + } + } else if (cmp > 0) { + do { + pos = pos->prev; + } while (pos != &sweep_line->sweep && + link_to_edge (pos)->x - rectangle->left.x > 0); + } + } + left_done: + cairo_list_add (&rectangle->left.link, pos); + sweep_line->current_left = &rectangle->left.link; + assert (validate_sweep_line (sweep_line)); + + return _pqueue_push (&sweep_line->stop, rectangle); +} + +static cairo_status_t +_cairo_bentley_ottmann_tessellate_rectangular (cairo_bo_rectangle_t **rectangles, + int num_rectangles, + cairo_fill_rule_t fill_rule, + cairo_traps_t *traps) +{ + cairo_bo_sweep_line_t sweep_line; + cairo_bo_rectangle_t *rectangle; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + _cairo_bo_sweep_line_init (&sweep_line, rectangles, num_rectangles); + + while ((rectangle = _cairo_bo_rectangle_pop_start (&sweep_line)) != NULL) { + status = _cairo_bo_sweep_line_insert (&sweep_line, rectangle, + fill_rule, traps); + if (unlikely (status)) + goto BAIL; + } + + while ((rectangle = _cairo_bo_rectangle_peek_stop (&sweep_line)) != NULL) { + status = _cairo_bo_sweep_line_delete (&sweep_line, rectangle, + fill_rule, traps); + if (unlikely (status)) + goto BAIL; + } + +BAIL: + _cairo_bo_sweep_line_fini (&sweep_line); + return status; +} + +cairo_status_t +_cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps, + cairo_fill_rule_t fill_rule) +{ + cairo_bo_rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_rectangle_t)]; + cairo_bo_rectangle_t *rectangles; + cairo_bo_rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 1]; + cairo_bo_rectangle_t **rectangles_ptrs; + cairo_status_t status; + int i; + + if (unlikely (traps->num_traps == 0)) + return CAIRO_STATUS_SUCCESS; + + assert (traps->is_rectangular); + + dump_traps (traps, "bo-rects-traps-in.txt"); + + rectangles = stack_rectangles; + rectangles_ptrs = stack_rectangles_ptrs; + if (traps->num_traps > ARRAY_LENGTH (stack_rectangles)) { + rectangles = _cairo_malloc_ab_plus_c (traps->num_traps, + sizeof (cairo_bo_rectangle_t) + + sizeof (cairo_bo_rectangle_t *), + sizeof (cairo_bo_rectangle_t *)); + if (unlikely (rectangles == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + rectangles_ptrs = (cairo_bo_rectangle_t **) (rectangles + traps->num_traps); + } + + for (i = 0; i < traps->num_traps; i++) { + if (traps->traps[i].left.p1.x < traps->traps[i].right.p1.x) { + rectangles[i].left.x = traps->traps[i].left.p1.x; + rectangles[i].left.dir = 1; + + rectangles[i].right.x = traps->traps[i].right.p1.x; + rectangles[i].right.dir = -1; + } else { + rectangles[i].right.x = traps->traps[i].left.p1.x; + rectangles[i].right.dir = 1; + + rectangles[i].left.x = traps->traps[i].right.p1.x; + rectangles[i].left.dir = -1; + } + + rectangles[i].left.deferred_trap.right = NULL; + cairo_list_init (&rectangles[i].left.link); + + rectangles[i].right.deferred_trap.right = NULL; + cairo_list_init (&rectangles[i].right.link); + + rectangles[i].top = traps->traps[i].top; + rectangles[i].bottom = traps->traps[i].bottom; + + rectangles_ptrs[i] = &rectangles[i]; + } + + _cairo_traps_clear (traps); + status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs, i, + fill_rule, + traps); + traps->is_rectilinear = TRUE; + traps->is_rectangular = TRUE; + + if (rectangles != stack_rectangles) + free (rectangles); + + dump_traps (traps, "bo-rects-traps-out.txt"); + + + return status; +} diff --git a/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectilinear.c b/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectilinear.c new file mode 100644 index 000000000000..c7e738b6f6a1 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectilinear.c @@ -0,0 +1,582 @@ +/* + * Copyright © 2004 Carl Worth + * Copyright © 2006 Red Hat, Inc. + * Copyright © 2008 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Carl Worth + * + * Contributor(s): + * Carl D. Worth + * Chris Wilson + */ + +/* Provide definitions for standalone compilation */ +#include "cairoint.h" + +#include "cairo-combsort-private.h" + +typedef struct _cairo_bo_edge cairo_bo_edge_t; +typedef struct _cairo_bo_trap cairo_bo_trap_t; + +/* A deferred trapezoid of an edge */ +struct _cairo_bo_trap { + cairo_bo_edge_t *right; + int32_t top; +}; + +struct _cairo_bo_edge { + cairo_edge_t edge; + cairo_bo_edge_t *prev; + cairo_bo_edge_t *next; + cairo_bo_trap_t deferred_trap; +}; + +typedef enum { + CAIRO_BO_EVENT_TYPE_START, + CAIRO_BO_EVENT_TYPE_STOP +} cairo_bo_event_type_t; + +typedef struct _cairo_bo_event { + cairo_bo_event_type_t type; + cairo_point_t point; + cairo_bo_edge_t *edge; +} cairo_bo_event_t; + +typedef struct _cairo_bo_sweep_line { + cairo_bo_event_t **events; + cairo_bo_edge_t *head; + cairo_bo_edge_t *stopped; + int32_t current_y; + cairo_bo_edge_t *current_edge; +} cairo_bo_sweep_line_t; + +static inline int +_cairo_point_compare (const cairo_point_t *a, + const cairo_point_t *b) +{ + int cmp; + + cmp = a->y - b->y; + if (likely (cmp)) + return cmp; + + return a->x - b->x; +} + +static inline int +_cairo_bo_edge_compare (const cairo_bo_edge_t *a, + const cairo_bo_edge_t *b) +{ + int cmp; + + cmp = a->edge.line.p1.x - b->edge.line.p1.x; + if (likely (cmp)) + return cmp; + + return b->edge.bottom - a->edge.bottom; +} + +static inline int +cairo_bo_event_compare (const cairo_bo_event_t *a, + const cairo_bo_event_t *b) +{ + int cmp; + + cmp = _cairo_point_compare (&a->point, &b->point); + if (likely (cmp)) + return cmp; + + cmp = a->type - b->type; + if (cmp) + return cmp; + + return a - b; +} + +static inline cairo_bo_event_t * +_cairo_bo_event_dequeue (cairo_bo_sweep_line_t *sweep_line) +{ + return *sweep_line->events++; +} + +CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort, + cairo_bo_event_t *, + cairo_bo_event_compare) + +static void +_cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line, + cairo_bo_event_t **events, + int num_events) +{ + _cairo_bo_event_queue_sort (events, num_events); + events[num_events] = NULL; + sweep_line->events = events; + + sweep_line->head = NULL; + sweep_line->current_y = INT32_MIN; + sweep_line->current_edge = NULL; +} + +static void +_cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t *sweep_line, + cairo_bo_edge_t *edge) +{ + if (sweep_line->current_edge != NULL) { + cairo_bo_edge_t *prev, *next; + int cmp; + + cmp = _cairo_bo_edge_compare (sweep_line->current_edge, edge); + if (cmp < 0) { + prev = sweep_line->current_edge; + next = prev->next; + while (next != NULL && _cairo_bo_edge_compare (next, edge) < 0) + prev = next, next = prev->next; + + prev->next = edge; + edge->prev = prev; + edge->next = next; + if (next != NULL) + next->prev = edge; + } else if (cmp > 0) { + next = sweep_line->current_edge; + prev = next->prev; + while (prev != NULL && _cairo_bo_edge_compare (prev, edge) > 0) + next = prev, prev = next->prev; + + next->prev = edge; + edge->next = next; + edge->prev = prev; + if (prev != NULL) + prev->next = edge; + else + sweep_line->head = edge; + } else { + prev = sweep_line->current_edge; + edge->prev = prev; + edge->next = prev->next; + if (prev->next != NULL) + prev->next->prev = edge; + prev->next = edge; + } + } else { + sweep_line->head = edge; + } + + sweep_line->current_edge = edge; +} + +static void +_cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t *sweep_line, + cairo_bo_edge_t *edge) +{ + if (edge->prev != NULL) + edge->prev->next = edge->next; + else + sweep_line->head = edge->next; + + if (edge->next != NULL) + edge->next->prev = edge->prev; + + if (sweep_line->current_edge == edge) + sweep_line->current_edge = edge->prev ? edge->prev : edge->next; +} + +static inline cairo_bool_t +edges_collinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) +{ + return a->edge.line.p1.x == b->edge.line.p1.x; +} + +static cairo_status_t +_cairo_bo_edge_end_trap (cairo_bo_edge_t *left, + int32_t bot, + cairo_traps_t *traps) +{ + cairo_bo_trap_t *trap = &left->deferred_trap; + + /* Only emit (trivial) non-degenerate trapezoids with positive height. */ + if (likely (trap->top < bot)) { + _cairo_traps_add_trap (traps, + trap->top, bot, + &left->edge.line, &trap->right->edge.line); + } + + trap->right = NULL; + + return _cairo_traps_status (traps); +} + +/* Start a new trapezoid at the given top y coordinate, whose edges + * are `edge' and `edge->next'. If `edge' already has a trapezoid, + * then either add it to the traps in `traps', if the trapezoid's + * right edge differs from `edge->next', or do nothing if the new + * trapezoid would be a continuation of the existing one. */ +static inline cairo_status_t +_cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left, + cairo_bo_edge_t *right, + int top, + cairo_traps_t *traps) +{ + cairo_status_t status; + + if (left->deferred_trap.right == right) + return CAIRO_STATUS_SUCCESS; + + if (left->deferred_trap.right != NULL) { + if (right != NULL && edges_collinear (left->deferred_trap.right, right)) + { + /* continuation on right, so just swap edges */ + left->deferred_trap.right = right; + return CAIRO_STATUS_SUCCESS; + } + + status = _cairo_bo_edge_end_trap (left, top, traps); + if (unlikely (status)) + return status; + } + + if (right != NULL && ! edges_collinear (left, right)) { + left->deferred_trap.top = top; + left->deferred_trap.right = right; + } + + return CAIRO_STATUS_SUCCESS; +} + +static inline cairo_status_t +_active_edges_to_traps (cairo_bo_edge_t *left, + int32_t top, + cairo_fill_rule_t fill_rule, + cairo_traps_t *traps) +{ + cairo_bo_edge_t *right; + cairo_status_t status; + + if (fill_rule == CAIRO_FILL_RULE_WINDING) { + while (left != NULL) { + int in_out; + + /* Greedily search for the closing edge, so that we generate the + * maximal span width with the minimal number of trapezoids. + */ + in_out = left->edge.dir; + + /* Check if there is a co-linear edge with an existing trap */ + right = left->next; + if (left->deferred_trap.right == NULL) { + while (right != NULL && right->deferred_trap.right == NULL) + right = right->next; + + if (right != NULL && edges_collinear (left, right)) { + /* continuation on left */ + left->deferred_trap = right->deferred_trap; + right->deferred_trap.right = NULL; + } + } + + /* End all subsumed traps */ + right = left->next; + while (right != NULL) { + if (right->deferred_trap.right != NULL) { + status = _cairo_bo_edge_end_trap (right, top, traps); + if (unlikely (status)) + return status; + } + + in_out += right->edge.dir; + if (in_out == 0) { + /* skip co-linear edges */ + if (right->next == NULL || + ! edges_collinear (right, right->next)) + { + break; + } + } + + right = right->next; + } + + status = _cairo_bo_edge_start_or_continue_trap (left, right, + top, traps); + if (unlikely (status)) + return status; + + left = right; + if (left != NULL) + left = left->next; + } + } else { + while (left != NULL) { + int in_out = 0; + + right = left->next; + while (right != NULL) { + if (right->deferred_trap.right != NULL) { + status = _cairo_bo_edge_end_trap (right, top, traps); + if (unlikely (status)) + return status; + } + + if ((in_out++ & 1) == 0) { + cairo_bo_edge_t *next; + cairo_bool_t skip = FALSE; + + /* skip co-linear edges */ + next = right->next; + if (next != NULL) + skip = edges_collinear (right, next); + + if (! skip) + break; + } + + right = right->next; + } + + status = _cairo_bo_edge_start_or_continue_trap (left, right, + top, traps); + if (unlikely (status)) + return status; + + left = right; + if (left != NULL) + left = left->next; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_bentley_ottmann_tessellate_rectilinear (cairo_bo_event_t **start_events, + int num_events, + cairo_fill_rule_t fill_rule, + cairo_traps_t *traps) +{ + cairo_bo_sweep_line_t sweep_line; + cairo_bo_event_t *event; + cairo_status_t status; + + _cairo_bo_sweep_line_init (&sweep_line, start_events, num_events); + + while ((event = _cairo_bo_event_dequeue (&sweep_line))) { + if (event->point.y != sweep_line.current_y) { + status = _active_edges_to_traps (sweep_line.head, + sweep_line.current_y, + fill_rule, traps); + if (unlikely (status)) + return status; + + sweep_line.current_y = event->point.y; + } + + switch (event->type) { + case CAIRO_BO_EVENT_TYPE_START: + _cairo_bo_sweep_line_insert (&sweep_line, event->edge); + break; + + case CAIRO_BO_EVENT_TYPE_STOP: + _cairo_bo_sweep_line_delete (&sweep_line, event->edge); + + if (event->edge->deferred_trap.right != NULL) { + status = _cairo_bo_edge_end_trap (event->edge, + sweep_line.current_y, + traps); + if (unlikely (status)) + return status; + } + + break; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_bentley_ottmann_tessellate_rectilinear_polygon (cairo_traps_t *traps, + const cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule) +{ + cairo_status_t status; + cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)]; + cairo_bo_event_t *events; + cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; + cairo_bo_event_t **event_ptrs; + cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)]; + cairo_bo_edge_t *edges; + int num_events; + int i, j; + + if (unlikely (polygon->num_edges == 0)) + return CAIRO_STATUS_SUCCESS; + + num_events = 2 * polygon->num_edges; + + events = stack_events; + event_ptrs = stack_event_ptrs; + edges = stack_edges; + if (num_events > ARRAY_LENGTH (stack_events)) { + events = _cairo_malloc_ab_plus_c (num_events, + sizeof (cairo_bo_event_t) + + sizeof (cairo_bo_edge_t) + + sizeof (cairo_bo_event_t *), + sizeof (cairo_bo_event_t *)); + if (unlikely (events == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + event_ptrs = (cairo_bo_event_t **) (events + num_events); + edges = (cairo_bo_edge_t *) (event_ptrs + num_events + 1); + } + + for (i = j = 0; i < polygon->num_edges; i++) { + edges[i].edge = polygon->edges[i]; + edges[i].deferred_trap.right = NULL; + edges[i].prev = NULL; + edges[i].next = NULL; + + event_ptrs[j] = &events[j]; + events[j].type = CAIRO_BO_EVENT_TYPE_START; + events[j].point.y = polygon->edges[i].top; + events[j].point.x = polygon->edges[i].line.p1.x; + events[j].edge = &edges[i]; + j++; + + event_ptrs[j] = &events[j]; + events[j].type = CAIRO_BO_EVENT_TYPE_STOP; + events[j].point.y = polygon->edges[i].bottom; + events[j].point.x = polygon->edges[i].line.p1.x; + events[j].edge = &edges[i]; + j++; + } + + status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j, + fill_rule, traps); + if (events != stack_events) + free (events); + + traps->is_rectilinear = TRUE; + + return status; +} + +cairo_status_t +_cairo_bentley_ottmann_tessellate_rectilinear_traps (cairo_traps_t *traps, + cairo_fill_rule_t fill_rule) +{ + cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)]; + cairo_bo_event_t *events; + cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; + cairo_bo_event_t **event_ptrs; + cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)]; + cairo_bo_edge_t *edges; + cairo_status_t status; + int i, j, k; + + if (unlikely (traps->num_traps == 0)) + return CAIRO_STATUS_SUCCESS; + + assert (traps->is_rectilinear); + + i = 4 * traps->num_traps; + + events = stack_events; + event_ptrs = stack_event_ptrs; + edges = stack_edges; + if (i > ARRAY_LENGTH (stack_events)) { + events = _cairo_malloc_ab_plus_c (i, + sizeof (cairo_bo_event_t) + + sizeof (cairo_bo_edge_t) + + sizeof (cairo_bo_event_t *), + sizeof (cairo_bo_event_t *)); + if (unlikely (events == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + event_ptrs = (cairo_bo_event_t **) (events + i); + edges = (cairo_bo_edge_t *) (event_ptrs + i + 1); + } + + for (i = j = k = 0; i < traps->num_traps; i++) { + edges[k].edge.top = traps->traps[i].top; + edges[k].edge.bottom = traps->traps[i].bottom; + edges[k].edge.line = traps->traps[i].left; + edges[k].edge.dir = 1; + edges[k].deferred_trap.right = NULL; + edges[k].prev = NULL; + edges[k].next = NULL; + + event_ptrs[j] = &events[j]; + events[j].type = CAIRO_BO_EVENT_TYPE_START; + events[j].point.y = traps->traps[i].top; + events[j].point.x = traps->traps[i].left.p1.x; + events[j].edge = &edges[k]; + j++; + + event_ptrs[j] = &events[j]; + events[j].type = CAIRO_BO_EVENT_TYPE_STOP; + events[j].point.y = traps->traps[i].bottom; + events[j].point.x = traps->traps[i].left.p1.x; + events[j].edge = &edges[k]; + j++; + k++; + + edges[k].edge.top = traps->traps[i].top; + edges[k].edge.bottom = traps->traps[i].bottom; + edges[k].edge.line = traps->traps[i].right; + edges[k].edge.dir = -1; + edges[k].deferred_trap.right = NULL; + edges[k].prev = NULL; + edges[k].next = NULL; + + event_ptrs[j] = &events[j]; + events[j].type = CAIRO_BO_EVENT_TYPE_START; + events[j].point.y = traps->traps[i].top; + events[j].point.x = traps->traps[i].right.p1.x; + events[j].edge = &edges[k]; + j++; + + event_ptrs[j] = &events[j]; + events[j].type = CAIRO_BO_EVENT_TYPE_STOP; + events[j].point.y = traps->traps[i].bottom; + events[j].point.x = traps->traps[i].right.p1.x; + events[j].edge = &edges[k]; + j++; + k++; + } + + _cairo_traps_clear (traps); + status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j, + fill_rule, + traps); + traps->is_rectilinear = TRUE; + + if (events != stack_events) + free (events); + + return status; +} diff --git a/gfx/cairo/cairo/src/cairo-bentley-ottmann.c b/gfx/cairo/cairo/src/cairo-bentley-ottmann.c index 1d59d7038628..cb508f518fe4 100644 --- a/gfx/cairo/cairo/src/cairo-bentley-ottmann.c +++ b/gfx/cairo/cairo/src/cairo-bentley-ottmann.c @@ -1,6 +1,7 @@ /* * Copyright © 2004 Carl Worth * Copyright © 2006 Red Hat, Inc. + * Copyright © 2008 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -31,25 +32,21 @@ * * Contributor(s): * Carl D. Worth + * Chris Wilson */ /* Provide definitions for standalone compilation */ #include "cairoint.h" -#include "cairo-skiplist-private.h" #include "cairo-freelist-private.h" #include "cairo-combsort-private.h" -#define DEBUG_VALIDATE 0 #define DEBUG_PRINT_STATE 0 +#define DEBUG_EVENTS 0 +#define DEBUG_TRAPS 0 typedef cairo_point_t cairo_bo_point32_t; -typedef struct _cairo_bo_point128 { - cairo_int128_t x; - cairo_int128_t y; -} cairo_bo_point128_t; - typedef struct _cairo_bo_intersect_ordinate { int32_t ordinate; enum { EXACT, INEXACT } exactness; @@ -61,91 +58,174 @@ typedef struct _cairo_bo_intersect_point { } cairo_bo_intersect_point_t; typedef struct _cairo_bo_edge cairo_bo_edge_t; -typedef struct _sweep_line_elt sweep_line_elt_t; typedef struct _cairo_bo_trap cairo_bo_trap_t; -typedef struct _cairo_bo_traps cairo_bo_traps_t; -/* A deferred trapezoid of an edge. */ +/* A deferred trapezoid of an edge */ struct _cairo_bo_trap { cairo_bo_edge_t *right; int32_t top; }; -struct _cairo_bo_traps { - cairo_traps_t *traps; - cairo_freelist_t freelist; - - /* These form the closed bounding box of the original input - * points. */ - cairo_fixed_t xmin; - cairo_fixed_t ymin; - cairo_fixed_t xmax; - cairo_fixed_t ymax; -}; - struct _cairo_bo_edge { - cairo_bo_point32_t top; - cairo_bo_point32_t middle; - cairo_bo_point32_t bottom; - int dir; + cairo_edge_t edge; cairo_bo_edge_t *prev; cairo_bo_edge_t *next; - cairo_bo_trap_t *deferred_trap; - sweep_line_elt_t *sweep_line_elt; + cairo_bo_trap_t deferred_trap; }; -struct _sweep_line_elt { - cairo_bo_edge_t *edge; - skip_elt_t elt; -}; +/* the parent is always given by index/2 */ +#define PQ_PARENT_INDEX(i) ((i) >> 1) +#define PQ_FIRST_ENTRY 1 -#define SKIP_ELT_TO_EDGE_ELT(elt) SKIP_LIST_ELT_TO_DATA (sweep_line_elt_t, (elt)) -#define SKIP_ELT_TO_EDGE(elt) (SKIP_ELT_TO_EDGE_ELT (elt)->edge) +/* left and right children are index * 2 and (index * 2) +1 respectively */ +#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) typedef enum { - CAIRO_BO_STATUS_INTERSECTION, - CAIRO_BO_STATUS_PARALLEL, - CAIRO_BO_STATUS_NO_INTERSECTION -} cairo_bo_status_t; - -typedef enum { - CAIRO_BO_EVENT_TYPE_START, CAIRO_BO_EVENT_TYPE_STOP, - CAIRO_BO_EVENT_TYPE_INTERSECTION + CAIRO_BO_EVENT_TYPE_INTERSECTION, + CAIRO_BO_EVENT_TYPE_START } cairo_bo_event_type_t; typedef struct _cairo_bo_event { cairo_bo_event_type_t type; - cairo_bo_edge_t *e1; - cairo_bo_edge_t *e2; - cairo_bo_point32_t point; - skip_elt_t elt; + cairo_point_t point; } cairo_bo_event_t; -#define SKIP_ELT_TO_EVENT(elt) SKIP_LIST_ELT_TO_DATA (cairo_bo_event_t, (elt)) +typedef struct _cairo_bo_start_event { + cairo_bo_event_type_t type; + cairo_point_t point; + cairo_bo_edge_t edge; +} cairo_bo_start_event_t; + +typedef struct _cairo_bo_queue_event { + cairo_bo_event_type_t type; + cairo_point_t point; + cairo_bo_edge_t *e1; + cairo_bo_edge_t *e2; +} cairo_bo_queue_event_t; + +typedef struct _pqueue { + int size, max_size; + + cairo_bo_event_t **elements; + cairo_bo_event_t *elements_embedded[1024]; +} pqueue_t; typedef struct _cairo_bo_event_queue { - cairo_skip_list_t intersection_queue; - - cairo_bo_event_t *startstop_events; - cairo_bo_event_t **sorted_startstop_event_ptrs; + cairo_freepool_t pool; + pqueue_t pqueue; + cairo_bo_event_t **start_events; } cairo_bo_event_queue_t; -/* This structure extends #cairo_skip_list_t, which must come first. */ typedef struct _cairo_bo_sweep_line { - cairo_skip_list_t active_edges; cairo_bo_edge_t *head; - cairo_bo_edge_t *tail; + cairo_bo_edge_t *stopped; int32_t current_y; + cairo_bo_edge_t *current_edge; } cairo_bo_sweep_line_t; +#if DEBUG_TRAPS +static void +dump_traps (cairo_traps_t *traps, const char *filename) +{ + FILE *file; + int n; + + if (getenv ("CAIRO_DEBUG_TRAPS") == NULL) + return; + + if (traps->has_limits) { + printf ("%s: limits=(%d, %d, %d, %d)\n", + filename, + traps->limits.p1.x, traps->limits.p1.y, + traps->limits.p2.x, traps->limits.p2.y); + } + printf ("%s: extents=(%d, %d, %d, %d)\n", + filename, + traps->extents.p1.x, traps->extents.p1.y, + traps->extents.p2.x, traps->extents.p2.y); + + file = fopen (filename, "a"); + if (file != NULL) { + for (n = 0; n < traps->num_traps; n++) { + fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n", + traps->traps[n].top, + traps->traps[n].bottom, + traps->traps[n].left.p1.x, + traps->traps[n].left.p1.y, + traps->traps[n].left.p2.x, + traps->traps[n].left.p2.y, + traps->traps[n].right.p1.x, + traps->traps[n].right.p1.y, + traps->traps[n].right.p2.x, + traps->traps[n].right.p2.y); + } + fprintf (file, "\n"); + fclose (file); + } +} + +static void +dump_edges (cairo_bo_start_event_t *events, + int num_edges, + const char *filename) +{ + FILE *file; + int n; + + if (getenv ("CAIRO_DEBUG_TRAPS") == NULL) + return; + + file = fopen (filename, "a"); + if (file != NULL) { + for (n = 0; n < num_edges; n++) { + fprintf (file, "(%d, %d), (%d, %d) %d %d %d\n", + events[n].edge.edge.line.p1.x, + events[n].edge.edge.line.p1.y, + events[n].edge.edge.line.p2.x, + events[n].edge.edge.line.p2.y, + events[n].edge.edge.top, + events[n].edge.edge.bottom, + events[n].edge.edge.dir); + } + fprintf (file, "\n"); + fclose (file); + } +} +#endif + +static cairo_fixed_t +_line_compute_intersection_x_for_y (const cairo_line_t *line, + cairo_fixed_t y) +{ + cairo_fixed_t x, dy; + + if (y == line->p1.y) + return line->p1.x; + if (y == line->p2.y) + return line->p2.x; + + x = line->p1.x; + dy = line->p2.y - line->p1.y; + if (dy != 0) { + x += _cairo_fixed_mul_div_floor (y - line->p1.y, + line->p2.x - line->p1.x, + dy); + } + + return x; +} static inline int _cairo_bo_point32_compare (cairo_bo_point32_t const *a, cairo_bo_point32_t const *b) { - int cmp = a->y - b->y; - if (cmp) return cmp; + int cmp; + + cmp = a->y - b->y; + if (cmp) + return cmp; + return a->x - b->x; } @@ -159,7 +239,7 @@ _cairo_bo_point32_compare (cairo_bo_point32_t const *a, * * which is: * - * (dx, dy) = (bottom.x - top.x, bottom.y - top.y) + * (dx, dy) = (line.p2.x - line.p1.x, line.p2.y - line.p1.y) * * We then define the slope of each edge as dx/dy, (which is the * inverse of the slope typically used in math instruction). We never @@ -183,17 +263,17 @@ _cairo_bo_point32_compare (cairo_bo_point32_t const *a, * the sense of the result will be exactly reversed for two edges that * have a common stop point. */ -static int -_slope_compare (cairo_bo_edge_t *a, - cairo_bo_edge_t *b) +static inline int +_slope_compare (const cairo_bo_edge_t *a, + const cairo_bo_edge_t *b) { /* XXX: We're assuming here that dx and dy will still fit in 32 * bits. That's not true in general as there could be overflow. We * should prevent that before the tessellation algorithm * begins. */ - int32_t adx = a->bottom.x - a->top.x; - int32_t bdx = b->bottom.x - b->top.x; + int32_t adx = a->edge.line.p2.x - a->edge.line.p1.x; + int32_t bdx = b->edge.line.p2.x - b->edge.line.p1.x; /* Since the dy's are all positive by construction we can fast * path several common cases. @@ -211,8 +291,8 @@ _slope_compare (cairo_bo_edge_t *a, /* Finally we actually need to do the general comparison. */ { - int32_t ady = a->bottom.y - a->top.y; - int32_t bdy = b->bottom.y - b->top.y; + int32_t ady = a->edge.line.p2.y - a->edge.line.p1.y; + int32_t bdy = b->edge.line.p2.y - b->edge.line.p1.y; cairo_int64_t adx_bdy = _cairo_int32x32_64_mul (adx, bdy); cairo_int64_t bdx_ady = _cairo_int32x32_64_mul (bdx, ady); @@ -270,23 +350,46 @@ edges_compare_x_for_y_general (const cairo_bo_edge_t *a, HAVE_ALL = HAVE_DX | HAVE_ADX | HAVE_BDX } have_dx_adx_bdx = HAVE_ALL; - ady = a->bottom.y - a->top.y; - adx = a->bottom.x - a->top.x; + /* don't bother solving for abscissa if the edges' bounding boxes + * can be used to order them. */ + { + int32_t amin, amax; + int32_t bmin, bmax; + if (a->edge.line.p1.x < a->edge.line.p2.x) { + amin = a->edge.line.p1.x; + amax = a->edge.line.p2.x; + } else { + amin = a->edge.line.p2.x; + amax = a->edge.line.p1.x; + } + if (b->edge.line.p1.x < b->edge.line.p2.x) { + bmin = b->edge.line.p1.x; + bmax = b->edge.line.p2.x; + } else { + bmin = b->edge.line.p2.x; + bmax = b->edge.line.p1.x; + } + if (amax < bmin) return -1; + if (amin > bmax) return +1; + } + + ady = a->edge.line.p2.y - a->edge.line.p1.y; + adx = a->edge.line.p2.x - a->edge.line.p1.x; if (adx == 0) have_dx_adx_bdx &= ~HAVE_ADX; - bdy = b->bottom.y - b->top.y; - bdx = b->bottom.x - b->top.x; + bdy = b->edge.line.p2.y - b->edge.line.p1.y; + bdx = b->edge.line.p2.x - b->edge.line.p1.x; if (bdx == 0) have_dx_adx_bdx &= ~HAVE_BDX; - dx = a->top.x - b->top.x; + dx = a->edge.line.p1.x - b->edge.line.p1.x; if (dx == 0) have_dx_adx_bdx &= ~HAVE_DX; #define L _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), dx) -#define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->top.y) -#define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->top.y) +#define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->edge.line.p1.y) +#define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->edge.line.p1.y) switch (have_dx_adx_bdx) { default: case HAVE_NONE: @@ -304,7 +407,7 @@ edges_compare_x_for_y_general (const cairo_bo_edge_t *a, /* 0 ∘ (Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */ if ((adx ^ bdx) < 0) { return adx; - } else if (a->top.y == b->top.y) { /* common origin */ + } else if (a->edge.line.p1.y == b->edge.line.p1.y) { /* common origin */ cairo_int64_t adx_bdy, bdx_ady; /* ∴ A_dx * B_dy ∘ B_dx * A_dy */ @@ -323,7 +426,7 @@ edges_compare_x_for_y_general (const cairo_bo_edge_t *a, cairo_int64_t ady_dx, dy_adx; ady_dx = _cairo_int32x32_64_mul (ady, dx); - dy_adx = _cairo_int32x32_64_mul (a->top.y - y, adx); + dy_adx = _cairo_int32x32_64_mul (a->edge.line.p1.y - y, adx); return _cairo_int64_cmp (ady_dx, dy_adx); } @@ -335,11 +438,12 @@ edges_compare_x_for_y_general (const cairo_bo_edge_t *a, cairo_int64_t bdy_dx, dy_bdx; bdy_dx = _cairo_int32x32_64_mul (bdy, dx); - dy_bdx = _cairo_int32x32_64_mul (y - b->top.y, bdx); + dy_bdx = _cairo_int32x32_64_mul (y - b->edge.line.p1.y, bdx); return _cairo_int64_cmp (bdy_dx, dy_bdx); } case HAVE_ALL: + /* XXX try comparing (a->edge.line.p2.x - b->edge.line.p2.x) et al */ return _cairo_int128_cmp (L, _cairo_int128_sub (B, A)); } #undef B @@ -377,16 +481,21 @@ edge_compare_for_y_against_x (const cairo_bo_edge_t *a, int32_t dx, dy; cairo_int64_t L, R; - adx = a->bottom.x - a->top.x; - dx = x - a->top.x; + if (x < a->edge.line.p1.x && x < a->edge.line.p2.x) + return 1; + if (x > a->edge.line.p1.x && x > a->edge.line.p2.x) + return -1; + + adx = a->edge.line.p2.x - a->edge.line.p1.x; + dx = x - a->edge.line.p1.x; if (adx == 0) return -dx; - if ((adx ^ dx) < 0) + if (dx == 0 || (adx ^ dx) < 0) return adx; - dy = y - a->top.y; - ady = a->bottom.y - a->top.y; + dy = y - a->edge.line.p1.y; + ady = a->edge.line.p2.y - a->edge.line.p1.y; L = _cairo_int32x32_64_mul (dy, adx); R = _cairo_int32x32_64_mul (dx, ady); @@ -412,17 +521,17 @@ edges_compare_x_for_y (const cairo_bo_edge_t *a, } have_ax_bx = HAVE_BOTH; int32_t ax, bx; - if (y == a->top.y) - ax = a->top.x; - else if (y == a->bottom.y) - ax = a->bottom.x; + if (y == a->edge.line.p1.y) + ax = a->edge.line.p1.x; + else if (y == a->edge.line.p2.y) + ax = a->edge.line.p2.x; else have_ax_bx &= ~HAVE_AX; - if (y == b->top.y) - bx = b->top.x; - else if (y == b->bottom.y) - bx = b->bottom.x; + if (y == b->edge.line.p1.y) + bx = b->edge.line.p1.x; + else if (y == b->edge.line.p2.y) + bx = b->edge.line.p2.x; else have_ax_bx &= ~HAVE_BX; @@ -439,240 +548,56 @@ edges_compare_x_for_y (const cairo_bo_edge_t *a, } } +static inline int +_line_equal (const cairo_line_t *a, const cairo_line_t *b) +{ + return a->p1.x == b->p1.x && a->p1.y == b->p1.y && + a->p2.x == b->p2.x && a->p2.y == b->p2.y; +} + static int _cairo_bo_sweep_line_compare_edges (cairo_bo_sweep_line_t *sweep_line, - cairo_bo_edge_t *a, - cairo_bo_edge_t *b) + const cairo_bo_edge_t *a, + const cairo_bo_edge_t *b) { int cmp; - if (a == b) - return 0; + /* compare the edges if not identical */ + if (! _line_equal (&a->edge.line, &b->edge.line)) { + cmp = edges_compare_x_for_y (a, b, sweep_line->current_y); + if (cmp) + return cmp; - /* don't bother solving for abscissa if the edges' bounding boxes - * can be used to order them. */ - { - int32_t amin, amax; - int32_t bmin, bmax; - if (a->middle.x < a->bottom.x) { - amin = a->middle.x; - amax = a->bottom.x; - } else { - amin = a->bottom.x; - amax = a->middle.x; - } - if (b->middle.x < b->bottom.x) { - bmin = b->middle.x; - bmax = b->bottom.x; - } else { - bmin = b->bottom.x; - bmax = b->middle.x; - } - if (amax < bmin) return -1; - if (amin > bmax) return +1; + /* The two edges intersect exactly at y, so fall back on slope + * comparison. We know that this compare_edges function will be + * called only when starting a new edge, (not when stopping an + * edge), so we don't have to worry about conditionally inverting + * the sense of _slope_compare. */ + cmp = _slope_compare (a, b); + if (cmp) + return cmp; } - cmp = edges_compare_x_for_y (a, b, sweep_line->current_y); - if (cmp) - return cmp; - - /* The two edges intersect exactly at y, so fall back on slope - * comparison. We know that this compare_edges function will be - * called only when starting a new edge, (not when stopping an - * edge), so we don't have to worry about conditionally inverting - * the sense of _slope_compare. */ - cmp = _slope_compare (a, b); - if (cmp) - return cmp; - /* We've got two collinear edges now. */ - - /* Since we're dealing with start events, prefer comparing top - * edges before bottom edges. */ - cmp = _cairo_bo_point32_compare (&a->top, &b->top); - if (cmp) - return cmp; - - cmp = _cairo_bo_point32_compare (&a->bottom, &b->bottom); - if (cmp) - return cmp; - - /* Finally, we've got two identical edges. Let's finally - * discriminate by a simple pointer comparison, (which works only - * because we "know" the edges are all in a single array and don't - * move. */ - if (a > b) - return 1; - else - return -1; -} - -static int -_sweep_line_elt_compare (void *list, - void *a, - void *b) -{ - cairo_bo_sweep_line_t *sweep_line = list; - sweep_line_elt_t *edge_elt_a = a; - sweep_line_elt_t *edge_elt_b = b; - - return _cairo_bo_sweep_line_compare_edges (sweep_line, - edge_elt_a->edge, - edge_elt_b->edge); -} - -static inline int -cairo_bo_event_compare (cairo_bo_event_t const *a, - cairo_bo_event_t const *b) -{ - int cmp; - - /* The major motion of the sweep line is vertical (top-to-bottom), - * and the minor motion is horizontal (left-to-right), dues to the - * infinitesimal tilt rule. - * - * Our point comparison function respects these rules. - */ - cmp = _cairo_bo_point32_compare (&a->point, &b->point); - if (cmp) - return cmp; - - /* The events share a common point, so further discrimination is - * determined by the event type. Due to the infinitesimal - * shortening rule, stop events come first, then intersection - * events, then start events. - */ - if (a->type != b->type) { - if (a->type == CAIRO_BO_EVENT_TYPE_STOP) - return -1; - if (a->type == CAIRO_BO_EVENT_TYPE_START) - return 1; - - if (b->type == CAIRO_BO_EVENT_TYPE_STOP) - return 1; - if (b->type == CAIRO_BO_EVENT_TYPE_START) - return -1; - } - - /* At this stage we are looking at two events of the same type at - * the same point. The final sort key is a slope comparison. We - * need a different sense for start and stop events based on the - * shortening rule. - * - * Note: Fortunately, we get to ignore errors in the relative - * ordering of intersection events. This means we don't even have - * to look at e2 here, nor worry about which sense of the slope - * comparison test is used for intersection events. - */ - cmp = _slope_compare (a->e1, b->e1); - if (cmp) { - if (a->type == CAIRO_BO_EVENT_TYPE_START) - return cmp; - else - return - cmp; - } - - /* Next look at the opposite point. This leaves ambiguities only - * for identical edges. */ - if (a->type == CAIRO_BO_EVENT_TYPE_START) { - cmp = _cairo_bo_point32_compare (&b->e1->bottom, - &a->e1->bottom); - if (cmp) - return cmp; - } - else if (a->type == CAIRO_BO_EVENT_TYPE_STOP) { - cmp = _cairo_bo_point32_compare (&a->e1->top, - &b->e1->top); - if (cmp) - return cmp; - } - else { /* CAIRO_BO_EVENT_TYPE_INTERSECT */ - /* For two intersection events at the identical point, we - * don't care what order they sort in, but we do care that we - * have a stable sort. In particular intersections between - * different pairs of edges must never return 0. */ - cmp = _cairo_bo_point32_compare (&a->e2->top, &b->e2->top); - if (cmp) - return cmp; - cmp = _cairo_bo_point32_compare (&a->e2->bottom, &b->e2->bottom); - if (cmp) - return cmp; - cmp = _cairo_bo_point32_compare (&a->e1->top, &b->e1->top); - if (cmp) - return cmp; - cmp = _cairo_bo_point32_compare (&a->e1->bottom, &b->e1->bottom); - if (cmp) - return cmp; - } - - /* Discrimination based on the edge pointers. */ - if (a->e1 < b->e1) - return -1; - if (a->e1 > b->e1) - return +1; - if (a->e2 < b->e2) - return -1; - if (a->e2 > b->e2) - return +1; - return 0; -} - -static int -cairo_bo_event_compare_abstract (void *list, - void *a, - void *b) -{ - cairo_bo_event_t *event_a = a; - cairo_bo_event_t *event_b = b; - - return cairo_bo_event_compare (event_a, event_b); -} - -static int -cairo_bo_event_compare_pointers (const cairo_bo_event_t *a, - const cairo_bo_event_t *b) -{ - int cmp; - - if (a == b) - return 0; - cmp = cairo_bo_event_compare (a, b); - if (cmp) - return cmp; - - return a - b; + return b->edge.bottom - a->edge.bottom; } static inline cairo_int64_t -det32_64 (int32_t a, - int32_t b, - int32_t c, - int32_t d) +det32_64 (int32_t a, int32_t b, + int32_t c, int32_t d) { - cairo_int64_t ad; - cairo_int64_t bc; - /* det = a * d - b * c */ - ad = _cairo_int32x32_64_mul (a, d); - bc = _cairo_int32x32_64_mul (b, c); - - return _cairo_int64_sub (ad, bc); + return _cairo_int64_sub (_cairo_int32x32_64_mul (a, d), + _cairo_int32x32_64_mul (b, c)); } static inline cairo_int128_t -det64x32_128 (cairo_int64_t a, - int32_t b, - cairo_int64_t c, - int32_t d) +det64x32_128 (cairo_int64_t a, int32_t b, + cairo_int64_t c, int32_t d) { - cairo_int128_t ad; - cairo_int128_t bc; - /* det = a * d - b * c */ - ad = _cairo_int64x32_128_mul (a, d); - bc = _cairo_int64x32_128_mul (c, b); - - return _cairo_int128_sub (ad, bc); + return _cairo_int128_sub (_cairo_int64x32_128_mul (a, d), + _cairo_int64x32_128_mul (c, b)); } /* Compute the intersection of two lines as defined by two edges. The @@ -681,7 +606,7 @@ det64x32_128 (cairo_int64_t a, * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection or * %CAIRO_BO_STATUS_PARALLEL if the two lines are exactly parallel. */ -static cairo_bo_status_t +static cairo_bool_t intersect_lines (cairo_bo_edge_t *a, cairo_bo_edge_t *b, cairo_bo_intersect_point_t *intersection) @@ -694,19 +619,17 @@ intersect_lines (cairo_bo_edge_t *a, * What we're doing to mitigate this is to perform clamping in * cairo_bo_tessellate_polygon(). */ - int32_t dx1 = a->top.x - a->bottom.x; - int32_t dy1 = a->top.y - a->bottom.y; + int32_t dx1 = a->edge.line.p1.x - a->edge.line.p2.x; + int32_t dy1 = a->edge.line.p1.y - a->edge.line.p2.y; - int32_t dx2 = b->top.x - b->bottom.x; - int32_t dy2 = b->top.y - b->bottom.y; + int32_t dx2 = b->edge.line.p1.x - b->edge.line.p2.x; + int32_t dy2 = b->edge.line.p1.y - b->edge.line.p2.y; cairo_int64_t den_det; cairo_int64_t R; cairo_quorem64_t qr; den_det = det32_64 (dx1, dy1, dx2, dy2); - if (_cairo_int64_is_zero (den_det)) - return CAIRO_BO_STATUS_PARALLEL; /* Q: Can we determine that the lines do not intersect (within range) * much more cheaply than computing the intersection point i.e. by @@ -727,58 +650,82 @@ intersect_lines (cairo_bo_edge_t *a, * A similar substitution can be performed for s, yielding: * s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by) */ - R = det32_64 (dx2, dy2, b->top.x - a->top.x, b->top.y - a->top.y); - if (_cairo_int64_is_zero (R)) - return CAIRO_BO_STATUS_NO_INTERSECTION; - if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (R)) - return CAIRO_BO_STATUS_NO_INTERSECTION; + R = det32_64 (dx2, dy2, + b->edge.line.p1.x - a->edge.line.p1.x, + b->edge.line.p1.y - a->edge.line.p1.y); if (_cairo_int64_negative (den_det)) { if (_cairo_int64_ge (den_det, R)) - return CAIRO_BO_STATUS_NO_INTERSECTION; + return FALSE; } else { if (_cairo_int64_le (den_det, R)) - return CAIRO_BO_STATUS_NO_INTERSECTION; + return FALSE; } - R = det32_64 (dy1, dx1, a->top.y - b->top.y, a->top.x - b->top.x); - if (_cairo_int64_is_zero (R)) - return CAIRO_BO_STATUS_NO_INTERSECTION; - if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (R)) - return CAIRO_BO_STATUS_NO_INTERSECTION; + R = det32_64 (dy1, dx1, + a->edge.line.p1.y - b->edge.line.p1.y, + a->edge.line.p1.x - b->edge.line.p1.x); if (_cairo_int64_negative (den_det)) { if (_cairo_int64_ge (den_det, R)) - return CAIRO_BO_STATUS_NO_INTERSECTION; + return FALSE; } else { if (_cairo_int64_le (den_det, R)) - return CAIRO_BO_STATUS_NO_INTERSECTION; + return FALSE; } /* We now know that the two lines should intersect within range. */ - a_det = det32_64 (a->top.x, a->top.y, - a->bottom.x, a->bottom.y); - b_det = det32_64 (b->top.x, b->top.y, - b->bottom.x, b->bottom.y); + a_det = det32_64 (a->edge.line.p1.x, a->edge.line.p1.y, + a->edge.line.p2.x, a->edge.line.p2.y); + b_det = det32_64 (b->edge.line.p1.x, b->edge.line.p1.y, + b->edge.line.p2.x, b->edge.line.p2.y); /* x = det (a_det, dx1, b_det, dx2) / den_det */ qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dx1, b_det, dx2), den_det); if (_cairo_int64_eq (qr.rem, den_det)) - return CAIRO_BO_STATUS_NO_INTERSECTION; - intersection->x.ordinate = _cairo_int64_to_int32 (qr.quo); + return FALSE; +#if 0 intersection->x.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT; +#else + intersection->x.exactness = EXACT; + if (! _cairo_int64_is_zero (qr.rem)) { + if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem)) + qr.rem = _cairo_int64_negate (qr.rem); + qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2)); + if (_cairo_int64_ge (qr.rem, den_det)) { + qr.quo = _cairo_int64_add (qr.quo, + _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); + } else + intersection->x.exactness = INEXACT; + } +#endif + intersection->x.ordinate = _cairo_int64_to_int32 (qr.quo); /* y = det (a_det, dy1, b_det, dy2) / den_det */ qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dy1, b_det, dy2), den_det); if (_cairo_int64_eq (qr.rem, den_det)) - return CAIRO_BO_STATUS_NO_INTERSECTION; - intersection->y.ordinate = _cairo_int64_to_int32 (qr.quo); + return FALSE; +#if 0 intersection->y.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT; +#else + intersection->y.exactness = EXACT; + if (! _cairo_int64_is_zero (qr.rem)) { + if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem)) + qr.rem = _cairo_int64_negate (qr.rem); + qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2)); + if (_cairo_int64_ge (qr.rem, den_det)) { + qr.quo = _cairo_int64_add (qr.quo, + _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); + } else + intersection->y.exactness = INEXACT; + } +#endif + intersection->y.ordinate = _cairo_int64_to_int32 (qr.quo); - return CAIRO_BO_STATUS_INTERSECTION; + return TRUE; } static int @@ -825,8 +772,10 @@ _cairo_bo_edge_contains_intersect_point (cairo_bo_edge_t *edge, * finder which needs them. */ - cmp_top = _cairo_bo_intersect_ordinate_32_compare (point->y, edge->top.y); - cmp_bottom = _cairo_bo_intersect_ordinate_32_compare (point->y, edge->bottom.y); + cmp_top = _cairo_bo_intersect_ordinate_32_compare (point->y, + edge->edge.top); + cmp_bottom = _cairo_bo_intersect_ordinate_32_compare (point->y, + edge->edge.bottom); if (cmp_top < 0 || cmp_bottom > 0) { @@ -849,10 +798,19 @@ _cairo_bo_edge_contains_intersect_point (cairo_bo_edge_t *edge, * edge, then the x value of the point must be less to be * considered as inside. */ - if (cmp_top == 0) - return (_cairo_bo_intersect_ordinate_32_compare (point->x, edge->top.x) > 0); - else /* cmp_bottom == 0 */ - return (_cairo_bo_intersect_ordinate_32_compare (point->x, edge->bottom.x) < 0); + if (cmp_top == 0) { + cairo_fixed_t top_x; + + top_x = _line_compute_intersection_x_for_y (&edge->edge.line, + edge->edge.top); + return _cairo_bo_intersect_ordinate_32_compare (point->x, top_x) > 0; + } else { /* cmp_bottom == 0 */ + cairo_fixed_t bot_x; + + bot_x = _line_compute_intersection_x_for_y (&edge->edge.line, + edge->edge.bottom); + return _cairo_bo_intersect_ordinate_32_compare (point->x, bot_x) < 0; + } } /* Compute the intersection of two edges. The result is provided as a @@ -871,23 +829,21 @@ _cairo_bo_edge_contains_intersect_point (cairo_bo_edge_t *edge, * effectively outside (no intersection is returned). Also, if the * intersection point has the same */ -static cairo_bo_status_t +static cairo_bool_t _cairo_bo_edge_intersect (cairo_bo_edge_t *a, cairo_bo_edge_t *b, cairo_bo_point32_t *intersection) { - cairo_bo_status_t status; cairo_bo_intersect_point_t quorem; - status = intersect_lines (a, b, &quorem); - if (status) - return status; + if (! intersect_lines (a, b, &quorem)) + return FALSE; if (! _cairo_bo_edge_contains_intersect_point (a, &quorem)) - return CAIRO_BO_STATUS_NO_INTERSECTION; + return FALSE; if (! _cairo_bo_edge_contains_intersect_point (b, &quorem)) - return CAIRO_BO_STATUS_NO_INTERSECTION; + return FALSE; /* Now that we've correctly compared the intersection point and * determined that it lies within the edge, then we know that we @@ -897,210 +853,313 @@ _cairo_bo_edge_intersect (cairo_bo_edge_t *a, intersection->x = quorem.x.ordinate; intersection->y = quorem.y.ordinate; - return CAIRO_BO_STATUS_INTERSECTION; + return TRUE; } -static void -_cairo_bo_event_init (cairo_bo_event_t *event, - cairo_bo_event_type_t type, - cairo_bo_edge_t *e1, - cairo_bo_edge_t *e2, - cairo_bo_point32_t point) +static inline int +cairo_bo_event_compare (const cairo_bo_event_t *a, + const cairo_bo_event_t *b) { - event->type = type; - event->e1 = e1; - event->e2 = e2; - event->point = point; + int cmp; + + cmp = _cairo_bo_point32_compare (&a->point, &b->point); + if (cmp) + return cmp; + + cmp = a->type - b->type; + if (cmp) + return cmp; + + return a - b; +} + +static inline void +_pqueue_init (pqueue_t *pq) +{ + pq->max_size = ARRAY_LENGTH (pq->elements_embedded); + pq->size = 0; + + pq->elements = pq->elements_embedded; +} + +static inline void +_pqueue_fini (pqueue_t *pq) +{ + if (pq->elements != pq->elements_embedded) + free (pq->elements); } static cairo_status_t -_cairo_bo_event_queue_insert (cairo_bo_event_queue_t *queue, - cairo_bo_event_t *event) +_pqueue_grow (pqueue_t *pq) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; - /* Don't insert if there's already an equivalent intersection event in the queue. */ - if (_cairo_skip_list_insert (&queue->intersection_queue, event, - event->type == CAIRO_BO_EVENT_TYPE_INTERSECTION) == NULL) - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - return status; + cairo_bo_event_t **new_elements; + pq->max_size *= 2; + + if (pq->elements == pq->elements_embedded) { + new_elements = _cairo_malloc_ab (pq->max_size, + sizeof (cairo_bo_event_t *)); + if (unlikely (new_elements == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (new_elements, pq->elements_embedded, + sizeof (pq->elements_embedded)); + } else { + new_elements = _cairo_realloc_ab (pq->elements, + pq->max_size, + sizeof (cairo_bo_event_t *)); + if (unlikely (new_elements == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pq->elements = new_elements; + return CAIRO_STATUS_SUCCESS; +} + +static inline cairo_status_t +_pqueue_push (pqueue_t *pq, cairo_bo_event_t *event) +{ + cairo_bo_event_t **elements; + int i, parent; + + if (unlikely (pq->size + 1 == pq->max_size)) { + cairo_status_t status; + + status = _pqueue_grow (pq); + if (unlikely (status)) + return status; + } + + elements = pq->elements; + + for (i = ++pq->size; + i != PQ_FIRST_ENTRY && + cairo_bo_event_compare (event, + elements[parent = PQ_PARENT_INDEX (i)]) < 0; + i = parent) + { + elements[i] = elements[parent]; + } + + elements[i] = event; + + return CAIRO_STATUS_SUCCESS; +} + +static inline void +_pqueue_pop (pqueue_t *pq) +{ + cairo_bo_event_t **elements = pq->elements; + cairo_bo_event_t *tail; + int child, i; + + tail = elements[pq->size--]; + if (pq->size == 0) { + elements[PQ_FIRST_ENTRY] = NULL; + return; + } + + for (i = PQ_FIRST_ENTRY; + (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; + i = child) + { + if (child != pq->size && + cairo_bo_event_compare (elements[child+1], + elements[child]) < 0) + { + child++; + } + + if (cairo_bo_event_compare (elements[child], tail) >= 0) + break; + + elements[i] = elements[child]; + } + elements[i] = tail; +} + +static inline cairo_status_t +_cairo_bo_event_queue_insert (cairo_bo_event_queue_t *queue, + cairo_bo_event_type_t type, + cairo_bo_edge_t *e1, + cairo_bo_edge_t *e2, + const cairo_point_t *point) +{ + cairo_bo_queue_event_t *event; + + event = _cairo_freepool_alloc (&queue->pool); + if (unlikely (event == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + event->type = type; + event->e1 = e1; + event->e2 = e2; + event->point = *point; + + return _pqueue_push (&queue->pqueue, (cairo_bo_event_t *) event); } static void _cairo_bo_event_queue_delete (cairo_bo_event_queue_t *queue, cairo_bo_event_t *event) { - if (CAIRO_BO_EVENT_TYPE_INTERSECTION == event->type) - _cairo_skip_list_delete_given ( &queue->intersection_queue, &event->elt ); + _cairo_freepool_free (&queue->pool, event); } static cairo_bo_event_t * _cairo_bo_event_dequeue (cairo_bo_event_queue_t *event_queue) { - skip_elt_t *elt = event_queue->intersection_queue.chains[0]; - cairo_bo_event_t *intersection = elt ? SKIP_ELT_TO_EVENT (elt) : NULL; - cairo_bo_event_t *startstop; + cairo_bo_event_t *event, *cmp; - startstop = *event_queue->sorted_startstop_event_ptrs; - if (startstop == NULL) - return intersection; - - if (intersection == NULL || - cairo_bo_event_compare (startstop, intersection) <= 0) + event = event_queue->pqueue.elements[PQ_FIRST_ENTRY]; + cmp = *event_queue->start_events; + if (event == NULL || + (cmp != NULL && cairo_bo_event_compare (cmp, event) < 0)) { - event_queue->sorted_startstop_event_ptrs++; - return startstop; + event = cmp; + event_queue->start_events++; + } + else + { + _pqueue_pop (&event_queue->pqueue); } - return intersection; + return event; } CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort, cairo_bo_event_t *, - cairo_bo_event_compare_pointers) + cairo_bo_event_compare) + +static void +_cairo_bo_event_queue_init (cairo_bo_event_queue_t *event_queue, + cairo_bo_event_t **start_events, + int num_events) +{ + _cairo_bo_event_queue_sort (start_events, num_events); + start_events[num_events] = NULL; + + event_queue->start_events = start_events; + + _cairo_freepool_init (&event_queue->pool, + sizeof (cairo_bo_queue_event_t)); + _pqueue_init (&event_queue->pqueue); + event_queue->pqueue.elements[PQ_FIRST_ENTRY] = NULL; +} static cairo_status_t -_cairo_bo_event_queue_init (cairo_bo_event_queue_t *event_queue, - cairo_bo_edge_t *edges, - int num_edges) +_cairo_bo_event_queue_insert_stop (cairo_bo_event_queue_t *event_queue, + cairo_bo_edge_t *edge) { - int i; - cairo_bo_event_t *events, **sorted_event_ptrs; - unsigned num_events = 2*num_edges; + cairo_bo_point32_t point; - /* The skip_elt_t field of a cairo_bo_event_t isn't used for start - * or stop events, so this allocation is safe. XXX: make the - * event type a union so it doesn't always contain the skip - * elt? */ - events = _cairo_malloc_ab_plus_c (num_events, - sizeof (cairo_bo_event_t) + - sizeof (cairo_bo_event_t *), - sizeof (cairo_bo_event_t *)); - if (unlikely (events == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - sorted_event_ptrs = (cairo_bo_event_t **) (events + num_events); - event_queue->startstop_events = events; - event_queue->sorted_startstop_event_ptrs = sorted_event_ptrs; - - for (i = 0; i < num_edges; i++) { - sorted_event_ptrs[i] = &events[2*i]; - sorted_event_ptrs[i+num_edges] = &events[2*i+1]; - - /* Initialize "middle" to top */ - edges[i].middle = edges[i].top; - - _cairo_bo_event_init (&events[2*i], - CAIRO_BO_EVENT_TYPE_START, - &edges[i], NULL, - edges[i].top); - - _cairo_bo_event_init (&events[2*i+1], - CAIRO_BO_EVENT_TYPE_STOP, - &edges[i], NULL, - edges[i].bottom); - } - - _cairo_bo_event_queue_sort (sorted_event_ptrs, num_events); - event_queue->sorted_startstop_event_ptrs[num_events] = NULL; - - _cairo_skip_list_init (&event_queue->intersection_queue, - cairo_bo_event_compare_abstract, - sizeof (cairo_bo_event_t)); - - return CAIRO_STATUS_SUCCESS; + point.y = edge->edge.bottom; + point.x = _line_compute_intersection_x_for_y (&edge->edge.line, + point.y); + return _cairo_bo_event_queue_insert (event_queue, + CAIRO_BO_EVENT_TYPE_STOP, + edge, NULL, + &point); } static void _cairo_bo_event_queue_fini (cairo_bo_event_queue_t *event_queue) { - _cairo_skip_list_fini (&event_queue->intersection_queue); - if (event_queue->startstop_events) - free (event_queue->startstop_events); + _pqueue_fini (&event_queue->pqueue); + _cairo_freepool_fini (&event_queue->pool); } -static cairo_status_t +static inline cairo_status_t _cairo_bo_event_queue_insert_if_intersect_below_current_y (cairo_bo_event_queue_t *event_queue, cairo_bo_edge_t *left, - cairo_bo_edge_t *right) + cairo_bo_edge_t *right) { - cairo_bo_status_t status; cairo_bo_point32_t intersection; - cairo_bo_event_t event; - if (left == NULL || right == NULL) + if (_line_equal (&left->edge.line, &right->edge.line)) return CAIRO_STATUS_SUCCESS; /* The names "left" and "right" here are correct descriptions of * the order of the two edges within the active edge list. So if a * slope comparison also puts left less than right, then we know - * that the intersection of these two segments has oalready + * that the intersection of these two segments has already * occurred before the current sweep line position. */ - if (_slope_compare (left, right) < 0) + if (_slope_compare (left, right) <= 0) return CAIRO_STATUS_SUCCESS; - status = _cairo_bo_edge_intersect (left, right, &intersection); - if (status == CAIRO_BO_STATUS_PARALLEL || - status == CAIRO_BO_STATUS_NO_INTERSECTION) - { + if (! _cairo_bo_edge_intersect (left, right, &intersection)) return CAIRO_STATUS_SUCCESS; - } - _cairo_bo_event_init (&event, - CAIRO_BO_EVENT_TYPE_INTERSECTION, - left, right, - intersection); - - return _cairo_bo_event_queue_insert (event_queue, &event); + return _cairo_bo_event_queue_insert (event_queue, + CAIRO_BO_EVENT_TYPE_INTERSECTION, + left, right, + &intersection); } static void _cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line) { - _cairo_skip_list_init (&sweep_line->active_edges, - _sweep_line_elt_compare, - sizeof (sweep_line_elt_t)); - sweep_line->head = NULL; - sweep_line->tail = NULL; - sweep_line->current_y = 0; -} - -static void -_cairo_bo_sweep_line_fini (cairo_bo_sweep_line_t *sweep_line) -{ - _cairo_skip_list_fini (&sweep_line->active_edges); + sweep_line->stopped = NULL; + sweep_line->current_y = INT32_MIN; + sweep_line->current_edge = NULL; } static cairo_status_t _cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t *sweep_line, cairo_bo_edge_t *edge) { - skip_elt_t *next_elt; - sweep_line_elt_t *sweep_line_elt; - cairo_bo_edge_t **prev_of_next, **next_of_prev; + if (sweep_line->current_edge != NULL) { + cairo_bo_edge_t *prev, *next; + int cmp; - sweep_line_elt = _cairo_skip_list_insert (&sweep_line->active_edges, &edge, - 1 /* unique inserts*/); - if (unlikely (sweep_line_elt == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + cmp = _cairo_bo_sweep_line_compare_edges (sweep_line, + sweep_line->current_edge, + edge); + if (cmp < 0) { + prev = sweep_line->current_edge; + next = prev->next; + while (next != NULL && + _cairo_bo_sweep_line_compare_edges (sweep_line, + next, edge) < 0) + { + prev = next, next = prev->next; + } - next_elt = sweep_line_elt->elt.next[0]; - if (next_elt) - prev_of_next = & (SKIP_ELT_TO_EDGE (next_elt)->prev); - else - prev_of_next = &sweep_line->tail; + prev->next = edge; + edge->prev = prev; + edge->next = next; + if (next != NULL) + next->prev = edge; + } else if (cmp > 0) { + next = sweep_line->current_edge; + prev = next->prev; + while (prev != NULL && + _cairo_bo_sweep_line_compare_edges (sweep_line, + prev, edge) > 0) + { + next = prev, prev = next->prev; + } - if (*prev_of_next) - next_of_prev = &(*prev_of_next)->next; - else - next_of_prev = &sweep_line->head; + next->prev = edge; + edge->next = next; + edge->prev = prev; + if (prev != NULL) + prev->next = edge; + else + sweep_line->head = edge; + } else { + prev = sweep_line->current_edge; + edge->prev = prev; + edge->next = prev->next; + if (prev->next != NULL) + prev->next->prev = edge; + prev->next = edge; + } + } else { + sweep_line->head = edge; + } - edge->prev = *prev_of_next; - edge->next = *next_of_prev; - *prev_of_next = edge; - *next_of_prev = edge; - - edge->sweep_line_elt = sweep_line_elt; + sweep_line->current_edge = edge; return CAIRO_STATUS_SUCCESS; } @@ -1109,21 +1168,16 @@ static void _cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t *sweep_line, cairo_bo_edge_t *edge) { - cairo_bo_edge_t **left_next, **right_prev; + if (edge->prev != NULL) + edge->prev->next = edge->next; + else + sweep_line->head = edge->next; - _cairo_skip_list_delete_given (&sweep_line->active_edges, - &edge->sweep_line_elt->elt); + if (edge->next != NULL) + edge->next->prev = edge->prev; - left_next = &sweep_line->head; - if (edge->prev) - left_next = &edge->prev->next; - - right_prev = &sweep_line->tail; - if (edge->next) - right_prev = &edge->next->prev; - - *left_next = edge->next; - *right_prev = edge->prev; + if (sweep_line->current_edge == edge) + sweep_line->current_edge = edge->prev ? edge->prev : edge->next; } static void @@ -1131,32 +1185,13 @@ _cairo_bo_sweep_line_swap (cairo_bo_sweep_line_t *sweep_line, cairo_bo_edge_t *left, cairo_bo_edge_t *right) { - sweep_line_elt_t *left_elt, *right_elt; - cairo_bo_edge_t **before_left, **after_right; + if (left->prev != NULL) + left->prev->next = right; + else + sweep_line->head = right; - /* Within the skip list we can do the swap simply by swapping the - * pointers to the edge elements and leaving all of the skip list - * elements and pointers unchanged. */ - left_elt = left->sweep_line_elt; - right_elt = SKIP_ELT_TO_EDGE_ELT (left_elt->elt.next[0]); - - left_elt->edge = right; - right->sweep_line_elt = left_elt; - - right_elt->edge = left; - left->sweep_line_elt = right_elt; - - /* Within the doubly-linked list of edges, there's a bit more - * bookkeeping involved with the swap. */ - before_left = &sweep_line->head; - if (left->prev) - before_left = &left->prev->next; - *before_left = right; - - after_right = &sweep_line->tail; - if (right->next) - after_right = &right->next->prev; - *after_right = left; + if (right->next != NULL) + right->next->prev = left; left->next = right->next; right->next = left; @@ -1170,8 +1205,8 @@ static void _cairo_bo_edge_print (cairo_bo_edge_t *edge) { printf ("(0x%x, 0x%x)-(0x%x, 0x%x)", - edge->top.x, edge->top.y, - edge->bottom.x, edge->bottom.y); + edge->edge.line.p1.x, edge->edge.line.p1.y, + edge->edge.line.p2.x, edge->edge.line.p2.y); } static void @@ -1200,43 +1235,16 @@ _cairo_bo_event_print (cairo_bo_event_t *event) static void _cairo_bo_event_queue_print (cairo_bo_event_queue_t *event_queue) { - skip_elt_t *elt; /* XXX: fixme to print the start/stop array too. */ - cairo_skip_list_t *queue = &event_queue->intersection_queue; - cairo_bo_event_t *event; - printf ("Event queue:\n"); - - for (elt = queue->chains[0]; - elt; - elt = elt->next[0]) - { - event = SKIP_ELT_TO_EVENT (elt); - _cairo_bo_event_print (event); - } } static void _cairo_bo_sweep_line_print (cairo_bo_sweep_line_t *sweep_line) { cairo_bool_t first = TRUE; - skip_elt_t *elt; cairo_bo_edge_t *edge; - printf ("Sweep line (reversed): "); - - for (edge = sweep_line->tail; - edge; - edge = edge->prev) - { - if (!first) - printf (", "); - _cairo_bo_edge_print (edge); - first = FALSE; - } - printf ("\n"); - - printf ("Sweep line from edge list: "); first = TRUE; for (edge = sweep_line->head; @@ -1249,394 +1257,458 @@ _cairo_bo_sweep_line_print (cairo_bo_sweep_line_t *sweep_line) first = FALSE; } printf ("\n"); - - printf ("Sweep line from skip list: "); - first = TRUE; - for (elt = sweep_line->active_edges.chains[0]; - elt; - elt = elt->next[0]) - { - if (!first) - printf (", "); - _cairo_bo_edge_print (SKIP_ELT_TO_EDGE (elt)); - first = FALSE; - } - printf ("\n"); } static void print_state (const char *msg, + cairo_bo_event_t *event, cairo_bo_event_queue_t *event_queue, cairo_bo_sweep_line_t *sweep_line) { - printf ("%s\n", msg); + printf ("%s ", msg); + _cairo_bo_event_print (event); _cairo_bo_event_queue_print (event_queue); _cairo_bo_sweep_line_print (sweep_line); printf ("\n"); } #endif -/* Adds the trapezoid, if any, of the left edge to the #cairo_traps_t - * of bo_traps. */ +#if DEBUG_EVENTS +static void CAIRO_PRINTF_FORMAT (1, 2) +event_log (const char *fmt, ...) +{ + FILE *file; + + if (getenv ("CAIRO_DEBUG_EVENTS") == NULL) + return; + + file = fopen ("bo-events.txt", "a"); + if (file != NULL) { + va_list ap; + + va_start (ap, fmt); + vfprintf (file, fmt, ap); + va_end (ap); + + fclose (file); + } +} +#endif + +static inline cairo_bool_t +edges_colinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) +{ + if (_line_equal (&a->edge.line, &b->edge.line)) + return TRUE; + + if (_slope_compare (a, b)) + return FALSE; + + /* The choice of y is not truly arbitrary since we must guarantee that it + * is greater than the start of either line. + */ + if (a->edge.line.p1.y == b->edge.line.p1.y) { + return a->edge.line.p1.x == b->edge.line.p1.x; + } else if (a->edge.line.p1.y < b->edge.line.p1.y) { + return edge_compare_for_y_against_x (b, + a->edge.line.p1.y, + a->edge.line.p1.x) == 0; + } else { + return edge_compare_for_y_against_x (a, + b->edge.line.p1.y, + b->edge.line.p1.x) == 0; + } +} + +/* Adds the trapezoid, if any, of the left edge to the #cairo_traps_t */ static cairo_status_t _cairo_bo_edge_end_trap (cairo_bo_edge_t *left, - int32_t bot, - cairo_bo_traps_t *bo_traps) + int32_t bot, + cairo_traps_t *traps) { - cairo_fixed_t fixed_top, fixed_bot; - cairo_bo_trap_t *trap = left->deferred_trap; - cairo_bo_edge_t *right; + cairo_bo_trap_t *trap = &left->deferred_trap; - if (!trap) - return CAIRO_STATUS_SUCCESS; - - /* If the right edge of the trapezoid stopped earlier than the - * left edge, then cut the trapezoid bottom early. */ - right = trap->right; - if (right->bottom.y < bot) - bot = right->bottom.y; - - fixed_top = trap->top; - fixed_bot = bot; - - /* Only emit trapezoids with positive height. */ - if (fixed_top < fixed_bot) { - cairo_line_t left_line; - cairo_line_t right_line; - cairo_fixed_t xmin = bo_traps->xmin; - cairo_fixed_t ymin = bo_traps->ymin; - fixed_top += ymin; - fixed_bot += ymin; - - left_line.p1.x = left->top.x + xmin; - left_line.p1.y = left->top.y + ymin; - right_line.p1.x = right->top.x + xmin; - right_line.p1.y = right->top.y + ymin; - - left_line.p2.x = left->bottom.x + xmin; - left_line.p2.y = left->bottom.y + ymin; - right_line.p2.x = right->bottom.x + xmin; - right_line.p2.y = right->bottom.y + ymin; - - /* Avoid emitting the trapezoid if it is obviously degenerate. - * TODO: need a real collinearity test here for the cases - * where the trapezoid is degenerate, yet the top and bottom - * coordinates aren't equal. */ - if (left_line.p1.x != right_line.p1.x || - left_line.p1.y != right_line.p1.y || - left_line.p2.x != right_line.p2.x || - left_line.p2.y != right_line.p2.y) - { - _cairo_traps_add_trap (bo_traps->traps, - fixed_top, fixed_bot, - &left_line, &right_line); + /* Only emit (trivial) non-degenerate trapezoids with positive height. */ + if (likely (trap->top < bot)) { + _cairo_traps_add_trap (traps, + trap->top, bot, + &left->edge.line, &trap->right->edge.line); #if DEBUG_PRINT_STATE - printf ("Deferred trap: left=(%08x, %08x)-(%08x,%08x) " - "right=(%08x,%08x)-(%08x,%08x) top=%08x, bot=%08x\n", - left->top.x, left->top.y, left->bottom.x, left->bottom.y, - right->top.x, right->top.y, right->bottom.x, right->bottom.y, - trap->top, bot); + printf ("Deferred trap: left=(%x, %x)-(%x,%x) " + "right=(%x,%x)-(%x,%x) top=%x, bot=%x\n", + left->edge.line.p1.x, left->edge.line.p1.y, + left->edge.line.p2.x, left->edge.line.p2.y, + trap->right->edge.line.p1.x, trap->right->edge.line.p1.y, + trap->right->edge.line.p2.x, trap->right->edge.line.p2.y, + trap->top, bot); +#endif +#if DEBUG_EVENTS + event_log ("end trap: %lu %lu %d %d\n", + (long) left, + (long) trap->right, + trap->top, + bot); #endif - } } - _cairo_freelist_free (&bo_traps->freelist, trap); - left->deferred_trap = NULL; + trap->right = NULL; - return _cairo_traps_status (bo_traps->traps); + return _cairo_traps_status (traps); } + /* Start a new trapezoid at the given top y coordinate, whose edges * are `edge' and `edge->next'. If `edge' already has a trapezoid, - * then either add it to the traps in `bo_traps', if the trapezoid's + * then either add it to the traps in `traps', if the trapezoid's * right edge differs from `edge->next', or do nothing if the new * trapezoid would be a continuation of the existing one. */ -static cairo_status_t -_cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *edge, - int32_t top, - cairo_bo_traps_t *bo_traps) +static inline cairo_status_t +_cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left, + cairo_bo_edge_t *right, + int top, + cairo_traps_t *traps) { cairo_status_t status; - cairo_bo_trap_t *trap = edge->deferred_trap; - if (trap) { - if (trap->right == edge->next) return CAIRO_STATUS_SUCCESS; - status = _cairo_bo_edge_end_trap (edge, top, bo_traps); - if (status) + if (left->deferred_trap.right == right) + return CAIRO_STATUS_SUCCESS; + + if (left->deferred_trap.right != NULL) { + if (right != NULL && edges_colinear (left->deferred_trap.right, right)) + { + /* continuation on right, so just swap edges */ + left->deferred_trap.right = right; + return CAIRO_STATUS_SUCCESS; + } + + status = _cairo_bo_edge_end_trap (left, top, traps); + if (unlikely (status)) return status; } - if (edge->next) { - trap = edge->deferred_trap = _cairo_freelist_alloc (&bo_traps->freelist); - if (!edge->deferred_trap) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (right != NULL && ! edges_colinear (left, right)) { + left->deferred_trap.top = top; + left->deferred_trap.right = right; - trap->right = edge->next; - trap->top = top; +#if DEBUG_EVENTS + event_log ("begin trap: %lu %lu %d\n", + (long) left, + (long) right, + top); +#endif } + return CAIRO_STATUS_SUCCESS; } -static void -_cairo_bo_traps_init (cairo_bo_traps_t *bo_traps, - cairo_traps_t *traps, - cairo_fixed_t xmin, - cairo_fixed_t ymin, - cairo_fixed_t xmax, - cairo_fixed_t ymax) -{ - bo_traps->traps = traps; - _cairo_freelist_init (&bo_traps->freelist, sizeof(cairo_bo_trap_t)); - bo_traps->xmin = xmin; - bo_traps->ymin = ymin; - bo_traps->xmax = xmax; - bo_traps->ymax = ymax; -} - -static void -_cairo_bo_traps_fini (cairo_bo_traps_t *bo_traps) -{ - _cairo_freelist_fini (&bo_traps->freelist); -} - -#if DEBUG_VALIDATE -static void -_cairo_bo_sweep_line_validate (cairo_bo_sweep_line_t *sweep_line) -{ - cairo_bo_edge_t *edge; - skip_elt_t *elt; - - /* March through both the skip list's singly-linked list and the - * sweep line's own list through pointers in the edges themselves - * and make sure they agree at every point. */ - - for (edge = sweep_line->head, elt = sweep_line->active_edges.chains[0]; - edge && elt; - edge = edge->next, elt = elt->next[0]) - { - if (SKIP_ELT_TO_EDGE (elt) != edge) { - fprintf (stderr, "*** Error: Sweep line fails to validate: Inconsistent data in the two lists.\n"); - abort (); - } - } - - if (edge || elt) { - fprintf (stderr, "*** Error: Sweep line fails to validate: One list ran out before the other.\n"); - abort (); - } -} -#endif - - -static cairo_status_t -_active_edges_to_traps (cairo_bo_edge_t *head, +static inline cairo_status_t +_active_edges_to_traps (cairo_bo_edge_t *left, int32_t top, cairo_fill_rule_t fill_rule, - cairo_bo_traps_t *bo_traps) + cairo_traps_t *traps) { + cairo_bo_edge_t *right; cairo_status_t status; - int in_out = 0; - cairo_bo_edge_t *edge; - for (edge = head; edge; edge = edge->next) { - if (fill_rule == CAIRO_FILL_RULE_WINDING) { - in_out += edge->dir; - if (in_out == 0) { - status = _cairo_bo_edge_end_trap (edge, top, bo_traps); - if (status) - return status; - continue; +#if DEBUG_PRINT_STATE + printf ("Processing active edges for %x\n", top); +#endif + + if (fill_rule == CAIRO_FILL_RULE_WINDING) { + while (left != NULL) { + int in_out; + + /* Greedily search for the closing edge, so that we generate the + * maximal span width with the minimal number of trapezoids. + */ + in_out = left->edge.dir; + + /* Check if there is a co-linear edge with an existing trap */ + right = left->next; + if (left->deferred_trap.right == NULL) { + while (right != NULL && right->deferred_trap.right == NULL) + right = right->next; + + if (right != NULL && edges_colinear (left, right)) { + /* continuation on left */ + left->deferred_trap = right->deferred_trap; + right->deferred_trap.right = NULL; + } } - } else { - in_out++; - if ((in_out & 1) == 0) { - status = _cairo_bo_edge_end_trap (edge, top, bo_traps); - if (status) - return status; - continue; + + /* End all subsumed traps */ + right = left->next; + while (right != NULL) { + if (right->deferred_trap.right != NULL) { + status = _cairo_bo_edge_end_trap (right, top, traps); + if (unlikely (status)) + return status; + } + + in_out += right->edge.dir; + if (in_out == 0) { + cairo_bo_edge_t *next; + cairo_bool_t skip = FALSE; + + /* skip co-linear edges */ + next = right->next; + if (next != NULL) + skip = edges_colinear (right, next); + + if (! skip) + break; + } + + right = right->next; } + + status = _cairo_bo_edge_start_or_continue_trap (left, right, + top, traps); + if (unlikely (status)) + return status; + + left = right; + if (left != NULL) + left = left->next; } + } else { + while (left != NULL) { + int in_out = 0; - status = _cairo_bo_edge_start_or_continue_trap (edge, top, bo_traps); - if (status) - return status; + right = left->next; + while (right != NULL) { + if (right->deferred_trap.right != NULL) { + status = _cairo_bo_edge_end_trap (right, top, traps); + if (unlikely (status)) + return status; + } + + if ((in_out++ & 1) == 0) { + cairo_bo_edge_t *next; + cairo_bool_t skip = FALSE; + + /* skip co-linear edges */ + next = right->next; + if (next != NULL) + skip = edges_colinear (right, next); + + if (! skip) + break; + } + + right = right->next; + } + + status = _cairo_bo_edge_start_or_continue_trap (left, right, + top, traps); + if (unlikely (status)) + return status; + + left = right; + if (left != NULL) + left = left->next; + } } return CAIRO_STATUS_SUCCESS; } + /* Execute a single pass of the Bentley-Ottmann algorithm on edges, * generating trapezoids according to the fill_rule and appending them * to traps. */ static cairo_status_t -_cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_edge_t *edges, - int num_edges, +_cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t **start_events, + int num_events, cairo_fill_rule_t fill_rule, cairo_traps_t *traps, - cairo_fixed_t xmin, - cairo_fixed_t ymin, - cairo_fixed_t xmax, - cairo_fixed_t ymax, int *num_intersections) { - cairo_status_t status; + cairo_status_t status = CAIRO_STATUS_SUCCESS; /* silence compiler */ int intersection_count = 0; cairo_bo_event_queue_t event_queue; cairo_bo_sweep_line_t sweep_line; - cairo_bo_traps_t bo_traps; - cairo_bo_event_t *event, event_saved; - cairo_bo_edge_t *edge; + cairo_bo_event_t *event; cairo_bo_edge_t *left, *right; - cairo_bo_edge_t *edge1, *edge2; + cairo_bo_edge_t *e1, *e2; - if (num_edges == 0) - return CAIRO_STATUS_SUCCESS; +#if DEBUG_EVENTS + { + int i; - status = _cairo_bo_event_queue_init (&event_queue, edges, num_edges); - if (status) - return status; - - _cairo_bo_sweep_line_init (&sweep_line); - - _cairo_bo_traps_init (&bo_traps, traps, xmin, ymin, xmax, ymax); - -#if DEBUG_PRINT_STATE - print_state ("After initializing", &event_queue, &sweep_line); + for (i = 0; i < num_events; i++) { + cairo_bo_start_event_t *event = + ((cairo_bo_start_event_t **) start_events)[i]; + event_log ("edge: %lu (%d, %d) (%d, %d) (%d, %d) %d\n", + (long) &events[i].edge, + event->edge.edge.line.p1.x, + event->edge.edge.line.p1.y, + event->edge.edge.line.p2.x, + event->edge.edge.line.p2.y, + event->edge.top, + event->edge.bottom, + event->edge.edge.dir); + } + } #endif - while (1) - { - event = _cairo_bo_event_dequeue (&event_queue); - if (!event) - break; + _cairo_bo_event_queue_init (&event_queue, start_events, num_events); + _cairo_bo_sweep_line_init (&sweep_line); + while ((event = _cairo_bo_event_dequeue (&event_queue))) { if (event->point.y != sweep_line.current_y) { + for (e1 = sweep_line.stopped; e1; e1 = e1->next) { + if (e1->deferred_trap.right != NULL) { + status = _cairo_bo_edge_end_trap (e1, + e1->edge.bottom, + traps); + if (unlikely (status)) + goto unwind; + } + } + sweep_line.stopped = NULL; + status = _active_edges_to_traps (sweep_line.head, sweep_line.current_y, - fill_rule, &bo_traps); - if (status) + fill_rule, traps); + if (unlikely (status)) goto unwind; sweep_line.current_y = event->point.y; } - event_saved = *event; - _cairo_bo_event_queue_delete (&event_queue, event); - event = &event_saved; +#if DEBUG_EVENTS + event_log ("event: %d (%ld, %ld) %lu, %lu\n", + event->type, + (long) event->point.x, + (long) event->point.y, + (long) event->e1, + (long) event->e2); +#endif switch (event->type) { case CAIRO_BO_EVENT_TYPE_START: - edge = event->e1; + e1 = &((cairo_bo_start_event_t *) event)->edge; - status = _cairo_bo_sweep_line_insert (&sweep_line, edge); - if (status) - goto unwind; - /* Cache the insert position for use in pass 2. - event->e2 = Sortlist::prev (sweep_line, edge); - */ - - left = edge->prev; - right = edge->next; - - status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, edge); - if (status) + status = _cairo_bo_sweep_line_insert (&sweep_line, e1); + if (unlikely (status)) goto unwind; - status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, edge, right); - if (status) + status = _cairo_bo_event_queue_insert_stop (&event_queue, e1); + if (unlikely (status)) goto unwind; -#if DEBUG_PRINT_STATE - print_state ("After processing start", &event_queue, &sweep_line); -#endif + /* check to see if this is a continuation of a stopped edge */ + /* XXX change to an infinitesimal lengthening rule */ + for (left = sweep_line.stopped; left; left = left->next) { + if (e1->edge.top <= left->edge.bottom && + edges_colinear (e1, left)) + { + e1->deferred_trap = left->deferred_trap; + if (left->prev != NULL) + left->prev = left->next; + else + sweep_line.stopped = left->next; + if (left->next != NULL) + left->next->prev = left->prev; + break; + } + } + + left = e1->prev; + right = e1->next; + + if (left != NULL) { + status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e1); + if (unlikely (status)) + goto unwind; + } + + if (right != NULL) { + status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right); + if (unlikely (status)) + goto unwind; + } + break; case CAIRO_BO_EVENT_TYPE_STOP: - edge = event->e1; + e1 = ((cairo_bo_queue_event_t *) event)->e1; + _cairo_bo_event_queue_delete (&event_queue, event); - left = edge->prev; - right = edge->next; + left = e1->prev; + right = e1->next; - _cairo_bo_sweep_line_delete (&sweep_line, edge); + _cairo_bo_sweep_line_delete (&sweep_line, e1); - status = _cairo_bo_edge_end_trap (edge, edge->bottom.y, &bo_traps); - if (status) - goto unwind; + /* first, check to see if we have a continuation via a fresh edge */ + if (e1->deferred_trap.right != NULL) { + e1->next = sweep_line.stopped; + if (sweep_line.stopped != NULL) + sweep_line.stopped->prev = e1; + sweep_line.stopped = e1; + e1->prev = NULL; + } - status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, right); - if (status) - goto unwind; + if (left != NULL && right != NULL) { + status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, right); + if (unlikely (status)) + goto unwind; + } -#if DEBUG_PRINT_STATE - print_state ("After processing stop", &event_queue, &sweep_line); -#endif break; case CAIRO_BO_EVENT_TYPE_INTERSECTION: - edge1 = event->e1; - edge2 = event->e2; + e1 = ((cairo_bo_queue_event_t *) event)->e1; + e2 = ((cairo_bo_queue_event_t *) event)->e2; + _cairo_bo_event_queue_delete (&event_queue, event); /* skip this intersection if its edges are not adjacent */ - if (edge2 != edge1->next) + if (e2 != e1->next) break; intersection_count++; - edge1->middle = event->point; - edge2->middle = event->point; + left = e1->prev; + right = e2->next; - left = edge1->prev; - right = edge2->next; - - _cairo_bo_sweep_line_swap (&sweep_line, edge1, edge2); + _cairo_bo_sweep_line_swap (&sweep_line, e1, e2); /* after the swap e2 is left of e1 */ - status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, - left, edge2); - if (status) - goto unwind; + if (left != NULL) { + status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e2); + if (unlikely (status)) + goto unwind; + } - status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, - edge1, right); - if (status) - goto unwind; + if (right != NULL) { + status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right); + if (unlikely (status)) + goto unwind; + } -#if DEBUG_PRINT_STATE - print_state ("After processing intersection", &event_queue, &sweep_line); -#endif break; } -#if DEBUG_VALIDATE - _cairo_bo_sweep_line_validate (&sweep_line); -#endif } *num_intersections = intersection_count; - unwind: - for (edge = sweep_line.head; edge; edge = edge->next) { - cairo_status_t status2 = _cairo_bo_edge_end_trap (edge, - sweep_line.current_y, - &bo_traps); - if (!status) - status = status2; + for (e1 = sweep_line.stopped; e1; e1 = e1->next) { + if (e1->deferred_trap.right != NULL) { + status = _cairo_bo_edge_end_trap (e1, e1->edge.bottom, traps); + if (unlikely (status)) + break; + } } - _cairo_bo_traps_fini (&bo_traps); - _cairo_bo_sweep_line_fini (&sweep_line); + unwind: _cairo_bo_event_queue_fini (&event_queue); - return status; -} -static void -update_minmax(cairo_fixed_t *inout_min, - cairo_fixed_t *inout_max, - cairo_fixed_t v) -{ - if (v < *inout_min) - *inout_min = v; - if (v > *inout_max) - *inout_max = v; +#if DEBUG_EVENTS + event_log ("\n"); +#endif + + return status; } cairo_status_t @@ -1646,116 +1718,114 @@ _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps, { int intersections; cairo_status_t status; - cairo_bo_edge_t stack_edges[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_edge_t)]; - cairo_bo_edge_t *edges; - cairo_fixed_t xmin = 0x7FFFFFFF; - cairo_fixed_t ymin = 0x7FFFFFFF; - cairo_fixed_t xmax = -0x80000000; - cairo_fixed_t ymax = -0x80000000; - cairo_box_t limit; - cairo_bool_t has_limits; - int num_bo_edges; + cairo_bo_start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_start_event_t)]; + cairo_bo_start_event_t *events; + cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; + cairo_bo_event_t **event_ptrs; + int num_events; int i; - if (0 == polygon->num_edges) + num_events = polygon->num_edges; + if (unlikely (0 == num_events)) return CAIRO_STATUS_SUCCESS; - if (CAIRO_INJECT_FAULT ()) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - has_limits = _cairo_traps_get_limit (traps, &limit); - - edges = stack_edges; - if (polygon->num_edges > ARRAY_LENGTH (stack_edges)) { - edges = _cairo_malloc_ab (polygon->num_edges, sizeof (cairo_bo_edge_t)); - if (unlikely (edges == NULL)) + events = stack_events; + event_ptrs = stack_event_ptrs; + if (num_events > ARRAY_LENGTH (stack_events)) { + events = _cairo_malloc_ab_plus_c (num_events, + sizeof (cairo_bo_start_event_t) + + sizeof (cairo_bo_event_t *), + sizeof (cairo_bo_event_t *)); + if (unlikely (events == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + event_ptrs = (cairo_bo_event_t **) (events + num_events); } - /* Figure out the bounding box of the input coordinates and - * validate that we're not given invalid polygon edges. */ - for (i = 0; i < polygon->num_edges; i++) { - update_minmax (&xmin, &xmax, polygon->edges[i].edge.p1.x); - update_minmax (&ymin, &ymax, polygon->edges[i].edge.p1.y); - update_minmax (&xmin, &xmax, polygon->edges[i].edge.p2.x); - update_minmax (&ymin, &ymax, polygon->edges[i].edge.p2.y); - assert (polygon->edges[i].edge.p1.y <= polygon->edges[i].edge.p2.y && - "BUG: tessellator given upside down or horizontal edges"); + for (i = 0; i < num_events; i++) { + event_ptrs[i] = (cairo_bo_event_t *) &events[i]; + + events[i].type = CAIRO_BO_EVENT_TYPE_START; + events[i].point.y = polygon->edges[i].top; + events[i].point.x = + _line_compute_intersection_x_for_y (&polygon->edges[i].line, + events[i].point.y); + + events[i].edge.edge = polygon->edges[i]; + events[i].edge.deferred_trap.right = NULL; + events[i].edge.prev = NULL; + events[i].edge.next = NULL; } - /* The tessellation functions currently assume that no line - * segment extends more than 2^31-1 in either dimension. We - * guarantee this by offsetting the internal coordinates to the - * range [0,2^31-1], and clamping to 2^31-1 if a coordinate - * exceeds the range (and yes, this generates an incorrect - * result). First we have to clamp the bounding box itself. */ - /* XXX: Rather than changing the input values, a better approach - * would be to detect out-of-bounds input and return a - * CAIRO_STATUS_OVERFLOW value to the user. */ - if (xmax - xmin < 0) - xmax = xmin + 0x7FFFFFFF; - if (ymax - ymin < 0) - ymax = ymin + 0x7FFFFFFF; - - for (i = 0, num_bo_edges = 0; i < polygon->num_edges; i++) { - cairo_bo_edge_t *edge = &edges[num_bo_edges]; - cairo_point_t top = polygon->edges[i].edge.p1; - cairo_point_t bot = polygon->edges[i].edge.p2; - - /* Discard the edge if it lies outside the limits of traps. */ - if (has_limits) { - /* Strictly above or below the limits? */ - if (bot.y <= limit.p1.y || top.y >= limit.p2.y) - continue; - } - - /* Offset coordinates into the non-negative range. */ - top.x -= xmin; - top.y -= ymin; - bot.x -= xmin; - bot.y -= ymin; - - /* If the coordinates are still negative, then their extent is - * overflowing 2^31-1. We're going to kludge it and clamp the - * coordinates into the clamped bounding box. */ - if (top.x < 0) top.x = xmax - xmin; - if (top.y < 0) top.y = ymax - ymin; - if (bot.x < 0) bot.x = xmax - xmin; - if (bot.y < 0) bot.y = ymax - ymin; - - if (top.y == bot.y) { - /* Clamping might have produced horizontal edges. Ignore - * those. */ - continue; - } - assert (top.y < bot.y && - "BUG: clamping the input range flipped the " - "orientation of an edge"); - - edge->top.x = top.x; - edge->top.y = top.y; - edge->bottom.x = bot.x; - edge->bottom.y = bot.y; - edge->dir = polygon->edges[i].dir; - edge->deferred_trap = NULL; - edge->prev = NULL; - edge->next = NULL; - edge->sweep_line_elt = NULL; - - num_bo_edges++; - } +#if DEBUG_TRAPS + dump_edges (events, num_events, "bo-polygon-edges.txt"); +#endif /* XXX: This would be the convenient place to throw in multiple * passes of the Bentley-Ottmann algorithm. It would merely * require storing the results of each pass into a temporary * cairo_traps_t. */ - status = _cairo_bentley_ottmann_tessellate_bo_edges (edges, num_bo_edges, + status = _cairo_bentley_ottmann_tessellate_bo_edges (event_ptrs, + num_events, fill_rule, traps, - xmin, ymin, xmax, ymax, &intersections); +#if DEBUG_TRAPS + dump_traps (traps, "bo-polygon-out.txt"); +#endif - if (edges != stack_edges) - free (edges); + if (events != stack_events) + free (events); + + return status; +} + +cairo_status_t +_cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps, + cairo_fill_rule_t fill_rule) +{ + cairo_status_t status; + cairo_polygon_t polygon; + int i; + + if (unlikely (0 == traps->num_traps)) + return CAIRO_STATUS_SUCCESS; + +#if DEBUG_TRAPS + dump_traps (traps, "bo-traps-in.txt"); +#endif + + _cairo_polygon_init (&polygon); + _cairo_polygon_limit (&polygon, traps->limits, traps->num_limits); + + for (i = 0; i < traps->num_traps; i++) { + status = _cairo_polygon_add_line (&polygon, + &traps->traps[i].left, + traps->traps[i].top, + traps->traps[i].bottom, + 1); + if (unlikely (status)) + goto CLEANUP; + + status = _cairo_polygon_add_line (&polygon, + &traps->traps[i].right, + traps->traps[i].top, + traps->traps[i].bottom, + -1); + if (unlikely (status)) + goto CLEANUP; + } + + _cairo_traps_clear (traps); + status = _cairo_bentley_ottmann_tessellate_polygon (traps, + &polygon, + fill_rule); + +#if DEBUG_TRAPS + dump_traps (traps, "bo-traps-out.txt"); +#endif + + CLEANUP: + _cairo_polygon_fini (&polygon); return status; } @@ -1769,15 +1839,14 @@ edges_have_an_intersection_quadratic (cairo_bo_edge_t *edges, int i, j; cairo_bo_edge_t *a, *b; cairo_bo_point32_t intersection; - cairo_bo_status_t status; /* We must not be given any upside-down edges. */ for (i = 0; i < num_edges; i++) { assert (_cairo_bo_point32_compare (&edges[i].top, &edges[i].bottom) < 0); - edges[i].top.x <<= CAIRO_BO_GUARD_BITS; - edges[i].top.y <<= CAIRO_BO_GUARD_BITS; - edges[i].bottom.x <<= CAIRO_BO_GUARD_BITS; - edges[i].bottom.y <<= CAIRO_BO_GUARD_BITS; + edges[i].line.p1.x <<= CAIRO_BO_GUARD_BITS; + edges[i].line.p1.y <<= CAIRO_BO_GUARD_BITS; + edges[i].line.p2.x <<= CAIRO_BO_GUARD_BITS; + edges[i].line.p2.y <<= CAIRO_BO_GUARD_BITS; } for (i = 0; i < num_edges; i++) { @@ -1788,20 +1857,16 @@ edges_have_an_intersection_quadratic (cairo_bo_edge_t *edges, a = &edges[i]; b = &edges[j]; - status = _cairo_bo_edge_intersect (a, b, &intersection); - if (status == CAIRO_BO_STATUS_PARALLEL || - status == CAIRO_BO_STATUS_NO_INTERSECTION) - { + if (! _cairo_bo_edge_intersect (a, b, &intersection)) continue; - } printf ("Found intersection (%d,%d) between (%d,%d)-(%d,%d) and (%d,%d)-(%d,%d)\n", intersection.x, intersection.y, - a->top.x, a->top.y, - a->bottom.x, a->bottom.y, - b->top.x, b->top.y, - b->bottom.x, b->bottom.y); + a->line.p1.x, a->line.p1.y, + a->line.p2.x, a->line.p2.y, + b->line.p1.x, b->line.p1.y, + b->line.p2.x, b->line.p2.y); return TRUE; } @@ -2041,16 +2106,16 @@ main (void) for (i = 0; i < num_random; i++) { do { edge = &random_edges[i]; - edge->top.x = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); - edge->top.y = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); - edge->bottom.x = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); - edge->bottom.y = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); - if (edge->top.y > edge->bottom.y) { - int32_t tmp = edge->top.y; - edge->top.y = edge->bottom.y; - edge->bottom.y = tmp; + edge->line.p1.x = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); + edge->line.p1.y = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); + edge->line.p2.x = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); + edge->line.p2.y = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); + if (edge->line.p1.y > edge->line.p2.y) { + int32_t tmp = edge->line.p1.y; + edge->line.p1.y = edge->line.p2.y; + edge->line.p2.y = tmp; } - } while (edge->top.y == edge->bottom.y); + } while (edge->line.p1.y == edge->line.p2.y); } sprintf (random_name, "random-%02d", num_random); @@ -2061,4 +2126,3 @@ main (void) return 0; } #endif - diff --git a/gfx/cairo/cairo/src/cairo-clip-private.h b/gfx/cairo/cairo/src/cairo-clip-private.h index 4229e4fb9268..6128243502b1 100644 --- a/gfx/cairo/cairo/src/cairo-clip-private.h +++ b/gfx/cairo/cairo/src/cairo-clip-private.h @@ -43,6 +43,12 @@ extern const cairo_private cairo_rectangle_list_t _cairo_rectangles_nil; +enum { + CAIRO_CLIP_PATH_HAS_REGION = 0x1, + CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED = 0x2, + CAIRO_CLIP_PATH_IS_BOX = 0x4 +}; + struct _cairo_clip_path { cairo_reference_count_t ref_count; cairo_path_fixed_t path; @@ -50,83 +56,80 @@ struct _cairo_clip_path { double tolerance; cairo_antialias_t antialias; cairo_clip_path_t *prev; + + cairo_rectangle_int_t extents; + + /* partial caches */ + unsigned int flags; + cairo_region_t *region; + cairo_surface_t *surface; }; struct _cairo_clip { - cairo_clip_mode_t mode; + /* can be used as a cairo_hash_entry_t for live clips */ + cairo_clip_path_t *path; cairo_bool_t all_clipped; - /* - * Mask-based clipping for cases where the backend - * clipping isn't sufficiently able. - * - * The rectangle here represents the - * portion of the destination surface that this - * clip surface maps to, it does not - * represent the extents of the clip region or - * clip paths - */ - cairo_surface_t *surface; - cairo_rectangle_int_t surface_rect; - /* - * Surface clip serial number to store - * in the surface when this clip is set - */ - unsigned int serial; - /* - * A clip region that can be placed in the surface - */ - cairo_region_t *region; - /* - * If the surface supports path clipping, we store the list of - * clipping paths that has been set here as a linked list. - */ - cairo_clip_path_t *path; }; cairo_private void -_cairo_clip_init (cairo_clip_t *clip, cairo_surface_t *target); +_cairo_clip_init (cairo_clip_t *clip); cairo_private cairo_status_t +_cairo_clip_init_rectangle (cairo_clip_t *clip, + const cairo_rectangle_int_t *rect); + +cairo_private_no_warn cairo_clip_t * _cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other); cairo_private cairo_status_t -_cairo_clip_init_deep_copy (cairo_clip_t *clip, - cairo_clip_t *other, - cairo_surface_t *target); +_cairo_clip_init_copy_transformed (cairo_clip_t *clip, + cairo_clip_t *other, + const cairo_matrix_t *matrix); cairo_private void _cairo_clip_reset (cairo_clip_t *clip); +#define _cairo_clip_fini(clip) _cairo_clip_reset (clip) + +cairo_private cairo_status_t +_cairo_clip_rectangle (cairo_clip_t *clip, + const cairo_rectangle_int_t *rectangle); + cairo_private cairo_status_t _cairo_clip_clip (cairo_clip_t *clip, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, - cairo_antialias_t antialias, - cairo_surface_t *target); + cairo_antialias_t antialias); cairo_private cairo_status_t -_cairo_clip_intersect_to_rectangle (cairo_clip_t *clip, - cairo_rectangle_int_t *rectangle); +_cairo_clip_apply_clip (cairo_clip_t *clip, + const cairo_clip_t *other); + +cairo_private const cairo_rectangle_int_t * +_cairo_clip_get_extents (const cairo_clip_t *clip); + +cairo_private cairo_surface_t * +_cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *dst); cairo_private cairo_status_t -_cairo_clip_intersect_to_region (cairo_clip_t *clip, - cairo_region_t *region); +_cairo_clip_combine_with_surface (cairo_clip_t *clip, + cairo_surface_t *dst, + const cairo_rectangle_int_t *extents); -cairo_private cairo_status_t -_cairo_clip_combine_to_surface (cairo_clip_t *clip, - cairo_operator_t op, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents); +cairo_private cairo_int_status_t +_cairo_clip_get_region (cairo_clip_t *clip, + cairo_region_t **region); + +cairo_private cairo_int_status_t +_cairo_clip_get_boxes (cairo_clip_t *clip, + cairo_box_t **boxes, + int *count); cairo_private void -_cairo_clip_translate (cairo_clip_t *clip, - cairo_fixed_t tx, - cairo_fixed_t ty); +_cairo_clip_drop_cache (cairo_clip_t *clip); cairo_private cairo_rectangle_list_t* _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate); diff --git a/gfx/cairo/cairo/src/cairo-clip.c b/gfx/cairo/cairo/src/cairo-clip.c index 79e4cbe61e16..6acbcff2716f 100644 --- a/gfx/cairo/cairo/src/cairo-clip.c +++ b/gfx/cairo/cairo/src/cairo-clip.c @@ -3,6 +3,7 @@ * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. + * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -35,255 +36,123 @@ * Contributor(s): * Carl D. Worth * Kristian Høgsberg + * Chris Wilson */ #include "cairoint.h" #include "cairo-clip-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-region-private.h" + +/* Keep a stash of recently freed clip_paths, since we need to + * reallocate them frequently. + */ +#define MAX_FREED_POOL_SIZE 4 +typedef struct { + void *pool[MAX_FREED_POOL_SIZE]; + int top; +} freed_pool_t; + +static freed_pool_t clip_path_pool; + +static void * +_atomic_fetch (void **slot) +{ + return _cairo_atomic_ptr_cmpxchg (slot, *slot, NULL); +} + +static cairo_bool_t +_atomic_store (void **slot, void *ptr) +{ + return _cairo_atomic_ptr_cmpxchg (slot, NULL, ptr) == NULL; +} + +static void * +_freed_pool_get (freed_pool_t *pool) +{ + void *ptr; + int i; + + i = pool->top - 1; + if (i < 0) + i = 0; + + ptr = _atomic_fetch (&pool->pool[i]); + if (ptr != NULL) { + pool->top = i; + return ptr; + } + + /* either empty or contended */ + for (i = ARRAY_LENGTH (pool->pool); i--;) { + ptr = _atomic_fetch (&pool->pool[i]); + if (ptr != NULL) { + pool->top = i; + return ptr; + } + } + + /* empty */ + pool->top = 0; + return NULL; +} + +static void +_freed_pool_put (freed_pool_t *pool, void *ptr) +{ + int i = pool->top; + + if (_atomic_store (&pool->pool[i], ptr)) { + pool->top = i + 1; + return; + } + + /* either full or contended */ + for (i = 0; i < ARRAY_LENGTH (pool->pool); i++) { + if (_atomic_store (&pool->pool[i], ptr)) { + pool->top = i + 1; + return; + } + } + + /* full */ + pool->top = ARRAY_LENGTH (pool->pool); + free (ptr); +} + +static void +_freed_pool_reset (freed_pool_t *pool) +{ + int i; + + for (i = 0; i < ARRAY_LENGTH (pool->pool); i++) { + free (pool->pool[i]); + pool->pool[i] = NULL; + } +} static cairo_clip_path_t * -_cairo_clip_path_reference (cairo_clip_path_t *clip_path); - -static void -_cairo_clip_path_destroy (cairo_clip_path_t *clip_path); - -void -_cairo_clip_init (cairo_clip_t *clip, cairo_surface_t *target) -{ - if (target && target->backend) - clip->mode = _cairo_surface_get_clip_mode (target); - else - clip->mode = CAIRO_CLIP_MODE_MASK; - - clip->all_clipped = FALSE; - - clip->surface = NULL; - clip->surface_rect.x = 0; - clip->surface_rect.y = 0; - clip->surface_rect.width = 0; - clip->surface_rect.height = 0; - - clip->serial = 0; - - clip->region = NULL; - - clip->path = NULL; -} - -cairo_status_t -_cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other) -{ - clip->mode = other->mode; - - clip->all_clipped = other->all_clipped; - - clip->surface = cairo_surface_reference (other->surface); - clip->surface_rect = other->surface_rect; - - clip->serial = other->serial; - - if (other->region) { - cairo_status_t status; - - clip->region = cairo_region_copy (other->region); - - status = cairo_region_status (clip->region); - if (unlikely (status)) { - cairo_surface_destroy (clip->surface); - cairo_region_destroy (clip->region); - clip->region = NULL; - - return status; - } - } else { - clip->region = NULL; - } - - clip->path = _cairo_clip_path_reference (other->path); - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_clip_reset (cairo_clip_t *clip) -{ - clip->all_clipped = FALSE; - - /* destroy any existing clip-region artifacts */ - cairo_surface_destroy (clip->surface); - clip->surface = NULL; - - clip->serial = 0; - - if (clip->region) { - cairo_region_destroy (clip->region); - - clip->region = NULL; - } - - _cairo_clip_path_destroy (clip->path); - clip->path = NULL; -} - -static void -_cairo_clip_set_all_clipped (cairo_clip_t *clip, cairo_surface_t *target) -{ - _cairo_clip_reset (clip); - - clip->all_clipped = TRUE; - clip->serial = _cairo_surface_allocate_clip_serial (target); -} - - -static cairo_status_t -_cairo_clip_path_intersect_to_rectangle (cairo_clip_path_t *clip_path, - cairo_rectangle_int_t *rectangle) -{ - while (clip_path) { - cairo_rectangle_int_t extents; - - _cairo_path_fixed_approximate_clip_extents (&clip_path->path, &extents); - - if (! _cairo_rectangle_intersect (rectangle, &extents)) - return CAIRO_STATUS_SUCCESS; - - clip_path = clip_path->prev; - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_clip_intersect_to_rectangle (cairo_clip_t *clip, - cairo_rectangle_int_t *rectangle) -{ - cairo_status_t status; - cairo_bool_t is_empty; - - if (!clip) - return CAIRO_STATUS_SUCCESS; - - if (clip->all_clipped) { - *rectangle = clip->surface_rect; - return CAIRO_STATUS_SUCCESS; - } - - if (clip->path) { - status = _cairo_clip_path_intersect_to_rectangle (clip->path, - rectangle); - if (unlikely (status)) - return status; - } - - if (clip->region) { - cairo_rectangle_int_t extents; - - cairo_region_get_extents (clip->region, &extents); - is_empty = _cairo_rectangle_intersect (rectangle, &extents); - if (is_empty) - return CAIRO_STATUS_SUCCESS; - } - - if (clip->surface) - is_empty = _cairo_rectangle_intersect (rectangle, &clip->surface_rect); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_clip_intersect_to_region (cairo_clip_t *clip, - cairo_region_t *region) -{ - cairo_status_t status; - - if (!clip) - return CAIRO_STATUS_SUCCESS; - - if (clip->all_clipped) - return cairo_region_intersect_rectangle (region, &clip->surface_rect); - - if (clip->path) { - /* Intersect clip path into region. */ - } - - if (clip->region) { - status = cairo_region_intersect (region, clip->region); - if (unlikely (status)) - return status; - } - - if (clip->surface) - return cairo_region_intersect_rectangle (region, &clip->surface_rect); - - return CAIRO_STATUS_SUCCESS; -} - -/* Combines the region of clip->surface given by extents in - * device backend coordinates into the given temporary surface, - * which has its origin at dst_x, dst_y in backend coordinates - */ -cairo_status_t -_cairo_clip_combine_to_surface (cairo_clip_t *clip, - cairo_operator_t op, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents) -{ - cairo_surface_pattern_t pattern; - cairo_status_t status; - - if (clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - _cairo_pattern_init_for_surface (&pattern, clip->surface); - - status = _cairo_surface_composite (op, - &pattern.base, - NULL, - dst, - extents->x - clip->surface_rect.x, - extents->y - clip->surface_rect.y, - 0, 0, - extents->x - dst_x, - extents->y - dst_y, - extents->width, extents->height); - - _cairo_pattern_fini (&pattern.base); - - return status; -} - -static cairo_status_t -_cairo_clip_intersect_path (cairo_clip_t *clip, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) +_cairo_clip_path_create (cairo_clip_t *clip) { cairo_clip_path_t *clip_path; - cairo_status_t status; - if (clip->mode != CAIRO_CLIP_MODE_PATH) - return CAIRO_INT_STATUS_UNSUPPORTED; - - clip_path = malloc (sizeof (cairo_clip_path_t)); - if (unlikely (clip_path == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _cairo_path_fixed_init_copy (&clip_path->path, path); - if (unlikely (status)) { - free (clip_path); - return status; + clip_path = _freed_pool_get (&clip_path_pool); + if (unlikely (clip_path == NULL)) { + clip_path = malloc (sizeof (cairo_clip_path_t)); + if (unlikely (clip_path == NULL)) + return NULL; } CAIRO_REFERENCE_COUNT_INIT (&clip_path->ref_count, 1); - clip_path->fill_rule = fill_rule; - clip_path->tolerance = tolerance; - clip_path->antialias = antialias; + + clip_path->flags = 0; + clip_path->region = NULL; + clip_path->surface = NULL; + clip_path->prev = clip->path; clip->path = clip_path; - return CAIRO_STATUS_SUCCESS; + return clip_path; } static cairo_clip_path_t * @@ -302,535 +171,1177 @@ _cairo_clip_path_reference (cairo_clip_path_t *clip_path) static void _cairo_clip_path_destroy (cairo_clip_path_t *clip_path) { - if (clip_path == NULL) - return; - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count)); if (! _cairo_reference_count_dec_and_test (&clip_path->ref_count)) return; _cairo_path_fixed_fini (&clip_path->path); - _cairo_clip_path_destroy (clip_path->prev); - free (clip_path); + if (clip_path->region != NULL) + cairo_region_destroy (clip_path->region); + if (clip_path->surface != NULL) + cairo_surface_destroy (clip_path->surface); + + if (clip_path->prev != NULL) + _cairo_clip_path_destroy (clip_path->prev); + + _freed_pool_put (&clip_path_pool, clip_path); } - -static cairo_int_status_t -_cairo_clip_intersect_region (cairo_clip_t *clip, - cairo_traps_t *traps, - cairo_surface_t *target) +void +_cairo_clip_init (cairo_clip_t *clip) { - cairo_region_t *region; - cairo_int_status_t status; + clip->all_clipped = FALSE; + clip->path = NULL; +} - if (clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - if (clip->mode != CAIRO_CLIP_MODE_REGION) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_traps_extract_region (traps, ®ion); - if (status) - return status; - - if (clip->region) { - status = cairo_region_intersect (clip->region, region); - cairo_region_destroy (region); - } else { - clip->region = region; +static void +_cairo_clip_set_all_clipped (cairo_clip_t *clip) +{ + clip->all_clipped = TRUE; + if (clip->path != NULL) { + _cairo_clip_path_destroy (clip->path); + clip->path = NULL; } - - clip->serial = _cairo_surface_allocate_clip_serial (target); - - if (!clip->region || cairo_region_is_empty (clip->region)) - _cairo_clip_set_all_clipped (clip, target); - - return status; } static cairo_status_t -_cairo_clip_intersect_mask (cairo_clip_t *clip, - cairo_traps_t *traps, - cairo_antialias_t antialias, - cairo_surface_t *target) +_cairo_clip_intersect_rectangle (cairo_clip_t *clip, + const cairo_rectangle_int_t *rect) { - cairo_pattern_union_t pattern; - cairo_box_t extents; - cairo_rectangle_int_t surface_rect, target_rect; - cairo_surface_t *surface = NULL; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - if (clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - /* Represent the clip as a mask surface. We create a new surface - * the size of the intersection of the old mask surface and the - * extents of the new clip path. */ - - _cairo_traps_extents (traps, &extents); - _cairo_box_round_to_rectangle (&extents, &surface_rect); - - if (clip->surface != NULL) { - if (! _cairo_rectangle_intersect (&surface_rect, &clip->surface_rect)) - goto DONE; - } - - /* Intersect with the target surface rectangle so we don't use - * more memory and time than we need to. */ - status = _cairo_surface_get_extents (target, &target_rect); - if (status == CAIRO_STATUS_SUCCESS) { - if (! _cairo_rectangle_intersect (&surface_rect, &target_rect)) - goto DONE; - } - - if (surface_rect.width == 0 || surface_rect.height == 0) - goto DONE; - - _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE, - CAIRO_CONTENT_COLOR); - /* The clipping operation should ideally be something like the following to - * avoid having to do as many passes over the data - - if (clip->surface != NULL) { - _cairo_pattern_init_for_surface (&pattern.surface, clip->surface); - } else { - _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE, - CAIRO_CONTENT_COLOR); - } - status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_IN, - &pattern.base, - surface, - antialias, - 0, 0, - 0, 0, - surface_rect.width, - surface_rect.height, - traps->traps, - traps->num_traps); - - However this operation is not accelerated by pixman - - I believe the best possible operation would probably an unbounded SRC - operator. Using SRC we could potentially avoid having to initialize - the surface which would be ideal from an efficiency point of view. - However, CAIRO_OPERATOR_SOURCE is bounded by the trapezoid mask and - _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_SOURCE) will assert - because it assumes CAIRO_OPERATOR_SOURCE has been converted into other - operations. - */ - - surface = _cairo_surface_create_similar_solid (target, - CAIRO_CONTENT_ALPHA, - surface_rect.width, - surface_rect.height, - CAIRO_COLOR_TRANSPARENT); - if (surface->status) { - _cairo_pattern_fini (&pattern.base); - return surface->status; - } - - /* Render the new clipping path into the new mask surface. */ - - _cairo_traps_translate (traps, -surface_rect.x, -surface_rect.y); - - status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_ADD, - &pattern.base, - surface, - antialias, - 0, 0, - 0, 0, - surface_rect.width, - surface_rect.height, - traps->traps, - traps->num_traps); - - _cairo_pattern_fini (&pattern.base); - - if (unlikely (status)) { - cairo_surface_destroy (surface); - return status; - } - - /* If there was a clip surface already, combine it with the new - * mask surface using the IN operator, so we get the intersection - * of the old and new clipping paths. */ - - if (clip->surface != NULL) { - _cairo_pattern_init_for_surface (&pattern.surface, clip->surface); - - status = _cairo_surface_composite (CAIRO_OPERATOR_IN, - &pattern.base, - NULL, - surface, - surface_rect.x - clip->surface_rect.x, - surface_rect.y - clip->surface_rect.y, - 0, 0, - 0, 0, - surface_rect.width, - surface_rect.height); - - _cairo_pattern_fini (&pattern.base); - - if (unlikely (status)) { - cairo_surface_destroy (surface); - return status; - } - } - - DONE: - cairo_surface_destroy (clip->surface); - clip->surface = surface; - clip->surface_rect = surface_rect; - clip->serial = _cairo_surface_allocate_clip_serial (target); - - if (surface_rect.width == 0 || surface_rect.height == 0) - _cairo_clip_set_all_clipped (clip, target); - - return status; -} - -static cairo_status_t -_cairo_clip_intersect_mask_using_spans (cairo_clip_t *clip, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_surface_t *target) -{ - cairo_span_renderer_t *renderer = NULL; - cairo_pattern_union_t pattern; - cairo_rectangle_int_t surface_rect; - cairo_surface_t *surface = NULL; + cairo_clip_path_t *clip_path; cairo_status_t status; - cairo_operator_t op; - cairo_composite_rectangles_t rects; - if (clip->all_clipped) + if (clip->path != NULL) { + if (rect->x <= clip->path->extents.x && + rect->y <= clip->path->extents.y && + rect->x + rect->width >= clip->path->extents.x + clip->path->extents.width && + rect->y + rect->height >= clip->path->extents.y + clip->path->extents.height) + { + return CAIRO_STATUS_SUCCESS; + } + } + + clip_path = _cairo_clip_path_create (clip); + if (unlikely (clip_path == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_path_fixed_init (&clip_path->path); + + status = _cairo_path_fixed_move_to (&clip_path->path, + _cairo_fixed_from_int (rect->x), + _cairo_fixed_from_int (rect->y)); + assert (status == CAIRO_STATUS_SUCCESS); + status = _cairo_path_fixed_rel_line_to (&clip_path->path, + _cairo_fixed_from_int (rect->width), + _cairo_fixed_from_int (0)); + assert (status == CAIRO_STATUS_SUCCESS); + status = _cairo_path_fixed_rel_line_to (&clip_path->path, + _cairo_fixed_from_int (0), + _cairo_fixed_from_int (rect->height)); + assert (status == CAIRO_STATUS_SUCCESS); + status = _cairo_path_fixed_rel_line_to (&clip_path->path, + _cairo_fixed_from_int (-rect->width), + _cairo_fixed_from_int (0)); + assert (status == CAIRO_STATUS_SUCCESS); + status = _cairo_path_fixed_close_path (&clip_path->path); + assert (status == CAIRO_STATUS_SUCCESS); + + clip_path->extents = *rect; + clip_path->fill_rule = CAIRO_FILL_RULE_WINDING; + clip_path->tolerance = 1; + clip_path->antialias = CAIRO_ANTIALIAS_NONE; + clip_path->flags |= CAIRO_CLIP_PATH_IS_BOX; + + /* could preallocate the region if it proves worthwhile */ + + return CAIRO_STATUS_SUCCESS; +} + +/* XXX consider accepting a matrix, no users yet. */ +cairo_status_t +_cairo_clip_init_rectangle (cairo_clip_t *clip, + const cairo_rectangle_int_t *rect) +{ + _cairo_clip_init (clip); + + if (rect == NULL) return CAIRO_STATUS_SUCCESS; - _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE, - CAIRO_CONTENT_COLOR); - - /* If we have a clip surface we're going to use IN to combine our - * new clip with the old clip. The ADD is done to a transparent - * surface, as that's a fast way of doing it currently. We should - * really be using SOURCE instead, but _cairo_surface_composite() - * checks that it's not called with SOURCE or DEST. */ - op = clip->surface ? CAIRO_OPERATOR_IN : CAIRO_OPERATOR_ADD; - - /* Test if the target can composite spans. We're going to assume - * this is a good indicator of whether a similar surface is going - * to be able to composite spans too. */ - if ( !_cairo_surface_check_span_renderer (op, - &pattern.base, - target, - antialias, - NULL)) - { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto BAIL; + if (rect->width == 0 || rect->height == 0) { + _cairo_clip_set_all_clipped (clip); + return CAIRO_STATUS_SUCCESS; } - status = _cairo_surface_get_extents (target, &surface_rect); - if (status) - goto BAIL; + return _cairo_clip_intersect_rectangle (clip, rect); +} - /* We'll create a new surface the size of the intersection of the - * old mask surface and the extents of the new clip path. */ - { - cairo_rectangle_int_t extents; +cairo_clip_t * +_cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other) +{ + if (other != NULL) { + clip->all_clipped = other->all_clipped; + clip->path = _cairo_clip_path_reference (other->path); - _cairo_path_fixed_approximate_clip_extents (path, &extents); - if (! _cairo_rectangle_intersect (&surface_rect, &extents)) - goto SUCCESS; - - if (clip->surface != NULL && - ! _cairo_rectangle_intersect (&surface_rect, &clip->surface_rect)) - goto SUCCESS; + /* this guy is here because of the weird return semantics of _cairo_clip_init_copy */ + if (!other->path) + return NULL; + } else { + _cairo_clip_init (clip); } - /* Make the new mask surface and optionally initialise it from the - * previous clip if we have one. */ - surface = _cairo_surface_create_similar_solid (target, - CAIRO_CONTENT_ALPHA, - surface_rect.width, - surface_rect.height, - CAIRO_COLOR_TRANSPARENT); - if (surface->status) { - _cairo_pattern_fini (&pattern.base); - return surface->status; + return clip; +} + +void +_cairo_clip_reset (cairo_clip_t *clip) +{ + clip->all_clipped = FALSE; + if (clip->path != NULL) { + _cairo_clip_path_destroy (clip->path); + clip->path = NULL; + } +} + +static cairo_status_t +_cairo_clip_intersect_path (cairo_clip_t *clip, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_clip_path_t *clip_path; + cairo_status_t status; + cairo_rectangle_int_t extents; + cairo_box_t box; + cairo_bool_t is_box = FALSE; + + if (clip->path != NULL) { + if (clip->path->fill_rule == fill_rule && + (path->is_rectilinear || + (tolerance == clip->path->tolerance && + antialias == clip->path->antialias)) && + _cairo_path_fixed_equal (&clip->path->path, path)) + { + return CAIRO_STATUS_SUCCESS; + } } - if (clip->surface) { - cairo_surface_pattern_t old_clip; - _cairo_pattern_init_for_surface (&old_clip, clip->surface); - status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, - &old_clip.base, - NULL, - surface, - surface_rect.x - clip->surface_rect.x, - surface_rect.y - clip->surface_rect.y, - 0, 0, - 0, 0, - surface_rect.width, - surface_rect.height); - _cairo_pattern_fini (&old_clip.base); - if (status) - goto BAIL; + _cairo_path_fixed_approximate_clip_extents (path, &extents); + if (extents.width == 0 || extents.height == 0) { + _cairo_clip_set_all_clipped (clip); + return CAIRO_STATUS_SUCCESS; } - _cairo_composite_rectangles_init (&rects, - surface_rect.x, - surface_rect.y, - surface_rect.width, - surface_rect.height); - rects.dst.x = 0; - rects.dst.y = 0; + is_box = _cairo_path_fixed_is_box (path, &box); + if (clip->path != NULL) { + if (! _cairo_rectangle_intersect (&extents, &clip->path->extents)) { + _cairo_clip_set_all_clipped (clip); + return CAIRO_STATUS_SUCCESS; + } - /* Render the new clipping path into the new mask surface. We've - * chosen op to either combine the new clip path with the existing - * clip mask (if there is one) or just render it. */ - status =_cairo_path_fixed_fill_using_spans (op, &pattern.base, - path, surface, - fill_rule, tolerance, - antialias, &rects); - if (status) - goto BAIL; + /* does this clip wholly subsume the others? */ + if (is_box && + box.p1.x <= _cairo_fixed_from_int (clip->path->extents.x) && + box.p2.x >= _cairo_fixed_from_int (clip->path->extents.x + clip->path->extents.width) && + box.p1.y <= _cairo_fixed_from_int (clip->path->extents.y) && + box.p2.y >= _cairo_fixed_from_int (clip->path->extents.y + clip->path->extents.height)) + { + return CAIRO_STATUS_SUCCESS; + } + } - SUCCESS: - if (clip->surface != NULL) - cairo_surface_destroy (clip->surface); - clip->surface = surface; - clip->surface_rect = surface_rect; - clip->serial = _cairo_surface_allocate_clip_serial (target); - surface = NULL; + clip_path = _cairo_clip_path_create (clip); + if (unlikely (clip_path == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); - if (surface_rect.width == 0 || surface_rect.height == 0) - _cairo_clip_set_all_clipped (clip, target); + status = _cairo_path_fixed_init_copy (&clip_path->path, path); + if (unlikely (status)) { + clip->path = clip->path->prev; + _cairo_clip_path_destroy (clip_path); + return status; + } - BAIL: - if (renderer) - renderer->destroy(renderer); - if (surface) - cairo_surface_destroy (surface); - _cairo_pattern_fini (&pattern.base); - return status; + clip_path->extents = extents; + clip_path->fill_rule = fill_rule; + clip_path->tolerance = tolerance; + clip_path->antialias = antialias; + if (is_box) + clip_path->flags |= CAIRO_CLIP_PATH_IS_BOX; + + return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_clip_clip (cairo_clip_t *clip, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, - cairo_antialias_t antialias, - cairo_surface_t *target) + cairo_antialias_t antialias) { - cairo_status_t status; - cairo_rectangle_int_t limits, extents; - cairo_traps_t traps; - cairo_box_t ignored_box; - cairo_bool_t have_limits; - if (clip->all_clipped) return CAIRO_STATUS_SUCCESS; /* catch the empty clip path */ - if (! path->has_current_point) { - _cairo_clip_set_all_clipped (clip, target); + if (_cairo_path_fixed_fill_is_empty (path)) { + _cairo_clip_set_all_clipped (clip); return CAIRO_STATUS_SUCCESS; } - status = _cairo_clip_intersect_path (clip, - path, fill_rule, tolerance, - antialias); - if (status == CAIRO_STATUS_SUCCESS) - clip->serial = _cairo_surface_allocate_clip_serial (target); + return _cairo_clip_intersect_path (clip, + path, fill_rule, tolerance, + antialias); +} - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; +cairo_status_t +_cairo_clip_rectangle (cairo_clip_t *clip, + const cairo_rectangle_int_t *rectangle) +{ + if (clip->all_clipped) + return CAIRO_STATUS_SUCCESS; - /* TODO: allow ANTIALIAS_NONE when we have a mono scan converter - * again. */ - if (antialias != CAIRO_ANTIALIAS_NONE && - !_cairo_path_fixed_is_box (path, &ignored_box) && - !_cairo_path_fixed_is_region (path)) - { - status = _cairo_clip_intersect_mask_using_spans ( - clip, path, fill_rule, tolerance, antialias, target); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) + if (rectangle->width == 0 || rectangle->height == 0) { + _cairo_clip_set_all_clipped (clip); + return CAIRO_STATUS_SUCCESS; + } + + /* if a smaller clip has already been set, ignore the new path */ + if (clip->path != NULL) { + if (rectangle->x <= clip->path->extents.x && + rectangle->y <= clip->path->extents.x && + rectangle->x + rectangle->width >= clip->path->extents.x + clip->path->extents.width && + rectangle->y + rectangle->height >= clip->path->extents.y + clip->path->extents.height) + { + return CAIRO_STATUS_SUCCESS; + } + } + + return _cairo_clip_intersect_rectangle (clip, rectangle); +} + +static cairo_status_t +_cairo_clip_path_reapply_clip_path_transform (cairo_clip_t *clip, + cairo_clip_path_t *other_path, + const cairo_matrix_t *matrix) +{ + cairo_status_t status; + cairo_clip_path_t *clip_path; + cairo_bool_t is_empty; + + if (other_path->prev != NULL) { + status = _cairo_clip_path_reapply_clip_path_transform (clip, + other_path->prev, + matrix); + if (unlikely (status)) return status; } + clip_path = _cairo_clip_path_create (clip); + if (unlikely (clip_path == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_path_fixed_init_copy (&clip_path->path, + &other_path->path); + if (unlikely (status)) { + _cairo_clip_path_destroy (clip_path); + return status; + } + + _cairo_path_fixed_transform (&clip_path->path, matrix); + _cairo_path_fixed_approximate_clip_extents (&clip_path->path, + &clip_path->extents); + if (clip_path->prev != NULL) { + is_empty = _cairo_rectangle_intersect (&clip_path->extents, + &clip_path->prev->extents); + } + + clip_path->fill_rule = other_path->fill_rule; + clip_path->tolerance = other_path->tolerance; + clip_path->antialias = other_path->antialias; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_clip_path_reapply_clip_path_translate (cairo_clip_t *clip, + cairo_clip_path_t *other_path, + int tx, int ty) +{ + cairo_status_t status; + cairo_clip_path_t *clip_path; + + if (other_path->prev != NULL) { + status = _cairo_clip_path_reapply_clip_path_translate (clip, + other_path->prev, + tx, ty); + if (unlikely (status)) + return status; + } + + clip_path = _cairo_clip_path_create (clip); + if (unlikely (clip_path == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_path_fixed_init_copy (&clip_path->path, + &other_path->path); + if (unlikely (status)) { + _cairo_clip_path_destroy (clip_path); + return status; + } + + _cairo_path_fixed_translate (&clip_path->path, + _cairo_fixed_from_int (tx), + _cairo_fixed_from_int (ty)); + + clip_path->fill_rule = other_path->fill_rule; + clip_path->tolerance = other_path->tolerance; + clip_path->antialias = other_path->antialias; + + clip_path->flags = other_path->flags; + if (other_path->region != NULL) { + clip_path->region = cairo_region_copy (other_path->region); + cairo_region_translate (clip_path->region, tx, ty); + } + clip_path->surface = cairo_surface_reference (other_path->surface); + + clip_path->extents = other_path->extents; + clip_path->extents.x += tx; + clip_path->extents.y += ty; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_clip_init_copy_transformed (cairo_clip_t *clip, + cairo_clip_t *other, + const cairo_matrix_t *matrix) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + int tx, ty; + + if (other == NULL) { + _cairo_clip_init (clip); + return CAIRO_STATUS_SUCCESS; + } + + if (other->all_clipped) { + _cairo_clip_init (clip); + clip->all_clipped = TRUE; + return CAIRO_STATUS_SUCCESS; + } + + if (_cairo_matrix_is_identity (matrix)) { + _cairo_clip_init_copy (clip, other); + return CAIRO_STATUS_SUCCESS; + } + + if (other->path != NULL) { + _cairo_clip_init (clip); + + /* if we only need to translate, so we can reuse the caches... */ + /* XXX we still loose the benefit of constructs when the copy is + * deleted though. Indirect clip_paths? + */ + if (_cairo_matrix_is_integer_translation (matrix, &tx, &ty)) { + status = _cairo_clip_path_reapply_clip_path_translate (clip, + other->path, + tx, ty); + } else { + status = _cairo_clip_path_reapply_clip_path_transform (clip, + other->path, + matrix); + if (clip->path->extents.width == 0 && + clip->path->extents.height == 0) + { + _cairo_clip_set_all_clipped (clip); + } + } + } + + return status; +} + +static cairo_status_t +_cairo_clip_apply_clip_path (cairo_clip_t *clip, + const cairo_clip_path_t *path) +{ + cairo_status_t status; + + if (path->prev != NULL) + status = _cairo_clip_apply_clip_path (clip, path->prev); + + return _cairo_clip_intersect_path (clip, + &path->path, + path->fill_rule, + path->tolerance, + path->antialias); +} + +cairo_status_t +_cairo_clip_apply_clip (cairo_clip_t *clip, + const cairo_clip_t *other) +{ + cairo_status_t status; + + if (clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (other->all_clipped) { + _cairo_clip_set_all_clipped (clip); + return CAIRO_STATUS_SUCCESS; + } + + status = CAIRO_STATUS_SUCCESS; + if (other->path != NULL) + status = _cairo_clip_apply_clip_path (clip, other->path); + + return status; +} + +static inline cairo_bool_t +_clip_paths_are_rectilinear (cairo_clip_path_t *clip_path) +{ + while (clip_path != NULL) { + if (! clip_path->path.is_rectilinear) + return FALSE; + + clip_path = clip_path->prev; + } + + return TRUE; +} + +static cairo_int_status_t +_cairo_clip_path_to_region_geometric (cairo_clip_path_t *clip_path) +{ + cairo_traps_t traps; + cairo_box_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (cairo_box_t)]; + cairo_box_t *boxes = stack_boxes; + cairo_status_t status; + int n; + + /* If we have nothing to intersect with this path, then it cannot + * magically be reduced into a region. + */ + if (clip_path->prev == NULL) + goto UNSUPPORTED; + + /* Start simple... Intersect some boxes with an arbitrary path. */ + if (! clip_path->path.is_rectilinear) + goto UNSUPPORTED; + if (clip_path->prev->prev != NULL) + goto UNSUPPORTED; + _cairo_traps_init (&traps); + _cairo_box_from_rectangle (&boxes[0], &clip_path->extents); + _cairo_traps_limit (&traps, boxes, 1); - /* Limit the traps to the target surface and current clip - * - so we don't add more traps than needed. */ - have_limits = FALSE; - if (clip->region != NULL) { - cairo_region_get_extents (clip->region, &limits); - have_limits = TRUE; + status = _cairo_path_fixed_fill_rectilinear_to_traps (&clip_path->path, + clip_path->fill_rule, + &traps); + if (unlikely (_cairo_status_is_error (status))) + return status; + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + goto UNSUPPORTED; + + if (traps.num_traps > ARRAY_LENGTH (stack_boxes)) { + boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t)); + if (unlikely (boxes == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - if (clip->surface != NULL) { - if (have_limits) { - if (! _cairo_rectangle_intersect (&limits, &clip->surface_rect)) { - _cairo_clip_set_all_clipped (clip, target); - return CAIRO_STATUS_SUCCESS; - } - } else { - limits = clip->surface_rect; - have_limits = TRUE; - } + for (n = 0; n < traps.num_traps; n++) { + boxes[n].p1.x = traps.traps[n].left.p1.x; + boxes[n].p1.y = traps.traps[n].top; + boxes[n].p2.x = traps.traps[n].right.p1.x; + boxes[n].p2.y = traps.traps[n].bottom; } - status = _cairo_surface_get_extents (target, &extents); - if (status == CAIRO_STATUS_SUCCESS) { - if (have_limits) { - if (! _cairo_rectangle_intersect (&limits, &extents)) { - _cairo_clip_set_all_clipped (clip, target); - return CAIRO_STATUS_SUCCESS; - } - } else { - limits = extents; - have_limits = TRUE; - } - } - - if (have_limits) { - cairo_box_t box; - - _cairo_box_from_rectangle (&box, &limits); - _cairo_traps_limit (&traps, &box); - } - - status = _cairo_path_fixed_fill_to_traps (path, - fill_rule, - tolerance, + _cairo_traps_clear (&traps); + _cairo_traps_limit (&traps, boxes, n); + status = _cairo_path_fixed_fill_to_traps (&clip_path->prev->path, + clip_path->prev->fill_rule, + clip_path->prev->tolerance, &traps); + if (boxes != stack_boxes) + free (boxes); + if (unlikely (status)) - goto bail; + return status; - status = _cairo_clip_intersect_region (clip, &traps, target); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto bail; + status = _cairo_traps_extract_region (&traps, &clip_path->region); + _cairo_traps_fini (&traps); - status = _cairo_clip_intersect_mask (clip, &traps, antialias, target); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + goto UNSUPPORTED; + if (unlikely (status)) + return status; - bail: + clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION; + return CAIRO_STATUS_SUCCESS; + +UNSUPPORTED: + clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED; + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_clip_path_to_region (cairo_clip_path_t *clip_path) +{ + cairo_int_status_t status; + cairo_region_t *prev = NULL; + + if (clip_path->flags & + (CAIRO_CLIP_PATH_HAS_REGION | + CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED)) + { + return clip_path->flags & CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED ? + CAIRO_INT_STATUS_UNSUPPORTED : + CAIRO_STATUS_SUCCESS; + } + + if (! clip_path->path.maybe_fill_region) + return _cairo_clip_path_to_region_geometric (clip_path); + + /* first retrieve the region for our antecedents */ + if (clip_path->prev != NULL) { + status = _cairo_clip_path_to_region (clip_path->prev); + if (status) { + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + return _cairo_clip_path_to_region_geometric (clip_path); + + return status; + } + + prev = clip_path->prev->region; + } + + /* now extract the region for ourselves */ + clip_path->region = + _cairo_path_fixed_fill_rectilinear_to_region (&clip_path->path, + clip_path->fill_rule, + &clip_path->extents); + assert (clip_path->region != NULL); + + status = clip_path->region->status; + if (unlikely (status)) + return status; + + if (prev != NULL) { + status = cairo_region_intersect (clip_path->region, prev); + if (unlikely (status)) + return status; + } + + clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION; + return CAIRO_STATUS_SUCCESS; +} + +static inline int +pot (int v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +/* XXX there is likely a faster method! ;-) */ +static cairo_status_t +_region_clip_to_boxes (const cairo_region_t *region, + cairo_box_t **boxes, + int *num_boxes, + int *size_boxes) +{ + cairo_traps_t traps; + cairo_status_t status; + int n, num_rects; + + _cairo_traps_init (&traps); + _cairo_traps_limit (&traps, *boxes, *num_boxes); + traps.is_rectilinear = TRUE; + traps.is_rectangular = TRUE; + + num_rects = cairo_region_num_rectangles (region); + for (n = 0; n < num_rects; n++) { + cairo_rectangle_int_t rect; + cairo_point_t p1, p2; + + cairo_region_get_rectangle (region, n, &rect); + + p1.x = _cairo_fixed_from_int (rect.x); + p1.y = _cairo_fixed_from_int (rect.y); + p2.x = _cairo_fixed_from_int (rect.x + rect.width); + p2.y = _cairo_fixed_from_int (rect.y + rect.height); + + status = _cairo_traps_tessellate_rectangle (&traps, &p1, &p2); + if (unlikely (status)) + goto CLEANUP; + } + + status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&traps, CAIRO_FILL_RULE_WINDING); + if (unlikely (status)) + goto CLEANUP; + + n = *size_boxes; + if (n < 0) + n = -n; + + if (traps.num_traps > n) { + cairo_box_t *new_boxes; + + new_boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t)); + if (unlikely (new_boxes == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP; + } + + if (*size_boxes > 0) + free (*boxes); + + *boxes = new_boxes; + *size_boxes = traps.num_traps; + } + + for (n = 0; n < traps.num_traps; n++) { + (*boxes)[n].p1.x = traps.traps[n].left.p1.x; + (*boxes)[n].p1.y = traps.traps[n].top; + (*boxes)[n].p2.x = traps.traps[n].right.p1.x; + (*boxes)[n].p2.y = traps.traps[n].bottom; + } + *num_boxes = n; + + CLEANUP: _cairo_traps_fini (&traps); return status; } -void -_cairo_clip_translate (cairo_clip_t *clip, - cairo_fixed_t tx, - cairo_fixed_t ty) -{ - if (clip->all_clipped) - return; - - if (clip->region) { - cairo_region_translate (clip->region, - _cairo_fixed_integer_part (tx), - _cairo_fixed_integer_part (ty)); - } - - if (clip->surface) { - clip->surface_rect.x += _cairo_fixed_integer_part (tx); - clip->surface_rect.y += _cairo_fixed_integer_part (ty); - } - - if (clip->path) { - cairo_clip_path_t *clip_path = clip->path; - cairo_matrix_t matrix; - - cairo_matrix_init_translate (&matrix, - _cairo_fixed_to_double (tx), - _cairo_fixed_to_double (ty)); - - while (clip_path) { - _cairo_path_fixed_transform (&clip_path->path, &matrix); - clip_path = clip_path->prev; - } - } -} - static cairo_status_t -_cairo_clip_path_reapply_clip_path (cairo_clip_t *clip, - cairo_clip_path_t *clip_path) +_rectilinear_clip_to_boxes (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + cairo_box_t **boxes, + int *num_boxes, + int *size_boxes) { + cairo_polygon_t polygon; + cairo_traps_t traps; cairo_status_t status; - if (clip_path->prev) { - status = _cairo_clip_path_reapply_clip_path (clip, clip_path->prev); - if (_cairo_status_is_error (status)) + _cairo_traps_init (&traps); + _cairo_traps_limit (&traps, *boxes, *num_boxes); + + _cairo_polygon_init (&polygon); + _cairo_polygon_limit (&polygon, *boxes, *num_boxes); + + status = _cairo_path_fixed_fill_rectilinear_to_traps (path, + fill_rule, + &traps); + if (unlikely (_cairo_status_is_error (status))) + goto CLEANUP; + if (status == CAIRO_STATUS_SUCCESS) + goto BOXES; + + /* tolerance will be ignored as the path is rectilinear */ + status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon); + if (unlikely (status)) + goto CLEANUP; + + if (polygon.num_edges == 0) { + *num_boxes = 0; + } else { + status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps, + &polygon, + fill_rule); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + int i; + + BOXES: + i = *size_boxes; + if (i < 0) + i = -i; + + if (traps.num_traps > i) { + cairo_box_t *new_boxes; + int new_size; + + new_size = pot (traps.num_traps); + new_boxes = _cairo_malloc_ab (new_size, sizeof (cairo_box_t)); + if (unlikely (new_boxes == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP; + } + + if (*size_boxes > 0) + free (*boxes); + + *boxes = new_boxes; + *size_boxes = new_size; + } + + for (i = 0; i < traps.num_traps; i++) { + (*boxes)[i].p1.x = traps.traps[i].left.p1.x; + (*boxes)[i].p1.y = traps.traps[i].top; + (*boxes)[i].p2.x = traps.traps[i].right.p1.x; + (*boxes)[i].p2.y = traps.traps[i].bottom; + } + *num_boxes = i; + } + } + + CLEANUP: + _cairo_polygon_fini (&polygon); + _cairo_traps_fini (&traps); + + return status; +} + +static cairo_int_status_t +_cairo_clip_path_to_boxes (cairo_clip_path_t *clip_path, + cairo_box_t **boxes, + int *count) +{ + int size = -*count; + int num_boxes = 0; + cairo_status_t status; + + if (clip_path->region != NULL) { + int num_rects, n; + + num_rects = cairo_region_num_rectangles (clip_path->region); + if (num_rects > -size) { + cairo_box_t *new_boxes; + + new_boxes = _cairo_malloc_ab (num_rects, sizeof (cairo_box_t)); + if (unlikely (new_boxes == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + *boxes = new_boxes; + } + + for (n = 0; n < num_rects; n++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (clip_path->region, n, &rect); + (*boxes)[n].p1.x = _cairo_fixed_from_int (rect.x); + (*boxes)[n].p1.y = _cairo_fixed_from_int (rect.y); + (*boxes)[n].p2.x = _cairo_fixed_from_int (rect.x + rect.width); + (*boxes)[n].p2.y = _cairo_fixed_from_int (rect.y + rect.height); + } + + *count = num_rects; + return CAIRO_STATUS_SUCCESS; + } + + /* keep it simple at first */ + if (! _clip_paths_are_rectilinear (clip_path)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + assert (-size >= 1); + if (_cairo_path_fixed_is_box (&clip_path->path, *boxes)) { + num_boxes = 1; + } else { + status = _rectilinear_clip_to_boxes (&clip_path->path, + clip_path->fill_rule, + boxes, &num_boxes, &size); + if (unlikely (status)) return status; } - return _cairo_clip_intersect_path (clip, - &clip_path->path, - clip_path->fill_rule, - clip_path->tolerance, - clip_path->antialias); + while (num_boxes > 0 && (clip_path = clip_path->prev) != NULL) { + cairo_box_t box; + + if (clip_path->region != NULL) { + status = _region_clip_to_boxes (clip_path->region, + boxes, &num_boxes, &size); + if (unlikely (status)) + return status; + + break; + } else if (_cairo_path_fixed_is_box (&clip_path->path, &box)) { + int i, j; + + for (i = j = 0; i < num_boxes; i++) { + if (j != i) + (*boxes)[j] = (*boxes)[i]; + + if (box.p1.x > (*boxes)[j].p1.x) + (*boxes)[j].p1.x = box.p1.x; + if (box.p2.x < (*boxes)[j].p2.x) + (*boxes)[j].p2.x = box.p2.x; + + if (box.p1.y > (*boxes)[j].p1.y) + (*boxes)[j].p1.y = box.p1.y; + if (box.p2.y < (*boxes)[j].p2.y) + (*boxes)[j].p2.y = box.p2.y; + + j += (*boxes)[j].p2.x > (*boxes)[j].p1.x && + (*boxes)[j].p2.y > (*boxes)[j].p1.y; + } + + num_boxes = j; + } else { + status = _rectilinear_clip_to_boxes (&clip_path->path, + clip_path->fill_rule, + boxes, &num_boxes, &size); + if (unlikely (status)) + return status; + } + } + + *count = num_boxes; + return CAIRO_STATUS_SUCCESS; } -cairo_status_t -_cairo_clip_init_deep_copy (cairo_clip_t *clip, - cairo_clip_t *other, - cairo_surface_t *target) +static cairo_status_t +_combine_region (cairo_surface_t *surface, + const cairo_region_t *region, + const cairo_rectangle_int_t *extents) { + cairo_region_t clear_region; cairo_status_t status; - _cairo_clip_init (clip, target); + _cairo_region_init_rectangle (&clear_region, extents); + status = cairo_region_subtract (&clear_region, region); + if (unlikely (status)) + return status; - if (other->mode != clip->mode) { - /* We should reapply the original clip path in this case, and let - * whatever the right handling is happen */ - } else { - if (other->region) { - clip->region = cairo_region_copy (other->region); - if (unlikely ((status = cairo_region_status (clip->region)))) - goto BAIL; - } + if (! cairo_region_is_empty (&clear_region)) { + cairo_region_translate (&clear_region, -extents->x, -extents->y); + status = _cairo_surface_fill_region (surface, + CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + &clear_region); + } + _cairo_region_fini (&clear_region); - if (other->surface) { - int dx, dy; - status = _cairo_surface_clone_similar (target, other->surface, + return status; +} + +static cairo_surface_t * +_cairo_clip_path_get_surface (cairo_clip_path_t *clip_path, + cairo_surface_t *target) +{ + cairo_surface_t *surface; + cairo_pattern_union_t pattern; + cairo_status_t status; + const cairo_rectangle_int_t *clip_extents = &clip_path->extents; + cairo_clip_path_t *prev; + cairo_bool_t need_translate; + + if (clip_path->surface != NULL && + clip_path->surface->backend == target->backend) + { + return cairo_surface_reference (clip_path->surface); + } + + surface = _cairo_surface_create_similar_solid (target, CAIRO_CONTENT_ALPHA, - 0, - 0, - other->surface_rect.width, - other->surface_rect.height, - &dx, &dy, - &clip->surface); + clip_extents->width, + clip_extents->height, + CAIRO_COLOR_TRANSPARENT, + FALSE); + if (surface == NULL) { + if (clip_path->surface != NULL && + clip_path->surface->backend == &_cairo_image_surface_backend) + { + return cairo_surface_reference (clip_path->surface); + } + + surface = + _cairo_image_surface_create_with_content (CAIRO_CONTENT_ALPHA, + clip_extents->width, + clip_extents->height); + } + if (unlikely (surface->status)) + return surface; + + _cairo_pattern_init_solid (&pattern.solid, + CAIRO_COLOR_WHITE, + CAIRO_CONTENT_COLOR); + + status = _cairo_clip_path_to_region (clip_path); + if (unlikely (_cairo_status_is_error (status))) + goto BAIL; + + need_translate = clip_extents->x | clip_extents->y; + if (status == CAIRO_STATUS_SUCCESS) { + if (need_translate) { + cairo_region_translate (clip_path->region, + -clip_extents->x, -clip_extents->y); + } + status = _cairo_surface_fill_region (surface, + CAIRO_OPERATOR_SOURCE, + CAIRO_COLOR_WHITE, + clip_path->region); + if (need_translate) { + cairo_region_translate (clip_path->region, + clip_extents->x, clip_extents->y); + } + if (unlikely (status)) + goto BAIL; + + goto DONE; + } else { + if (need_translate) { + _cairo_path_fixed_translate (&clip_path->path, + _cairo_fixed_from_int (-clip_extents->x), + _cairo_fixed_from_int (-clip_extents->y)); + } + status = _cairo_surface_fill (surface, + CAIRO_OPERATOR_OVER, + &pattern.base, + &clip_path->path, + clip_path->fill_rule, + clip_path->tolerance, + clip_path->antialias, + NULL); + if (need_translate) { + _cairo_path_fixed_translate (&clip_path->path, + _cairo_fixed_from_int (clip_extents->x), + _cairo_fixed_from_int (clip_extents->y)); + } + + if (unlikely (status)) + goto BAIL; + } + + prev = clip_path->prev; + NEXT_PATH: + if (prev != NULL) { + status = _cairo_clip_path_to_region (prev); + if (unlikely (_cairo_status_is_error (status))) + goto BAIL; + + if (status == CAIRO_STATUS_SUCCESS) { + status = _combine_region (surface, prev->region, clip_extents); + if (unlikely (status)) + goto BAIL; + } else if (prev->flags & CAIRO_CLIP_PATH_IS_BOX) { + /* a simple box only affects the extents */ + } else if (prev->path.is_rectilinear) { + if (need_translate) { + _cairo_path_fixed_translate (&prev->path, + _cairo_fixed_from_int (-clip_extents->x), + _cairo_fixed_from_int (-clip_extents->y)); + } + status = _cairo_surface_fill (surface, + CAIRO_OPERATOR_IN, + &pattern.base, + &prev->path, + prev->fill_rule, + prev->tolerance, + prev->antialias, + NULL); + if (need_translate) { + _cairo_path_fixed_translate (&prev->path, + _cairo_fixed_from_int (clip_extents->x), + _cairo_fixed_from_int (clip_extents->y)); + } + if (unlikely (status)) goto BAIL; - clip->surface_rect = other->surface_rect; + prev = prev->prev; + goto NEXT_PATH; + } else { + cairo_surface_t *prev_surface; - /* src offset was 0, so we expect an exact replica of the surface */ - assert (dx == 0); - assert (dy == 0); - } + prev_surface = _cairo_clip_path_get_surface (prev, target); + _cairo_pattern_init_for_surface (&pattern.surface, prev_surface); + cairo_surface_destroy (prev_surface); - if (other->path) { - status = _cairo_clip_path_reapply_clip_path (clip, other->path); - if (_cairo_status_is_error (status)) + cairo_matrix_init_translate (&pattern.base.matrix, + -prev->extents.x + clip_extents->x, + -prev->extents.y + clip_extents->y); + status = _cairo_surface_paint (surface, + CAIRO_OPERATOR_IN, + &pattern.base, + NULL); + _cairo_pattern_fini (&pattern.base); + + if (unlikely (status)) goto BAIL; - } + } } + DONE: + cairo_surface_destroy (clip_path->surface); + return clip_path->surface = cairo_surface_reference (surface); + + BAIL: + cairo_surface_destroy (surface); + return _cairo_surface_create_in_error (status); +} + +void +_cairo_debug_print_clip (FILE *stream, cairo_clip_t *clip) +{ + cairo_clip_path_t *clip_path; + + if (clip == NULL) { + fprintf (stream, "no clip\n"); + return; + } + + if (clip->all_clipped) { + fprintf (stream, "clip: all-clipped\n"); + return; + } + + if (clip->path == NULL) { + fprintf (stream, "clip: empty\n"); + return; + } + + fprintf (stream, "clip:\n"); + + clip_path = clip->path; + do { + fprintf (stream, "path: has region? %s, has surface? %s: ", + clip_path->region == NULL ? "no" : "yes", + clip_path->surface == NULL ? "no" : "yes"); + _cairo_debug_print_path (stream, &clip_path->path); + fprintf (stream, "\n"); + } while ((clip_path = clip_path->prev) != NULL); +} + +cairo_surface_t * +_cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *target) +{ + assert (clip->path != NULL); + return _cairo_clip_path_get_surface (clip->path, target); +} + +cairo_status_t +_cairo_clip_combine_with_surface (cairo_clip_t *clip, + cairo_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + cairo_pattern_union_t pattern; + cairo_clip_path_t *clip_path = clip->path; + cairo_bool_t need_translate; + cairo_status_t status; + + assert (clip_path != NULL); + + if (clip_path->surface != NULL && + clip_path->surface->backend == dst->backend) + { + _cairo_pattern_init_for_surface (&pattern.surface, + clip_path->surface); + cairo_matrix_init_translate (&pattern.base.matrix, + extents->x - clip_path->extents.x, + extents->y - clip_path->extents.y); + status = _cairo_surface_paint (dst, + CAIRO_OPERATOR_IN, + &pattern.base, + NULL); + + _cairo_pattern_fini (&pattern.base); + + return status; + } + + _cairo_pattern_init_solid (&pattern.solid, + CAIRO_COLOR_WHITE, + CAIRO_CONTENT_COLOR); + + need_translate = extents->x | extents->y; + do { + status = _cairo_clip_path_to_region (clip_path); + if (unlikely (_cairo_status_is_error (status))) + return status; + + if (status == CAIRO_STATUS_SUCCESS) + return _combine_region (dst, clip_path->region, extents); + + if (clip_path->surface != NULL && + clip_path->surface->backend == dst->backend) + { + _cairo_pattern_init_for_surface (&pattern.surface, + clip_path->surface); + cairo_matrix_init_translate (&pattern.base.matrix, + extents->x - clip_path->extents.x, + extents->y - clip_path->extents.y); + status = _cairo_surface_paint (dst, + CAIRO_OPERATOR_IN, + &pattern.base, + NULL); + + _cairo_pattern_fini (&pattern.base); + + return status; + } + + if (clip_path->flags & CAIRO_CLIP_PATH_IS_BOX) { + cairo_region_t clip_region; + + _cairo_region_init_rectangle (&clip_region, &clip_path->extents); + status = _combine_region (dst, &clip_region, extents); + } else { + if (need_translate) { + _cairo_path_fixed_translate (&clip_path->path, + _cairo_fixed_from_int (-extents->x), + _cairo_fixed_from_int (-extents->y)); + } + status = _cairo_surface_fill (dst, + CAIRO_OPERATOR_IN, + &pattern.base, + &clip_path->path, + clip_path->fill_rule, + clip_path->tolerance, + clip_path->antialias, + NULL); + if (need_translate) { + _cairo_path_fixed_translate (&clip_path->path, + _cairo_fixed_from_int (extents->x), + _cairo_fixed_from_int (extents->y)); + } + } + + if (unlikely (status)) + return status; + } while ((clip_path = clip_path->prev) != NULL); + return CAIRO_STATUS_SUCCESS; +} -BAIL: - if (clip->region) - cairo_region_destroy (clip->region); - if (clip->surface) - cairo_surface_destroy (clip->surface); +const cairo_rectangle_int_t * +_cairo_clip_get_extents (const cairo_clip_t *clip) +{ + if (clip->path == NULL) + return NULL; - return status; + return &clip->path->extents; +} + +void +_cairo_clip_drop_cache (cairo_clip_t *clip) +{ + cairo_clip_path_t *clip_path; + + if (clip->path == NULL) + return; + + clip_path = clip->path; + do { + if (clip_path->region != NULL) { + cairo_region_destroy (clip_path->region); + clip_path->region = NULL; + } + + if (clip_path->surface != NULL) { + cairo_surface_destroy (clip_path->surface); + clip_path->surface = NULL; + } + + clip_path->flags &= ~CAIRO_CLIP_PATH_HAS_REGION; + } while ((clip_path = clip_path->prev) != NULL); } const cairo_rectangle_list_t _cairo_rectangles_nil = @@ -862,77 +1373,162 @@ _cairo_clip_int_rect_to_user (cairo_gstate_t *gstate, return is_tight; } +cairo_int_status_t +_cairo_clip_get_region (cairo_clip_t *clip, + cairo_region_t **region) +{ + cairo_int_status_t status; + + if (clip->all_clipped) + goto CLIPPED; + + assert (clip->path != NULL); + + status = _cairo_clip_path_to_region (clip->path); + if (status) + return status; + + if (cairo_region_is_empty (clip->path->region)) { + _cairo_clip_set_all_clipped (clip); + goto CLIPPED; + } + + if (region) + *region = clip->path->region; + return CAIRO_STATUS_SUCCESS; + + CLIPPED: + if (region) + *region = NULL; + return CAIRO_INT_STATUS_NOTHING_TO_DO; +} + +cairo_int_status_t +_cairo_clip_get_boxes (cairo_clip_t *clip, + cairo_box_t **boxes, + int *count) +{ + cairo_int_status_t status; + + if (clip->all_clipped) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + assert (clip->path != NULL); + + status = _cairo_clip_path_to_boxes (clip->path, boxes, count); + if (status) + return status; + + if (*count == 0) { + _cairo_clip_set_all_clipped (clip); + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_rectangle_list_t * +_cairo_rectangle_list_create_in_error (cairo_status_t status) +{ + cairo_rectangle_list_t *list; + + if (status == CAIRO_STATUS_NO_MEMORY) + return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; + if (status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE) + return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable; + + list = malloc (sizeof (*list)); + if (unlikely (list == NULL)) { + _cairo_error_throw (status); + return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; + } + + list->status = status; + list->rectangles = NULL; + list->num_rectangles = 0; + + return list; +} + cairo_rectangle_list_t * _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate) { +#define ERROR_LIST(S) _cairo_rectangle_list_create_in_error (_cairo_error (S)); + cairo_rectangle_list_t *list; cairo_rectangle_t *rectangles = NULL; + cairo_region_t *region = NULL; + cairo_int_status_t status; int n_rects = 0; + int i; - if (clip->all_clipped) - goto DONE; - - if (clip->path || clip->surface) { - _cairo_error_throw (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); - return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable; + if (clip != NULL && clip->path != NULL) { + status = _cairo_clip_get_region (clip, ®ion); + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) { + goto DONE; + } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE) + } else if (unlikely (status)) { + return ERROR_LIST (status); + } } - if (clip->region) { - int i; - - n_rects = cairo_region_num_rectangles (clip->region); - + if (region != NULL) { + n_rects = cairo_region_num_rectangles (region); if (n_rects) { rectangles = _cairo_malloc_ab (n_rects, sizeof (cairo_rectangle_t)); if (unlikely (rectangles == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; + return ERROR_LIST (CAIRO_STATUS_NO_MEMORY); } for (i = 0; i < n_rects; ++i) { cairo_rectangle_int_t clip_rect; - cairo_region_get_rectangle (clip->region, i, &clip_rect); - - if (!_cairo_clip_int_rect_to_user(gstate, &clip_rect, &rectangles[i])) { - _cairo_error_throw (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); + cairo_region_get_rectangle (region, i, &clip_rect); + + if (! _cairo_clip_int_rect_to_user (gstate, + &clip_rect, + &rectangles[i])) + { free (rectangles); - return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable; + return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); } } } } else { cairo_rectangle_int_t extents; - n_rects = 1; - - rectangles = malloc(sizeof (cairo_rectangle_t)); - if (unlikely (rectangles == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; + if (! _cairo_surface_get_extents (_cairo_gstate_get_target (gstate), + &extents)) + { + /* unbounded surface -> unclipped */ + goto DONE; } - if (_cairo_surface_get_extents (_cairo_gstate_get_target (gstate), &extents) || - !_cairo_clip_int_rect_to_user(gstate, &extents, rectangles)) - { - _cairo_error_throw (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); + n_rects = 1; + rectangles = malloc(sizeof (cairo_rectangle_t)); + if (unlikely (rectangles == NULL)) + return ERROR_LIST (CAIRO_STATUS_NO_MEMORY); + + if (! _cairo_clip_int_rect_to_user (gstate, &extents, rectangles)) { free (rectangles); - return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable; + return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); } } DONE: list = malloc (sizeof (cairo_rectangle_list_t)); if (unlikely (list == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); free (rectangles); - return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; + return ERROR_LIST (CAIRO_STATUS_NO_MEMORY); } list->status = CAIRO_STATUS_SUCCESS; list->rectangles = rectangles; list->num_rectangles = n_rects; return list; + +#undef ERROR_LIST } /** @@ -955,3 +1551,9 @@ cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list) free (rectangle_list->rectangles); free (rectangle_list); } + +void +_cairo_clip_reset_static_data (void) +{ + _freed_pool_reset (&clip_path_pool); +} diff --git a/gfx/cairo/cairo/src/cairo-combsort-private.h b/gfx/cairo/cairo/src/cairo-combsort-private.h index d2fbb72b9ead..ce31257ececd 100644 --- a/gfx/cairo/cairo/src/cairo-combsort-private.h +++ b/gfx/cairo/cairo/src/cairo-combsort-private.h @@ -56,7 +56,7 @@ NAME (TYPE *base, unsigned int nmemb) \ int swapped; \ do { \ gap = _cairo_combsort_newgap (gap); \ - swapped = 0; \ + swapped = gap > 1; \ for (i = 0; i < nmemb-gap ; i++) { \ j = i + gap; \ if (CMP (base[i], base[j]) > 0 ) { \ @@ -67,5 +67,5 @@ NAME (TYPE *base, unsigned int nmemb) \ swapped = 1; \ } \ } \ - } while (gap > 1 || swapped); \ + } while (swapped); \ } diff --git a/gfx/cairo/cairo/src/cairo-d2d-private.h b/gfx/cairo/cairo/src/cairo-d2d-private.h index 16f29bd98fb2..4cff0a805406 100644 --- a/gfx/cairo/cairo/src/cairo-d2d-private.h +++ b/gfx/cairo/cairo/src/cairo-d2d-private.h @@ -45,6 +45,7 @@ extern "C" { #include "cairoint.h" +#include "cairo-surface-clipper-private.h" } struct _cairo_d2d_surface { @@ -91,6 +92,8 @@ struct _cairo_d2d_surface { bool isDrawing; /** Indicates if text rendering is initialized */ bool textRenderingInit; + + cairo_surface_clipper_t clipper; }; typedef struct _cairo_d2d_surface cairo_d2d_surface_t; @@ -217,5 +220,8 @@ _cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf, unsigned int *runs_remaining, bool *pushed_clip, bool unique = false); +void +_cairo_d2d_begin_draw_state(cairo_d2d_surface_t *d2dsurf); + #endif /* CAIRO_HAS_D2D_SURFACE */ #endif /* CAIRO_D2D_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp index 4e6a86225db7..7a03cf9b105c 100644 --- a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp +++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp @@ -42,6 +42,7 @@ extern "C" { #include "cairo-win32.h" #include "cairo-analysis-surface-private.h" +#include "cairo-surface-clipper-private.h" } @@ -159,7 +160,7 @@ _cairo_d2d_flush(void *surface); * \param fill_rule The fill rule to uses on the path * \param tolerance The tolerance applied to the filling * \param antialias The anti-alias mode to use - * \param extents The extents of the surface to which to apply this operation + * \param clip The clip of this operation * \return Return code, this can be CAIRO_ERROR_SURFACE_TYPE_MISMATCH, * CAIRO_INT_STATUS_UNSUPPORTED or CAIRO_STATUS_SUCCESS */ @@ -171,17 +172,16 @@ _cairo_d2d_fill(void *surface, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); /** * Paint this surface, applying the operation to the entire surface - * or to the passed in 'extents' of the surface. * * \param surface The surface to apply this operation to, must be * a D2D surface * \param op Operator to use when painting * \param source The pattern to fill this surface with, source of the op - * \param Extents of the surface to apply this operation to + * \param clip The clip of this operation * \return Return code, this can be CAIRO_ERROR_SURFACE_TYPE_MISMATCH, * CAIRO_INT_STATUS_UNSUPPORTED or CAIRO_STATUS_SUCCESS */ @@ -189,7 +189,7 @@ static cairo_int_status_t _cairo_d2d_paint(void *surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); /** * Paint something on the surface applying a certain mask to that @@ -200,7 +200,7 @@ _cairo_d2d_paint(void *surface, * \param op Operator to use * \param source Source for this operation * \param mask Pattern to mask source with - * \param extents Extents of the surface to apply this operation to + * \param clip The clip of this operation * \return Return code, this can be CAIRO_ERROR_SURFACE_TYPE_MISMATCH, * CAIRO_INT_STATUS_UNSUPPORTED or CAIRO_STATUS_SUCCESS */ @@ -209,7 +209,7 @@ _cairo_d2d_mask(void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); /** * Show a glyph run on the target D2D surface. @@ -223,7 +223,7 @@ _cairo_d2d_mask(void *surface, * \param scaled_font Scaled font to draw * \param remaining_glyphs Pointer to store amount of glyphs still * requiring drawing. - * \param extents Extents this operation applies to. + * \param clip The clip of this operation * \return CAIRO_ERROR_SURFACE_TYPE_MISMATCH, CAIRO_ERROR_FONT_TYPE_MISMATCH, * CAIRO_INT_STATUS_UNSUPPORTED or CAIRO_STATUS_SUCCESS */ @@ -234,8 +234,8 @@ _cairo_d2d_show_glyphs (void *surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip, + int *remaining_glyphs); /** * Get the extents of this surface. @@ -244,20 +244,17 @@ _cairo_d2d_show_glyphs (void *surface, * \param extents Pointer to where to store the extents * \param CAIRO_ERROR_SURFACE_TYPE_MISTMATCH or CAIRO_STATUS_SUCCESS */ -static cairo_int_status_t +static cairo_bool_t _cairo_d2d_getextents(void *surface, cairo_rectangle_int_t *extents); -/** - * See cairo backend documentation. - */ -static cairo_int_status_t -_cairo_d2d_intersect_clip_path(void *dst, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias); +static cairo_status_t +_cairo_d2d_surface_clipper_intersect_clip_path(cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias); /** * Stroke a path on this D2D surface. @@ -275,7 +272,7 @@ _cairo_d2d_intersect_clip_path(void *dst, * to logical space. * \param tolerance Tolerance to stroke with * \param antialias Antialias mode to use - * \param extents Extents of the surface to apply this operation to + * \param clip The clip of this operation * \return Return code, this can be CAIRO_ERROR_SURFACE_TYPE_MISMATCH, * CAIRO_INT_STATUS_UNSUPPORTED or CAIRO_STATUS_SUCCESS */ @@ -289,7 +286,7 @@ _cairo_d2d_stroke(void *surface, cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); static const cairo_surface_backend_t cairo_d2d_surface_backend = { CAIRO_SURFACE_TYPE_D2D, @@ -307,9 +304,7 @@ static const cairo_surface_backend_t cairo_d2d_surface_backend = { NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ - NULL, /* set_clip_region */ - _cairo_d2d_intersect_clip_path, /* intersect_clip_path */ - _cairo_d2d_getextents, /* getextents */ + _cairo_d2d_getextents, /* get_extents */ NULL, /* old_show_glyphs */ NULL, /* get_font_options */ _cairo_d2d_flush, /* flush */ @@ -322,7 +317,6 @@ static const cairo_surface_backend_t cairo_d2d_surface_backend = { _cairo_d2d_fill, /* fill */ _cairo_d2d_show_glyphs, /* show_glyphs */ NULL, /* snapshot */ - NULL, NULL }; @@ -502,6 +496,15 @@ static void _begin_draw_state(cairo_d2d_surface_t* surface) } } +/* External helper called from dwrite code. + * Will hopefully go away when/if that code + * moves into here */ +void +_cairo_d2d_begin_draw_state(cairo_d2d_surface_t *d2dsurf) +{ + _begin_draw_state(d2dsurf); +} + /** * Get a D2D matrix from a cairo matrix. Note that D2D uses row vectors where cairo * uses column vectors. Hence the transposition. @@ -1285,6 +1288,7 @@ _cairo_d2d_create_similar(void *surface, memset(newSurf, 0, sizeof(cairo_d2d_surface_t)); _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, content); + _cairo_surface_clipper_init(&newSurf->clipper, _cairo_d2d_surface_clipper_intersect_clip_path); D2D1_SIZE_U sizePixels; D2D1_SIZE_F size; @@ -1391,6 +1395,9 @@ static cairo_status_t _cairo_d2d_finish(void *surface) { cairo_d2d_surface_t *d2dsurf = static_cast(surface); + + _cairo_surface_clipper_reset (&d2dsurf->clipper); + if (d2dsurf->rt) { d2dsurf->rt->Release(); } @@ -1430,6 +1437,7 @@ _cairo_d2d_finish(void *surface) if (d2dsurf->bufferTexture) { d2dsurf->bufferTexture->Release(); } + return CAIRO_STATUS_SUCCESS; } @@ -1588,14 +1596,14 @@ _cairo_d2d_release_dest_image(void *abstract_surface, softTexture->Release(); } -cairo_int_status_t -_cairo_d2d_intersect_clip_path(void *dst, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) +static cairo_status_t +_cairo_d2d_surface_clipper_intersect_clip_path(cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) { - cairo_d2d_surface_t *d2dsurf = static_cast(dst); + cairo_d2d_surface_t *d2dsurf = cairo_container_of (clipper, cairo_d2d_surface_t, clipper); _cairo_d2d_surface_pop_clip(d2dsurf); if (!path) { @@ -1607,7 +1615,7 @@ _cairo_d2d_intersect_clip_path(void *dst, delete d2dsurf->clipRect; d2dsurf->clipRect = NULL; } - return CAIRO_INT_STATUS_SUCCESS; + return CAIRO_STATUS_SUCCESS; } cairo_box_t box; @@ -1618,11 +1626,12 @@ _cairo_d2d_intersect_clip_path(void *dst, */ if (!d2dsurf->clipRect && !d2dsurf->clipMask) { /** Nothing yet, just use this clip rect */ + //XXX: unchecked allocation d2dsurf->clipRect = new D2D1_RECT_F(D2D1::RectF(_cairo_fixed_to_float(box.p1.x), _cairo_fixed_to_float(box.p1.y), _cairo_fixed_to_float(box.p2.x), _cairo_fixed_to_float(box.p2.y))); - return CAIRO_INT_STATUS_SUCCESS; + return CAIRO_STATUS_SUCCESS; } else if (!d2dsurf->clipMask) { /** We have a clip rect, intersect of two rects is simple */ d2dsurf->clipRect->top = max(_cairo_fixed_to_float(box.p1.y), d2dsurf->clipRect->top); @@ -1635,7 +1644,7 @@ _cairo_d2d_intersect_clip_path(void *dst, if (d2dsurf->clipRect->left > d2dsurf->clipRect->right) { d2dsurf->clipRect->left = d2dsurf->clipRect->right; } - return CAIRO_INT_STATUS_SUCCESS; + return CAIRO_STATUS_SUCCESS; } else { /** * We have a mask, see if this rect is completely contained by it, so we @@ -1653,11 +1662,12 @@ _cairo_d2d_intersect_clip_path(void *dst, d2dsurf->clipMask->Release(); d2dsurf->clipMask = NULL; newMask->Release(); + //XXX: unchecked allocation d2dsurf->clipRect = new D2D1_RECT_F(D2D1::RectF(_cairo_fixed_to_float(box.p1.x), _cairo_fixed_to_float(box.p1.y), _cairo_fixed_to_float(box.p2.x), _cairo_fixed_to_float(box.p2.y))); - return CAIRO_INT_STATUS_SUCCESS; + return CAIRO_STATUS_SUCCESS; } newMask->Release(); @@ -1697,7 +1707,7 @@ _cairo_d2d_intersect_clip_path(void *dst, if (relation == D2D1_GEOMETRY_RELATION_CONTAINS) { currentMask->Release(); newMask->Release(); - return CAIRO_INT_STATUS_SUCCESS; + return CAIRO_STATUS_SUCCESS; } else if (relation == D2D1_GEOMETRY_RELATION_IS_CONTAINED) { currentMask->Release(); d2dsurf->clipMask = newMask; @@ -1723,7 +1733,7 @@ _cairo_d2d_intersect_clip_path(void *dst, d2dsurf->clipRect = NULL; } - return CAIRO_INT_STATUS_SUCCESS; + return CAIRO_STATUS_SUCCESS; } static cairo_status_t @@ -1743,9 +1753,15 @@ static cairo_int_status_t _cairo_d2d_paint(void *surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_d2d_surface_t *d2dsurf = static_cast(surface); + cairo_int_status_t status; + + status = (cairo_int_status_t)_cairo_surface_clipper_set_clip (&d2dsurf->clipper, clip); + if (unlikely(status)) + return status; + _begin_draw_state(d2dsurf); op = _cairo_d2d_simplify_operator(op, source); @@ -1771,20 +1787,14 @@ _cairo_d2d_paint(void *surface, if (!brush) { return CAIRO_INT_STATUS_UNSUPPORTED; } - if (op == CAIRO_OPERATOR_OVER && extents) { - d2dsurf->rt->FillRectangle(D2D1::RectF((FLOAT)extents->x, - (FLOAT)extents->y, - (FLOAT)extents->x + extents->width, - (FLOAT)extents->y + extents->height), - brush); - } else if (op == CAIRO_OPERATOR_OVER) { + if (op == CAIRO_OPERATOR_OVER) { D2D1_SIZE_F size = d2dsurf->rt->GetSize(); d2dsurf->rt->FillRectangle(D2D1::RectF((FLOAT)0, (FLOAT)0, (FLOAT)size.width, (FLOAT)size.height), brush); - } else if (op == CAIRO_OPERATOR_SOURCE && !extents) { + } else if (op == CAIRO_OPERATOR_SOURCE) { D2D1_SIZE_F size = d2dsurf->rt->GetSize(); d2dsurf->rt->Clear(D2D1::ColorF(0, 0)); d2dsurf->rt->FillRectangle(D2D1::RectF((FLOAT)0, @@ -1810,13 +1820,28 @@ _cairo_d2d_mask(void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_d2d_surface_t *d2dsurf = static_cast(surface); - _begin_draw_state(d2dsurf); + cairo_rectangle_int_t extents; unsigned int runs_remaining = 0; bool pushed_clip; + cairo_int_status_t status; + + status = (cairo_int_status_t)_cairo_surface_clipper_set_clip (&d2dsurf->clipper, clip); + if (unlikely (status)) + return status; + + _begin_draw_state(d2dsurf); + + status = (cairo_int_status_t)_cairo_surface_mask_extents (&d2dsurf->base, + op, source, + mask, + clip, &extents); + if (unlikely (status)) + return status; + ID2D1Brush *brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, source, 0, &runs_remaining, &pushed_clip); if (!brush) { @@ -1831,12 +1856,12 @@ _cairo_d2d_mask(void *surface, 0, (FLOAT)d2dsurf->rt->GetPixelSize().width, (FLOAT)d2dsurf->rt->GetPixelSize().height); - if (extents) { - rect.left = (FLOAT)extents->x; - rect.right = (FLOAT)(extents->x + extents->width); - rect.top = (FLOAT)extents->y; - rect.bottom = (FLOAT)(extents->y + extents->height); - } + + rect.left = (FLOAT)extents.x; + rect.right = (FLOAT)(extents.x + extents.width); + rect.top = (FLOAT)extents.y; + rect.bottom = (FLOAT)(extents.y + extents.height); + if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_solid_pattern_t *solidPattern = @@ -1889,10 +1914,11 @@ _cairo_d2d_stroke(void *surface, cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { + cairo_int_status_t status; + cairo_d2d_surface_t *d2dsurf = static_cast(surface); - _begin_draw_state(d2dsurf); op = _cairo_d2d_simplify_operator(op, source); @@ -1906,6 +1932,12 @@ _cairo_d2d_stroke(void *surface, return CAIRO_INT_STATUS_UNSUPPORTED; } + status = (cairo_int_status_t)_cairo_surface_clipper_set_clip (&d2dsurf->clipper, clip); + if (unlikely(status)) + return status; + + _begin_draw_state(d2dsurf); + if (antialias == CAIRO_ANTIALIAS_NONE) { d2dsurf->rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); } else { @@ -2018,13 +2050,11 @@ _cairo_d2d_fill(void *surface, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { - if (((cairo_surface_t*)surface)->type != CAIRO_SURFACE_TYPE_D2D) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } + cairo_int_status_t status; + cairo_d2d_surface_t *d2dsurf = static_cast(surface); - _begin_draw_state(d2dsurf); op = _cairo_d2d_simplify_operator(op, source); @@ -2038,6 +2068,12 @@ _cairo_d2d_fill(void *surface, return CAIRO_INT_STATUS_UNSUPPORTED; } + status = (cairo_int_status_t)_cairo_surface_clipper_set_clip (&d2dsurf->clipper, clip); + if (unlikely (status)) + return status; + + _begin_draw_state(d2dsurf); + if (antialias == CAIRO_ANTIALIAS_NONE) { d2dsurf->rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); } else { @@ -2118,6 +2154,7 @@ _cairo_d2d_fill(void *surface, return CAIRO_INT_STATUS_SUCCESS; } + static cairo_int_status_t _cairo_d2d_show_glyphs (void *surface, cairo_operator_t op, @@ -2125,8 +2162,8 @@ _cairo_d2d_show_glyphs (void *surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip, + int *remaining_glyphs) { if (((cairo_surface_t*)surface)->type != CAIRO_SURFACE_TYPE_D2D) { return CAIRO_INT_STATUS_UNSUPPORTED; @@ -2139,30 +2176,27 @@ _cairo_d2d_show_glyphs (void *surface, d2dsurf->textRenderingInit = true; params->Release(); } - _begin_draw_state(d2dsurf); cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; if (scaled_font->backend->type == CAIRO_FONT_TYPE_DWRITE) { status = (cairo_int_status_t) - cairo_dwrite_show_glyphs_on_d2d_surface(surface, op, source, glyphs, num_glyphs, scaled_font, extents); + _cairo_dwrite_show_glyphs_on_d2d_surface(surface, op, source, glyphs, num_glyphs, scaled_font, clip); } return status; } -static cairo_int_status_t + +static cairo_bool_t _cairo_d2d_getextents(void *surface, cairo_rectangle_int_t *extents) { - if (((cairo_surface_t*)surface)->type != CAIRO_SURFACE_TYPE_D2D) { - return (cairo_int_status_t)CAIRO_STATUS_SURFACE_TYPE_MISMATCH; - } cairo_d2d_surface_t *d2dsurf = static_cast(surface); extents->x = 0; extents->y = 0; D2D1_SIZE_U size = d2dsurf->rt->GetPixelSize(); extents->width = size.width; extents->height = size.height; - return CAIRO_INT_STATUS_SUCCESS; + return TRUE; } @@ -2183,6 +2217,7 @@ cairo_d2d_surface_create_for_hwnd(HWND wnd) memset(newSurf, 0, sizeof(cairo_d2d_surface_t)); _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, CAIRO_CONTENT_COLOR); + _cairo_surface_clipper_init(&newSurf->clipper, _cairo_d2d_surface_clipper_intersect_clip_path); RECT rc; HRESULT hr; @@ -2318,6 +2353,9 @@ cairo_d2d_surface_create(cairo_format_t format, _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, CAIRO_CONTENT_ALPHA); dxgiformat = DXGI_FORMAT_A8_UNORM; } + + _cairo_surface_clipper_init(&newSurf->clipper, _cairo_d2d_surface_clipper_intersect_clip_path); + newSurf->format = format; D2D1_SIZE_U sizePixels; diff --git a/gfx/cairo/cairo/src/cairo-ddraw-private.h b/gfx/cairo/cairo/src/cairo-ddraw-private.h index 85a5d603bad4..2d854008043a 100644 --- a/gfx/cairo/cairo/src/cairo-ddraw-private.h +++ b/gfx/cairo/cairo/src/cairo-ddraw-private.h @@ -37,6 +37,7 @@ #include "cairo-ddraw.h" #include "cairoint.h" +#include "cairo-region-private.h" #ifdef CAIRO_DDRAW_USE_GL #include diff --git a/gfx/cairo/cairo/src/cairo-ddraw-surface.c b/gfx/cairo/cairo/src/cairo-ddraw-surface.c index 9a6aa3527d88..dce71d866cbb 100644 --- a/gfx/cairo/cairo/src/cairo-ddraw-surface.c +++ b/gfx/cairo/cairo/src/cairo-ddraw-surface.c @@ -241,28 +241,6 @@ _cairo_ddraw_check_ogl_error (const char *context) #endif /* CAIRO_DDRAW_USE_GL */ -static cairo_status_t -_cairo_ddraw_surface_set_image_clip (cairo_ddraw_surface_t *surface) -{ - if (surface->image_clip_invalid) { - surface->image_clip_invalid = FALSE; - if (surface->has_clip_region) { - unsigned int serial = - _cairo_surface_allocate_clip_serial (surface->image); - surface->has_image_clip = TRUE; - return _cairo_surface_set_clip_region (surface->image, - &surface->clip_region, - serial); - } else { - surface->has_image_clip = FALSE; - return _cairo_surface_set_clip_region (surface->image, - NULL, 0); - } - } - - return CAIRO_STATUS_SUCCESS; -} - static cairo_status_t _cairo_ddraw_surface_set_clip_list (cairo_ddraw_surface_t * surface) { @@ -371,10 +349,7 @@ _cairo_ddraw_surface_lock (cairo_ddraw_surface_t *surface) if (surface->image->status) return surface->image->status; - surface->has_image_clip = FALSE; - surface->image_clip_invalid = surface->has_clip_region; - - return _cairo_ddraw_surface_set_image_clip (surface); + return CAIRO_STATUS_SUCCESS; } if (surface->locked) @@ -408,10 +383,7 @@ _cairo_ddraw_surface_lock (cairo_ddraw_surface_t *surface) if (surface->image->status) return surface->image->status; - surface->has_image_clip = FALSE; - surface->image_clip_invalid = surface->has_clip_region; - - return _cairo_ddraw_surface_set_image_clip (surface); + return CAIRO_STATUS_SUCCESS; } static inline cairo_status_t @@ -464,6 +436,8 @@ _cairo_ddraw_surface_reset_clipper (cairo_ddraw_surface_t *surface) return CAIRO_STATUS_SUCCESS; } + + #ifdef CAIRO_DDRAW_USE_GL #define CAIRO_DDRAW_API_ENTRY_STATUS \ do { \ @@ -572,7 +546,7 @@ _cairo_ddraw_surface_flush (void *abstract_surface) cairo_status_t _cairo_ddraw_surface_reset (cairo_surface_t *surface) { - return _cairo_surface_reset_clip (surface); + return _cairo_ddraw_surface_set_clip_region (surface, NULL); } static cairo_surface_t * @@ -2477,8 +2451,8 @@ _cairo_ddraw_surface_show_glyphs (void *abstract_dst, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip, + int *remaining_glyphs) { cairo_ddraw_surface_t * dst = (cairo_ddraw_surface_t *) abstract_dst; cairo_color_t * color; @@ -2502,10 +2476,17 @@ _cairo_ddraw_surface_show_glyphs (void *abstract_dst, if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) return CAIRO_INT_STATUS_UNSUPPORTED; - if (dst->base.clip && - (dst->base.clip->mode != CAIRO_CLIP_MODE_REGION || - dst->base.clip->surface != NULL)) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (clip != NULL) { + cairo_region_t *clip_region; + cairo_status_t status; + + status = _cairo_clip_get_region (clip, &clip_region); + assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); + if (status) + return status; + + _cairo_ddraw_surface_set_clip_region (surface, clip_region); + } color = &((cairo_solid_pattern_t *)pattern)->color; @@ -2711,7 +2692,8 @@ _cairo_ddraw_surface_composite (cairo_operator_t op, int dst_x, int dst_y, unsigned int width, - unsigned int height) + unsigned int height, + cairo_regiont_t *clip_region) { cairo_ddraw_surface_t * dst = (cairo_ddraw_surface_t *) abstract_dst; cairo_ddraw_ogl_program_info_t * info = NULL; @@ -2741,6 +2723,10 @@ _cairo_ddraw_surface_composite (cairo_operator_t op, if (op == CAIRO_OPERATOR_DEST) return CAIRO_STATUS_SUCCESS; + status = _cairo_ddraw_surface_set_clip_region (dst, clip_region); + if (status) + return status; + /* bail out for source copies that aren't ddraw surfaces) */ if (src->type == CAIRO_PATTERN_TYPE_SURFACE && (((cairo_surface_pattern_t *) src)->surface->type @@ -3136,9 +3122,6 @@ _cairo_ddraw_surface_acquire_dest_image (void *abstract_surfa return status; END_TIMER(acquiredst); - if ((status = _cairo_ddraw_surface_set_image_clip (surface))) - return status; - *image_out = (cairo_image_surface_t *) surface->image; *image_rect = surface->extents; *image_extra = NULL; @@ -3167,7 +3150,6 @@ _cairo_ddraw_surface_set_clip_region (void *abstract_surface, if (region) { cairo_region_t *tmp_region; surface->has_clip_region = TRUE; - surface->image_clip_invalid = TRUE; surface->clip_list_invalid = TRUE; tmp_region = &surface->clip_region; @@ -3180,7 +3162,6 @@ _cairo_ddraw_surface_set_clip_region (void *abstract_surface, -surface->extents.y); } else { surface->has_clip_region = FALSE; - surface->image_clip_invalid = surface->has_image_clip; } return CAIRO_STATUS_SUCCESS; @@ -3284,8 +3265,6 @@ cairo_ddraw_surface_create (LPDIRECTDRAW lpdd, surface->dirty = FALSE; #endif surface->has_clip_region = FALSE; - surface->has_image_clip = FALSE; - surface->image_clip_invalid = FALSE; surface->clip_list_invalid = FALSE; surface->installed_clipper = NULL; @@ -3361,8 +3340,6 @@ cairo_ddraw_surface_create_alias (cairo_surface_t *root_surface, surface->dirty = FALSE; #endif surface->has_clip_region = FALSE; - surface->has_image_clip = FALSE; - surface->image_clip_invalid = FALSE; surface->clip_list_invalid = FALSE; surface->format = root->format; @@ -3566,6 +3543,10 @@ _cairo_ddraw_surface_fill_rectangles (void *abstract_surface, sb = color->blue_short * (1.0f / 65535.0f); sa = color->alpha_short * (1.0f / 65535.0f); + status = _cairo_ddraw_surface_set_clip_region (surface, NULL); + if (status) + return status; + /* convert to solid clears if we can (so we can use glClear) */ if (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER && color->alpha_short >= 0xff00) { @@ -4110,8 +4091,6 @@ static const cairo_surface_backend_t _cairo_ddraw_surface_backend = { NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ - _cairo_ddraw_surface_set_clip_region, - NULL, /* intersect_clip_path */ _cairo_ddraw_surface_get_extents, NULL, /* old_show_glyphs */ NULL, /* get_font_options */ diff --git a/gfx/cairo/cairo/src/cairo-debug.c b/gfx/cairo/cairo/src/cairo-debug.c index d4548f1570b7..9160728e0ff7 100644 --- a/gfx/cairo/cairo/src/cairo-debug.c +++ b/gfx/cairo/cairo/src/cairo-debug.c @@ -75,6 +75,12 @@ cairo_debug_reset_static_data (void) _cairo_pattern_reset_static_data (); + _cairo_clip_reset_static_data (); + +#if CAIRO_HAS_DRM_SURFACE + _cairo_drm_device_reset_static_data (); +#endif + CAIRO_MUTEX_FINALIZE (); } @@ -159,3 +165,70 @@ _cairo_image_surface_write_to_ppm (cairo_image_surface_t *isurf, const char *fn) fprintf (stderr, "Wrote %s\n", fn); } #endif + +static cairo_status_t +_print_move_to (void *closure, + const cairo_point_t *point) +{ + fprintf (closure, + " %f %f m", + _cairo_fixed_to_double (point->x), + _cairo_fixed_to_double (point->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_print_line_to (void *closure, + const cairo_point_t *point) +{ + fprintf (closure, + " %f %f l", + _cairo_fixed_to_double (point->x), + _cairo_fixed_to_double (point->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_print_curve_to (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2, + const cairo_point_t *p3) +{ + fprintf (closure, + " %f %f %f %f %f %f c", + _cairo_fixed_to_double (p1->x), + _cairo_fixed_to_double (p1->y), + _cairo_fixed_to_double (p2->x), + _cairo_fixed_to_double (p2->y), + _cairo_fixed_to_double (p3->x), + _cairo_fixed_to_double (p3->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_print_close (void *closure) +{ + fprintf (closure, " h"); + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path) +{ + cairo_status_t status; + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _print_move_to, + _print_line_to, + _print_curve_to, + _print_close, + stream); + assert (status == CAIRO_STATUS_SUCCESS); + + printf ("\n"); +} diff --git a/gfx/cairo/cairo/src/cairo-directfb-surface.c b/gfx/cairo/cairo/src/cairo-directfb-surface.c index 174711757f3b..afad2705268e 100644 --- a/gfx/cairo/cairo/src/cairo-directfb-surface.c +++ b/gfx/cairo/cairo/src/cairo-directfb-surface.c @@ -38,14 +38,16 @@ #include "cairoint.h" #include "cairo-directfb.h" +#include "cairo-clip-private.h" + +#include + #include #include #include #include #include -#include "cairo-clip-private.h" - /* * Rectangle works fine. * Bugs 361377, 359553, 359243 in Gnome BTS are caused @@ -68,6 +70,8 @@ */ #define DFB_SHOW_GLYPHS 1 +#define PIXMAN_invalid (pixman_format_code_t) 0 + D_DEBUG_DOMAIN (CairoDFB_Acquire, "CairoDFB/Acquire", "Cairo DirectFB Acquire"); D_DEBUG_DOMAIN (CairoDFB_Clip, "CairoDFB/Clip", "Cairo DirectFB Clipping"); @@ -78,17 +82,15 @@ D_DEBUG_DOMAIN (CairoDFB_Surface, "CairoDFB/Surface", "Cairo DirectFB Surface"); /*****************************************************************************/ typedef struct _cairo_directfb_surface { - cairo_surface_t base; - cairo_format_t format; - cairo_content_t content; + cairo_surface_t base; - IDirectFB *dfb; - IDirectFBSurface *dfbsurface; - IDirectFBSurface *tmpsurface; + pixman_format_code_t pixman_format; + cairo_bool_t supported_destination; - cairo_bool_t has_clip; - DFBRegion *clips; - int n_clips; + IDirectFB *dfb; + IDirectFBSurface *dfbsurface; + IDirectFBSurface *tmpsurface; + pixman_format_code_t tmpformat; int width; int height; @@ -119,13 +121,18 @@ static int _directfb_argb_font = 0; /*****************************************************************************/ -#define RUN_CLIPPED(surface, clip, func) {\ - if ((surface)->has_clip) {\ - int k;\ - for (k = 0; k < (surface)->n_clips; k++) {\ +#define RUN_CLIPPED(surface, clip_region, clip, func) {\ + if ((clip_region) != NULL) {\ + int n_clips = cairo_region_num_rectangles (clip_region), n; \ + for (n = 0; n < n_clips; n++) {\ if (clip) {\ - DFBRegion reg = (surface)->clips[k];\ - DFBRegion *cli = (clip);\ + DFBRegion reg, *cli = (clip); \ + cairo_rectangle_int_t rect; \ + cairo_region_get_rectangle (clip_region, n, &rect); \ + reg.x1 = rect.x; \ + reg.y1 = rect.y; \ + reg.x2 = rect.x + rect.width - 1; \ + reg.y2 = rect.y + rect.height - 1; \ if (reg.x2 < cli->x1 || reg.y2 < cli->y1 ||\ reg.x1 > cli->x2 || reg.y1 > cli->y2)\ continue;\ @@ -139,8 +146,14 @@ static int _directfb_argb_font = 0; reg.y2 = cli->y2;\ (surface)->dfbsurface->SetClip ((surface)->dfbsurface, ®);\ } else {\ - (surface)->dfbsurface->SetClip ((surface)->dfbsurface,\ - &(surface)->clips[k]);\ + DFBRegion reg; \ + cairo_rectangle_int_t rect; \ + cairo_region_get_rectangle (clip_region, n, &rect); \ + reg.x1 = rect.x; \ + reg.y1 = rect.y; \ + reg.x2 = rect.x + rect.width - 1; \ + reg.y2 = rect.y + rect.height - 1; \ + (surface)->dfbsurface->SetClip ((surface)->dfbsurface, ®); \ }\ func;\ }\ @@ -150,19 +163,19 @@ static int _directfb_argb_font = 0; }\ } -#define TRANSFORM_POINT2X(m, x, y, ret_x, ret_y) {\ - double _x = (x);\ - double _y = (y);\ - (ret_x) = (_x * (m).xx + (m).x0);\ - (ret_y) = (_y * (m).yy + (m).y0);\ -} +#define TRANSFORM_POINT2X(m, x, y, ret_x, ret_y) do { \ + double _x = (x); \ + double _y = (y); \ + (ret_x) = (_x * (m).xx + (m).x0); \ + (ret_y) = (_y * (m).yy + (m).y0); \ +} while (0) -#define TRANSFORM_POINT3X(m, x, y, ret_x, ret_y) {\ - double _x = (x);\ - double _y = (y);\ - (ret_x) = (_x * (m).xx + _y * (m).xy + (m).x0);\ - (ret_y) = (_x * (m).yx + _y * (m).yy + (m).y0);\ -} +#define TRANSFORM_POINT3X(m, x, y, ret_x, ret_y) do { \ + double _x = (x); \ + double _y = (y); \ + (ret_x) = (_x * (m).xx + _y * (m).xy + (m).x0); \ + (ret_y) = (_x * (m).yx + _y * (m).yy + (m).y0); \ +} while (0) /* XXX: A1 has a different bits ordering in cairo. * Probably we should drop it. @@ -200,23 +213,69 @@ _cairo_to_directfb_format (cairo_format_t format) return -1; } -static inline cairo_format_t -_directfb_to_cairo_format (DFBSurfacePixelFormat format) +static inline pixman_format_code_t +_directfb_to_pixman_format (DFBSurfacePixelFormat format) { switch (format) { - case DSPF_RGB32: - return CAIRO_FORMAT_RGB24; - case DSPF_ARGB: - return CAIRO_FORMAT_ARGB32; - case DSPF_A8: - return CAIRO_FORMAT_A8; - case DSPF_A1: - return CAIRO_FORMAT_A1; - default: - break; + case DSPF_UNKNOWN: return PIXMAN_invalid; + case DSPF_ARGB1555: return PIXMAN_a1r5g5b5; + case DSPF_RGB16: return PIXMAN_r5g6b5; + case DSPF_RGB24: return PIXMAN_r8g8b8; + case DSPF_RGB32: return PIXMAN_x8r8g8b8; + case DSPF_ARGB: return PIXMAN_a8r8g8b8; + case DSPF_A8: return PIXMAN_a8; + case DSPF_YUY2: return PIXMAN_yuy2; + case DSPF_RGB332: return PIXMAN_r3g3b2; + case DSPF_UYVY: return PIXMAN_invalid; + case DSPF_I420: return PIXMAN_invalid; + case DSPF_YV12: return PIXMAN_yv12; + case DSPF_LUT8: return PIXMAN_invalid; + case DSPF_ALUT44: return PIXMAN_invalid; + case DSPF_AiRGB: return PIXMAN_invalid; + case DSPF_A1: return PIXMAN_a1; /* bit reversed, oops */ + case DSPF_NV12: return PIXMAN_invalid; + case DSPF_NV16: return PIXMAN_invalid; + case DSPF_ARGB2554: return PIXMAN_invalid; + case DSPF_ARGB4444: return PIXMAN_a4r4g4b4; + case DSPF_NV21: return PIXMAN_invalid; + case DSPF_AYUV: return PIXMAN_invalid; + case DSPF_A4: return PIXMAN_a4; + case DSPF_ARGB1666: return PIXMAN_invalid; + case DSPF_ARGB6666: return PIXMAN_invalid; + case DSPF_RGB18: return PIXMAN_invalid; + case DSPF_LUT2: return PIXMAN_invalid; + case DSPF_RGB444: return PIXMAN_x4r4g4b4; + case DSPF_RGB555: return PIXMAN_x1r5g5b5; +#if DFB_NUM_PIXELFORMATS >= 29 + case DSPF_BGR555: return PIXMAN_x1b5g5r5; +#endif } + return PIXMAN_invalid; +} - return -1; +static inline DFBSurfacePixelFormat +_directfb_from_pixman_format (pixman_format_code_t format) +{ + switch ((int) format) { + case PIXMAN_a1r5g5b5: return DSPF_ARGB1555; + case PIXMAN_r5g6b5: return DSPF_RGB16; + case PIXMAN_r8g8b8: return DSPF_RGB24; + case PIXMAN_x8r8g8b8: return DSPF_RGB32; + case PIXMAN_a8r8g8b8: return DSPF_ARGB; + case PIXMAN_a8: return DSPF_A8; + case PIXMAN_yuy2: return DSPF_YUY2; + case PIXMAN_r3g3b2: return DSPF_RGB332; + case PIXMAN_yv12: return DSPF_YV12; + case PIXMAN_a1: return DSPF_A1; /* bit reversed, oops */ + case PIXMAN_a4r4g4b4: return DSPF_ARGB4444; + case PIXMAN_a4: return DSPF_A4; + case PIXMAN_x4r4g4b4: return DSPF_RGB444; + case PIXMAN_x1r5g5b5: return DSPF_RGB555; +#if DFB_NUM_PIXELFORMATS >= 29 + case PIXMAN_x1b5g5r5: return DSPF_BGR555; +#endif + default: return 0; + } } static cairo_bool_t @@ -287,6 +346,21 @@ _directfb_get_operator (cairo_operator_t operator, dstblend = DSBF_ONE; break; #endif + case CAIRO_OPERATOR_MULTIPLY: + case CAIRO_OPERATOR_SCREEN: + case CAIRO_OPERATOR_OVERLAY: + case CAIRO_OPERATOR_DARKEN: + case CAIRO_OPERATOR_LIGHTEN: + case CAIRO_OPERATOR_COLOR_DODGE: + case CAIRO_OPERATOR_COLOR_BURN: + case CAIRO_OPERATOR_HARD_LIGHT: + case CAIRO_OPERATOR_SOFT_LIGHT: + case CAIRO_OPERATOR_DIFFERENCE: + case CAIRO_OPERATOR_EXCLUSION: + case CAIRO_OPERATOR_HSL_HUE: + case CAIRO_OPERATOR_HSL_SATURATION: + case CAIRO_OPERATOR_HSL_COLOR: + case CAIRO_OPERATOR_HSL_LUMINOSITY: default: return FALSE; } @@ -335,17 +409,14 @@ _directfb_acquire_surface (cairo_directfb_surface_t *surface, IDirectFBSurface *buffer = NULL; DFBRectangle source_rect; cairo_surface_t *image; - cairo_format_t cairo_format; + pixman_image_t *pixman_image; + pixman_format_code_t pixman_format; cairo_status_t status; void *data; int pitch; - if (surface->format == (cairo_format_t) -1 || - (lock_flags & DSLF_WRITE && surface->has_clip)) - { - DFBSurfaceCapabilities caps; - - if (intrest_rec) { + if (surface->pixman_format == PIXMAN_invalid) { + if (intrest_rec != NULL) { source_rect.x = intrest_rec->x; source_rect.y = intrest_rec->y; source_rect.w = intrest_rec->width; @@ -357,30 +428,38 @@ _directfb_acquire_surface (cairo_directfb_surface_t *surface, &source_rect.w, &source_rect.h); } - if (surface->tmpsurface) { + if (surface->tmpsurface != NULL) { int w, h; + surface->tmpsurface->GetSize (surface->tmpsurface, &w, &h); if (w < source_rect.w || h < source_rect.h) { surface->tmpsurface->Release (surface->tmpsurface); surface->tmpsurface = NULL; + surface->tmpformat = PIXMAN_invalid; } } - cairo_format = _cairo_format_from_content (surface->content); - if (!surface->tmpsurface) { + if (surface->tmpsurface == NULL) { + DFBSurfacePixelFormat format; + D_DEBUG_AT (CairoDFB_Acquire, "Allocating buffer for surface %p.\n", surface); + format = _cairo_to_directfb_format (_cairo_format_from_content (surface->base.content)); status = - _directfb_buffer_surface_create (surface->dfb, - _cairo_to_directfb_format (cairo_format), + _directfb_buffer_surface_create (surface->dfb, format, source_rect.w, source_rect.h, &surface->tmpsurface); - if (status) + if (unlikely (status)) goto ERROR; + + surface->tmpformat = _directfb_to_pixman_format (format); } buffer = surface->tmpsurface; + pixman_format = surface->tmpformat; + /* surface->dfbsurface->GetCapabilities (surface->dfbsurface, &caps); + DFBSurfaceCapabilities caps; if (caps & DSCAPS_FLIPPING) { DFBRegion region = { .x1 = source_rect.x, .y1 = source_rect.y, .x2 = source_rect.x + source_rect.w - 1, @@ -391,7 +470,7 @@ _directfb_acquire_surface (cairo_directfb_surface_t *surface, } else { /*might be a subsurface get the offset*/ surface->dfbsurface->GetVisibleRectangle (surface->dfbsurface, &source_rect); - cairo_format = surface->format; + pixman_format = surface->pixman_format; buffer = surface->dfbsurface; } @@ -401,9 +480,16 @@ _directfb_acquire_surface (cairo_directfb_surface_t *surface, goto ERROR; } - image = cairo_image_surface_create_for_data (data, cairo_format, - source_rect.w, source_rect.h, - pitch); + pixman_image = pixman_image_create_bits (pixman_format, + source_rect.w, source_rect.h, + data, pitch); + if (pixman_image == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto ERROR; + } + + image = _cairo_image_surface_create_for_pixman_image (pixman_image, + pixman_format); status = image->status; if (status) goto ERROR; @@ -436,37 +522,27 @@ ERROR: } static cairo_surface_t * -_cairo_directfb_surface_create_similar (void *abstract_src, - cairo_content_t content, - int width, - int height) +_cairo_directfb_surface_create_internal (IDirectFB *dfb, + DFBSurfacePixelFormat format, + cairo_content_t content, + int width, + int height) { - cairo_directfb_surface_t *source = abstract_src; cairo_directfb_surface_t *surface; - cairo_format_t format; cairo_status_t status; - D_DEBUG_AT (CairoDFB_Surface, - "%s( src=%p, content=0x%x, width=%d, height=%d).\n", - __FUNCTION__, source, content, width, height); - - width = (width <= 0) ? 1 : width; - height = (height<= 0) ? 1 : height; - - format = _cairo_format_from_content (content); surface = calloc (1, sizeof (cairo_directfb_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - surface->dfb = source->dfb; + surface->dfb = dfb; if (width < 8 || height < 8) { IDirectFBSurface *tmp; DFBRectangle rect = { .x=0, .y=0, .w=width, .h=height }; /* Some cards (e.g. Matrox) don't support surfaces smaller than 8x8 */ - status = _directfb_buffer_surface_create (surface->dfb, - _cairo_to_directfb_format (format), + status = _directfb_buffer_surface_create (dfb, format, MAX (width, 8), MAX (height, 8), &tmp); if (status) { @@ -477,11 +553,9 @@ _cairo_directfb_surface_create_similar (void *abstract_src, tmp->GetSubSurface (tmp, &rect, &surface->dfbsurface); tmp->Release (tmp); } else { - status = - _directfb_buffer_surface_create (surface->dfb, - _cairo_to_directfb_format (format), - width, height, - &surface->dfbsurface); + status = _directfb_buffer_surface_create (dfb, format, + width, height, + &surface->dfbsurface); if (status) { free (surface); return _cairo_surface_create_in_error (status); @@ -491,8 +565,9 @@ _cairo_directfb_surface_create_similar (void *abstract_src, _cairo_surface_init (&surface->base, &_cairo_directfb_surface_backend, content); - surface->format = format; - surface->content = content; + surface->pixman_format = _directfb_to_pixman_format (format); + surface->supported_destination = pixman_format_supported_destination (surface->pixman_format); + surface->width = width; surface->height = height; surface->local = TRUE; @@ -501,6 +576,27 @@ _cairo_directfb_surface_create_similar (void *abstract_src, return &surface->base; } +static cairo_surface_t * +_cairo_directfb_surface_create_similar (void *abstract_src, + cairo_content_t content, + int width, + int height) +{ + cairo_directfb_surface_t *other = abstract_src; + DFBSurfacePixelFormat format; + + D_DEBUG_AT (CairoDFB_Surface, + "%s( src=%p, content=0x%x, width=%d, height=%d).\n", + __FUNCTION__, other, content, width, height); + + width = (width <= 0) ? 1 : width; + height = (height<= 0) ? 1 : height; + + format = _cairo_to_directfb_format (_cairo_format_from_content (content)); + return _cairo_directfb_surface_create_internal (other->dfb, format, + content, width, height); +} + static cairo_status_t _cairo_directfb_surface_finish (void *data) { @@ -508,12 +604,6 @@ _cairo_directfb_surface_finish (void *data) D_DEBUG_AT (CairoDFB_Surface, "%s( surface=%p ).\n", __FUNCTION__, surface); - if (surface->clips) { - free (surface->clips); - surface->clips = NULL; - surface->n_clips = 0; - } - if (surface->tmpsurface) { surface->tmpsurface->Release (surface->tmpsurface); surface->tmpsurface = NULL; @@ -549,11 +639,10 @@ _cairo_directfb_surface_release_source_image (void *abstract_su cairo_image_surface_t *image, void *image_extra) { - cairo_directfb_surface_t *surface = abstract_surface; IDirectFBSurface *buffer = image_extra; D_DEBUG_AT (CairoDFB_Acquire, - "%s( surface=%p ).\n", __FUNCTION__, surface); + "%s( release=%p ).\n", __FUNCTION__, abstract_surface); buffer->Unlock (buffer); @@ -605,10 +694,10 @@ _cairo_directfb_surface_release_dest_image (void *abstract_surf .y2 = interest_rect->y + interest_rect->height - 1 }; surface->dfbsurface->SetBlittingFlags (surface->dfbsurface, DSBLIT_NOFX); - RUN_CLIPPED (surface, ®ion, - surface->dfbsurface->Blit (surface->dfbsurface, - buffer, NULL, - image_rect->x, image_rect->y)); + surface->dfbsurface->SetClip (surface->dfbsurface, ®ion); + surface->dfbsurface->Blit (surface->dfbsurface, + buffer, NULL, + image_rect->x, image_rect->y); } cairo_surface_destroy (&image->base); @@ -617,7 +706,6 @@ _cairo_directfb_surface_release_dest_image (void *abstract_surf static cairo_status_t _cairo_directfb_surface_clone_similar (void *abstract_surface, cairo_surface_t *src, - cairo_content_t content, int src_x, int src_y, int width, @@ -640,56 +728,51 @@ _cairo_directfb_surface_clone_similar (void *abstract_surface, return CAIRO_STATUS_SUCCESS; } else if (_cairo_surface_is_image (src)) { cairo_image_surface_t *image_src = (cairo_image_surface_t *) src; - unsigned char *dst, *src = image_src->data; - int pitch; - int i, j; + DFBSurfacePixelFormat format; DFBResult ret; + pixman_image_t *pixman_image; + void *data; + int pitch; + + format = _directfb_from_pixman_format (image_src->pixman_format); + if (format == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; clone = (cairo_directfb_surface_t *) - _cairo_directfb_surface_create_similar (surface, - _cairo_content_from_format (image_src->format), - width, height); - if (clone == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (clone->base.status) + _cairo_directfb_surface_create_internal (surface->dfb, format, + image_src->base.content, + width, height); + if (unlikely (clone->base.status)) return clone->base.status; ret = clone->dfbsurface->Lock (clone->dfbsurface, - DSLF_WRITE, (void *)&dst, &pitch); + DSLF_WRITE, (void *)&data, &pitch); if (ret) { DirectFBError ("IDirectFBSurface::Lock()", ret); cairo_surface_destroy (&clone->base); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - src += image_src->stride * src_y; - if (image_src->format == CAIRO_FORMAT_A1) { - /* A1 -> A8 */ - dst -= src_x; - for (i = 0; i < height; i++) { - for (j = src_x; j < src_x + width; j++) - dst[j] = (src[j>>3] & (1 << (j&7))) ? 0xff : 0x00; - dst += pitch; - src += image_src->stride; - } - } else { - int len; - - if (image_src->format == CAIRO_FORMAT_A8) { - src += src_x; - len = width; - } else { - src += src_x * 4; - len = width * 4; - } - - for (i = 0; i < height; i++) { - direct_memcpy (dst, src, len); - dst += pitch; - src += image_src->stride; - } + pixman_image = pixman_image_create_bits (clone->pixman_format, + width, height, + data, pitch); + if (unlikely (pixman_image == NULL)) { + DirectFBError ("IDirectFBSurface::Lock()", ret); + cairo_surface_destroy (&clone->base); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); } + pixman_image_composite (PIXMAN_OP_SRC, + image_src->pixman_image, + NULL, + pixman_image, + src_x, src_y, + 0, 0, + 0, 0, + width, height); + + pixman_image_unref (pixman_image); + clone->dfbsurface->Unlock (clone->dfbsurface); *clone_offset_x = src_x; @@ -730,8 +813,6 @@ _directfb_prepare_composite (cairo_directfb_surface_t *dst, return CAIRO_INT_STATUS_UNSUPPORTED; if (mask_pattern) { - cairo_solid_pattern_t *pattern; - return CAIRO_INT_STATUS_UNSUPPORTED; if (mask_pattern->type != CAIRO_PATTERN_TYPE_SOLID) { const cairo_pattern_t *tmp; @@ -764,7 +845,6 @@ _directfb_prepare_composite (cairo_directfb_surface_t *dst, } status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, - CAIRO_CONTENT_COLOR_ALPHA, *src_x, *src_y, width, height, CAIRO_PATTERN_ACQUIRE_NO_REFLECT, (cairo_surface_t **) &src, @@ -779,7 +859,7 @@ _directfb_prepare_composite (cairo_directfb_surface_t *dst, return CAIRO_INT_STATUS_UNSUPPORTED; } - if (src->content == CAIRO_CONTENT_COLOR) { + if ((src->base.content & CAIRO_CONTENT_ALPHA) == 0) { if (sblend == DSBF_SRCALPHA) sblend = DSBF_ONE; else if (sblend == DSBF_INVSRCALPHA) @@ -791,7 +871,7 @@ _directfb_prepare_composite (cairo_directfb_surface_t *dst, dblend = DSBF_ZERO; } - if (dst->content == CAIRO_CONTENT_COLOR) { + if ((dst->base.content & CAIRO_CONTENT_ALPHA) == 0) { if (sblend == DSBF_DESTALPHA) sblend = DSBF_ONE; else if (sblend == DSBF_INVDESTALPHA) @@ -898,7 +978,8 @@ _cairo_directfb_surface_composite (cairo_operator_t op, int mask_x, int mask_y, int dst_x, int dst_y, unsigned int width, - unsigned int height) + unsigned int height, + cairo_region_t *clip_region) { cairo_directfb_surface_t *dst = abstract_dst; cairo_directfb_surface_t *src; @@ -915,6 +996,9 @@ _cairo_directfb_surface_composite (cairo_operator_t op, __FUNCTION__, op, src_pattern, mask_pattern, dst, src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height); + if (! dst->supported_destination) + return CAIRO_INT_STATUS_UNSUPPORTED; + status = _directfb_prepare_composite (dst, src_pattern, mask_pattern, op, &src_x, &src_y, &mask_x, &mask_y, width, height, &src, &src_attr); @@ -941,7 +1025,7 @@ _cairo_directfb_surface_composite (cairo_operator_t op, src_x += src_attr.x_offset; src_y += src_attr.y_offset; - switch (accel) { + switch ((int) accel) { case DFXL_BLIT: { DFBRectangle sr; @@ -959,11 +1043,10 @@ _cairo_directfb_surface_composite (cairo_operator_t op, if (src_attr.extend == CAIRO_EXTEND_NONE) { D_DEBUG_AT (CairoDFB_Render, "Running Blit().\n"); - RUN_CLIPPED (dst, NULL, + RUN_CLIPPED (dst, clip_region, NULL, dst->dfbsurface->Blit (dst->dfbsurface, src->dfbsurface, - &sr, - dst_x, dst_y)); + &sr, dst_x, dst_y)); } else if (src_attr.extend == CAIRO_EXTEND_REPEAT) { DFBRegion clip; @@ -974,11 +1057,10 @@ _cairo_directfb_surface_composite (cairo_operator_t op, D_DEBUG_AT (CairoDFB_Render, "Running TileBlit().\n"); - RUN_CLIPPED (dst, &clip, + RUN_CLIPPED (dst, clip_region, &clip, dst->dfbsurface->TileBlit (dst->dfbsurface, src->dfbsurface, - &sr, - dst_x, dst_y)); + &sr, dst_x, dst_y)); } break; } @@ -1005,9 +1087,10 @@ _cairo_directfb_surface_composite (cairo_operator_t op, D_DEBUG_AT (CairoDFB_Render, "Running StretchBlit().\n"); - RUN_CLIPPED (dst, NULL, + RUN_CLIPPED (dst, clip_region, NULL, dst->dfbsurface->StretchBlit (dst->dfbsurface, - src->dfbsurface, &sr, &dr)); + src->dfbsurface, + &sr, &dr)); break; } @@ -1029,29 +1112,25 @@ _cairo_directfb_surface_composite (cairo_operator_t op, src->dfbsurface->GetSize (src->dfbsurface, &w, &h); - TRANSFORM_POINT3X (src_attr.matrix, - x1, y1, v[0].x, v[0].y); + TRANSFORM_POINT3X (src_attr.matrix, x1, y1, v[0].x, v[0].y); v[0].z = 0; v[0].w = 1; v[0].s = x1 / w; v[0].t = y1 / h; - TRANSFORM_POINT3X (src_attr.matrix, - x2, y1, v[1].x, v[1].y); + TRANSFORM_POINT3X (src_attr.matrix, x2, y1, v[1].x, v[1].y); v[1].z = 0; v[1].w = 1; v[1].s = x2 / w; v[1].t = y1 / h; - TRANSFORM_POINT3X (src_attr.matrix, - x2, y2, v[2].x, v[2].y); + TRANSFORM_POINT3X (src_attr.matrix, x2, y2, v[2].x, v[2].y); v[2].z = 0; v[2].w = 1; v[2].s = x2 / w; v[2].t = y2 / h; - TRANSFORM_POINT3X (src_attr.matrix, - x1, y2, v[3].x, v[3].y); + TRANSFORM_POINT3X (src_attr.matrix, x1, y2, v[3].x, v[3].y); v[3].z = 0; v[3].w = 1; v[3].s = x1 / w; @@ -1064,9 +1143,11 @@ _cairo_directfb_surface_composite (cairo_operator_t op, D_DEBUG_AT (CairoDFB_Render, "Running TextureTriangles().\n"); - RUN_CLIPPED (dst, &clip, + RUN_CLIPPED (dst, clip_region, &clip, dst->dfbsurface->TextureTriangles (dst->dfbsurface, - src->dfbsurface, v, NULL, 4, DTTF_FAN)); + src->dfbsurface, + v, NULL, + 4, DTTF_FAN)); break; } @@ -1077,7 +1158,7 @@ _cairo_directfb_surface_composite (cairo_operator_t op, _directfb_finish_composite (dst, src_pattern, &src->base, &src_attr); - return status; + return CAIRO_STATUS_SUCCESS; } #endif /* DFB_COMPOSITE */ @@ -1100,8 +1181,8 @@ _cairo_directfb_surface_fill_rectangles (void *abstract_surface "%s( dst=%p, op=%d, color=%p, rects=%p, n_rects=%d ).\n", __FUNCTION__, dst, op, color, rects, n_rects); - if (! _cairo_operator_bounded_by_source (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (! dst->supported_destination) + return CAIRO_INT_STATUS_UNSUPPORTED; if (! _directfb_get_operator (op, &sblend, &dblend)) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -1117,7 +1198,7 @@ _cairo_directfb_surface_fill_rectangles (void *abstract_surface else if (dblend == DSBF_INVSRCALPHA) dblend = DSBF_ZERO; } - if (dst->content == CAIRO_CONTENT_COLOR) { + if ((dst->base.content & CAIRO_CONTENT_ALPHA) == 0) { if (sblend == DSBF_DESTALPHA) sblend = DSBF_ONE; else if (sblend == DSBF_INVDESTALPHA) @@ -1149,7 +1230,7 @@ _cairo_directfb_surface_fill_rectangles (void *abstract_surface r[i].h = rects[i].height; } - RUN_CLIPPED (dst, NULL, + RUN_CLIPPED (dst, NULL, NULL, dst->dfbsurface->FillRectangles (dst->dfbsurface, r, n_rects)); return CAIRO_STATUS_SUCCESS; @@ -1167,7 +1248,8 @@ _cairo_directfb_surface_composite_trapezoids (cairo_operator_t op, unsigned int width, unsigned int height, cairo_trapezoid_t *traps, - int num_traps) + int num_traps, + cairo_region_t *clip_region) { cairo_directfb_surface_t *dst = abstract_dst; cairo_directfb_surface_t *src; @@ -1182,6 +1264,9 @@ _cairo_directfb_surface_composite_trapezoids (cairo_operator_t op, __FUNCTION__, op, pattern, dst, antialias, src_x, src_y, dst_x, dst_y, width, height, traps, num_traps); + if (! dst->supported_destination) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (antialias != CAIRO_ANTIALIAS_NONE) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -1276,7 +1361,7 @@ _cairo_directfb_surface_composite_trapezoids (cairo_operator_t op, D_DEBUG_AT (CairoDFB_Render, "Running TextureTriangles().\n"); - RUN_CLIPPED (dst, NULL, + RUN_CLIPPED (dst, clip_region, NULL, dst->dfbsurface->TextureTriangles (dst->dfbsurface, src->dfbsurface, vertex, NULL, n, @@ -1291,64 +1376,7 @@ _cairo_directfb_surface_composite_trapezoids (cairo_operator_t op, } #endif /* DFB_COMPOSITE_TRAPEZOIDS */ -static cairo_int_status_t -_cairo_directfb_surface_set_clip_region (void *abstract_surface, - cairo_region_t *region) -{ - cairo_directfb_surface_t *surface = abstract_surface; - - D_DEBUG_AT (CairoDFB_Clip, - "%s( surface=%p, region=%p ).\n", - __FUNCTION__, surface, region); - - if (region) { - int n_rects; - cairo_status_t status; - int i; - - surface->has_clip = TRUE; - - n_rects = cairo_region_num_rectangles (region); - - if (n_rects == 0) - return CAIRO_STATUS_SUCCESS; - - if (surface->n_clips != n_rects) { - if (surface->clips) - free (surface->clips); - - surface->clips = _cairo_malloc_ab (n_rects, sizeof (DFBRegion)); - if (!surface->clips) { - surface->n_clips = 0; - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - surface->n_clips = n_rects; - } - - for (i = 0; i < n_rects; i++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (region, i, &rect); - - surface->clips[i].x1 = rect.x; - surface->clips[i].y1 = rect.y; - surface->clips[i].x2 = rect.x + rect.width - 1; - surface->clips[i].y2 = rect.y + rect.height - 1; - } - } else { - surface->has_clip = FALSE; - if (surface->clips) { - free (surface->clips); - surface->clips = NULL; - surface->n_clips = 0; - } - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t +static cairo_bool_t _cairo_directfb_abstract_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { @@ -1368,7 +1396,7 @@ _cairo_directfb_abstract_surface_get_extents (void *abstract_su rectangle->width = surface->width; rectangle->height = surface->height; - return CAIRO_STATUS_SUCCESS; + return TRUE; } #if DFB_SHOW_GLYPHS @@ -1407,6 +1435,7 @@ _directfb_destroy_font_cache (cairo_directfb_font_cache_t *cache) free (cache); } +/* XXX hook into rtree font cache from drm */ static cairo_status_t _directfb_acquire_font_cache (cairo_directfb_surface_t *surface, cairo_scaled_font_t *scaled_font, @@ -1458,6 +1487,7 @@ _directfb_acquire_font_cache (cairo_directfb_surface_t *surface, case CAIRO_FORMAT_A8: case CAIRO_FORMAT_ARGB32: break; + case CAIRO_FORMAT_RGB24: default: D_DEBUG_AT (CairoDFB_Font, " -> Unsupported font format %d!\n", img->format); @@ -1690,8 +1720,8 @@ _cairo_directfb_surface_show_glyphs (void *abstract_dst, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip, + int *remaining_glyphs) { cairo_directfb_surface_t *dst = abstract_dst; cairo_directfb_font_cache_t *cache; @@ -1703,28 +1733,29 @@ _cairo_directfb_surface_show_glyphs (void *abstract_dst, DFBPoint points[num_glyphs]; int num; const cairo_color_t *color; - + cairo_region_t *clip_region = NULL; D_DEBUG_AT (CairoDFB_Font, "%s( dst=%p, op=%d, pattern=%p, glyphs=%p, num_glyphs=%d, scaled_font=%p ).\n", __FUNCTION__, dst, op, pattern, glyphs, num_glyphs, scaled_font); + if (! dst->supported_destination) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) return CAIRO_INT_STATUS_UNSUPPORTED; /* Fallback if we need to emulate clip regions */ - if (dst->base.clip && - (dst->base.clip->mode != CAIRO_CLIP_MODE_REGION || - dst->base.clip->surface != NULL)) - { - return CAIRO_INT_STATUS_UNSUPPORTED; + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); + if (status) + return status; } /* XXX Unbounded operators are not handled correctly */ if (! _cairo_operator_bounded_by_mask (op)) return CAIRO_INT_STATUS_UNSUPPORTED; - if (! _cairo_operator_bounded_by_source (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; if (! _directfb_get_operator (op, &sblend, &dblend) || sblend == DSBF_DESTALPHA || sblend == DSBF_INVDESTALPHA) @@ -1773,7 +1804,7 @@ _cairo_directfb_surface_show_glyphs (void *abstract_dst, D_DEBUG_AT (CairoDFB_Font, "Running BatchBlit().\n"); - RUN_CLIPPED (dst, NULL, + RUN_CLIPPED (dst, clip_region, NULL, dst->dfbsurface->BatchBlit (dst->dfbsurface, cache->dfbsurface, rects, points, num)); @@ -1820,8 +1851,6 @@ _cairo_directfb_surface_backend = { NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ - _cairo_directfb_surface_set_clip_region,/* set_clip_region */ - NULL, /* intersect_clip_path */ _cairo_directfb_abstract_surface_get_extents,/* get_extents */ NULL, /* old_show_glyphs */ NULL, /* get_font_options */ @@ -1845,7 +1874,6 @@ _cairo_directfb_surface_backend = { #endif NULL, /* snapshot */ _cairo_directfb_surface_is_similar, - NULL /* reset */ }; @@ -1918,8 +1946,8 @@ cairo_directfb_surface_create (IDirectFB *dfb, IDirectFBSurface *dfbsurface) dfbsurface->GetSize (dfbsurface, &surface->width, &surface->height); surface->dfb = dfb; surface->dfbsurface = dfbsurface; - surface->format = _directfb_to_cairo_format (format); - surface->content = _directfb_format_to_content (format); + surface->pixman_format = _directfb_to_pixman_format (format); + surface->supported_destination = pixman_format_supported_destination (surface->pixman_format); dfbsurface->GetCapabilities (dfbsurface, &caps); if (caps & DSCAPS_PREMULTIPLIED) @@ -1927,7 +1955,7 @@ cairo_directfb_surface_create (IDirectFB *dfb, IDirectFBSurface *dfbsurface) _cairo_surface_init (&surface->base, &_cairo_directfb_surface_backend, - surface->content); + _directfb_format_to_content (format)); return &surface->base; } diff --git a/gfx/cairo/cairo/src/cairo-drm.h b/gfx/cairo/cairo/src/cairo-drm.h new file mode 100644 index 000000000000..1b50b1bd4ebb --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-drm.h @@ -0,0 +1,135 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Chris Wilson. + */ + +#ifndef CAIRO_DRM_H +#define CAIRO_DRM_H + +#include "cairo.h" + +#if CAIRO_HAS_DRM_SURFACE + +CAIRO_BEGIN_DECLS + +typedef struct _cairo_drm_device cairo_drm_device_t; + +struct udev_device; + +cairo_public cairo_drm_device_t * +cairo_drm_device_get (struct udev_device *device); + +cairo_public cairo_drm_device_t * +cairo_drm_device_get_for_fd (int fd); + +cairo_public cairo_drm_device_t * +cairo_drm_device_default (void); + +cairo_public cairo_drm_device_t * +cairo_drm_device_reference (cairo_drm_device_t *device); + +cairo_public cairo_status_t +cairo_drm_device_status (cairo_drm_device_t *device); + +cairo_public int +cairo_drm_device_get_fd (cairo_drm_device_t *device); + +cairo_public void +cairo_drm_device_throttle (cairo_drm_device_t *device); + +cairo_public void +cairo_drm_device_destroy (cairo_drm_device_t *device); + + +cairo_public cairo_surface_t * +cairo_drm_surface_create (cairo_drm_device_t *device, + cairo_content_t content, + int width, int height); + +cairo_public cairo_surface_t * +cairo_drm_surface_create_for_name (cairo_drm_device_t *device, + unsigned int name, + cairo_format_t format, + int width, int height, int stride); + +cairo_public cairo_surface_t * +cairo_drm_surface_create_from_cacheable_image (cairo_drm_device_t *device, + cairo_surface_t *surface); + +cairo_public cairo_status_t +cairo_drm_surface_enable_scan_out (cairo_surface_t *surface); + +cairo_public cairo_drm_device_t * +cairo_drm_surface_get_device (cairo_surface_t *abstract_surface); + +cairo_public unsigned int +cairo_drm_surface_get_handle (cairo_surface_t *surface); + +cairo_public unsigned int +cairo_drm_surface_get_name (cairo_surface_t *surface); + +cairo_public cairo_format_t +cairo_drm_surface_get_format (cairo_surface_t *surface); + +cairo_public int +cairo_drm_surface_get_width (cairo_surface_t *surface); + +cairo_public int +cairo_drm_surface_get_height (cairo_surface_t *surface); + +cairo_public int +cairo_drm_surface_get_stride (cairo_surface_t *surface); + +/* XXX map/unmap, general surface layer? */ + +/* Rough outline, culled from a conversation on IRC: + * map() returns an image-surface representation of the drm-surface, + * which you unmap() when you are finished, i.e. map() pulls the buffer back + * from the GPU, maps it into the CPU domain and gives you direct access to + * the pixels. With the unmap(), the buffer is ready to be used again by the + * GPU and *until* the unmap(), all operations will be done in software. + * + * (Technically calling cairo_surface_flush() on the underlying drm-surface + * will also disassociate the mapping.) +*/ +cairo_public cairo_surface_t * +cairo_drm_surface_map (cairo_surface_t *surface); + +cairo_public void +cairo_drm_surface_unmap (cairo_surface_t *drm_surface, + cairo_surface_t *image_surface); + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_DRM_SURFACE */ +# error Cairo was not compiled with support for the DRM backend +#endif /* CAIRO_HAS_DRM_SURFACE */ + +#endif /* CAIRO_DRM_H */ diff --git a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp index c582a6e5245d..0ee97a96a3e4 100644 --- a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp +++ b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp @@ -181,6 +181,7 @@ _cairo_dwrite_scaled_show_glyphs(void *scaled_font, unsigned int height, cairo_glyph_t *glyphs, int num_glyphs, + cairo_region_t *clip_region, int *remaining_glyphs); cairo_int_status_t @@ -425,6 +426,7 @@ _cairo_dwrite_scaled_show_glyphs(void *scaled_font, unsigned int height, cairo_glyph_t *glyphs, int num_glyphs, + cairo_region_t *clip_region, int *remaining_glyphs) { cairo_win32_surface_t *surface = (cairo_win32_surface_t *)generic_surface; @@ -437,7 +439,9 @@ _cairo_dwrite_scaled_show_glyphs(void *scaled_font, surface->format == CAIRO_FORMAT_RGB24 && op == CAIRO_OPERATOR_OVER) { - status = (cairo_int_status_t)cairo_dwrite_show_glyphs_on_surface (surface, op, pattern, + //XXX: we need to set the clip region here + + status = (cairo_int_status_t)_cairo_dwrite_show_glyphs_on_surface (surface, op, pattern, glyphs, num_glyphs, (cairo_scaled_font_t*)scaled_font, NULL); @@ -547,7 +551,8 @@ _cairo_dwrite_scaled_show_glyphs(void *scaled_font, source_x, source_y, 0, 0, dest_x, dest_y, - width, height); + width, height, + clip_region); _cairo_pattern_fini (&mask.base); @@ -1022,29 +1027,20 @@ _dwrite_draw_glyphs_to_gdi_surface_d2d(cairo_win32_surface_t *surface, } /* Surface helper function */ -cairo_public cairo_int_status_t -cairo_dwrite_show_glyphs_on_surface(void *surface, +cairo_int_status_t +_cairo_dwrite_show_glyphs_on_surface(void *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { // TODO: Check font & surface for types. cairo_dwrite_scaled_font_t *dwritesf = reinterpret_cast(scaled_font); cairo_dwrite_font_face_t *dwriteff = reinterpret_cast(scaled_font->font_face); cairo_win32_surface_t *dst = reinterpret_cast(surface); cairo_int_status_t status; - - /* If we have a fallback mask clip set on the dst, we have - * to go through the fallback path, but only if we're not - * doing this for printing */ - if (dst->base.clip && - (dst->base.clip->mode != CAIRO_CLIP_MODE_REGION || - dst->base.clip->surface != NULL)) - return CAIRO_INT_STATUS_UNSUPPORTED; - /* We can only handle dwrite fonts */ if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -1059,6 +1055,21 @@ cairo_dwrite_show_glyphs_on_surface(void *surface, (dst->format != CAIRO_FORMAT_RGB24)) return CAIRO_INT_STATUS_UNSUPPORTED; + /* If we have a fallback mask clip set on the dst, we have + * to go through the fallback path */ + if (clip != NULL) { + if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) == 0) { + cairo_region_t *clip_region; + cairo_int_status_t status; + + status = _cairo_clip_get_region (clip, &clip_region); + assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); + if (status) + return status; + + _cairo_win32_surface_set_clip_region (dst, clip_region); + } + } /* It is vital that dx values for dxy_buf are calculated from the delta of * _logical_ x coordinates (not user x coordinates) or else the sum of all @@ -1212,21 +1223,25 @@ cairo_dwrite_show_glyphs_on_surface(void *surface, #if CAIRO_HAS_D2D_SURFACE /* Surface helper function */ +//XXX: this function should probably be in cairo-d2d-surface.cpp cairo_int_status_t -cairo_dwrite_show_glyphs_on_d2d_surface(void *surface, +_cairo_dwrite_show_glyphs_on_d2d_surface(void *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { + cairo_int_status_t status; + // TODO: Check font & surface for types. cairo_dwrite_scaled_font_t *dwritesf = reinterpret_cast(scaled_font); cairo_dwrite_font_face_t *dwriteff = reinterpret_cast(scaled_font->font_face); cairo_d2d_surface_t *dst = reinterpret_cast(surface); /* We can only handle dwrite fonts */ + //XXX: this is checked by at least one caller if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -1236,6 +1251,11 @@ cairo_dwrite_show_glyphs_on_d2d_surface(void *surface, if (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) return CAIRO_INT_STATUS_UNSUPPORTED; + status = (cairo_int_status_t)_cairo_surface_clipper_set_clip (&dst->clipper, clip); + if (unlikely (status)) + return status; + + _cairo_d2d_begin_draw_state(dst); D2D1_TEXT_ANTIALIAS_MODE cleartype = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; diff --git a/gfx/cairo/cairo/src/cairo-dwrite-private.h b/gfx/cairo/cairo/src/cairo-dwrite-private.h index f885f805e5d2..059acbf1d490 100644 --- a/gfx/cairo/cairo/src/cairo-dwrite-private.h +++ b/gfx/cairo/cairo/src/cairo-dwrite-private.h @@ -98,10 +98,10 @@ private: }; cairo_int_status_t -cairo_dwrite_show_glyphs_on_d2d_surface(void *surface, +_cairo_dwrite_show_glyphs_on_d2d_surface(void *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); diff --git a/gfx/cairo/cairo/src/cairo-eagle-context.c b/gfx/cairo/cairo/src/cairo-eagle-context.c new file mode 100644 index 000000000000..23766a944d1c --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-eagle-context.c @@ -0,0 +1,181 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Eric Anholt + * Copyright © 2009 Chris Wilson + * Copyright © 2005 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Carl Worth + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-gl-private.h" + +#include /* XXX dummy surface for glewInit() */ +#include + +typedef struct _cairo_eagle_context { + cairo_gl_context_t base; + + EGLDisplay display; + EGLContext context; +} cairo_eagle_context_t; + +typedef struct _cairo_eagle_surface { + cairo_gl_surface_t base; + + EGLSurface eagle; +} cairo_eagle_surface_t; + +static void +_eagle_make_current (void *abstract_ctx, + cairo_gl_surface_t *abstract_surface) +{ + cairo_eagle_context_t *ctx = abstract_ctx; + cairo_eagle_surface_t *surface = (cairo_eagle_surface_t *) abstract_surface; + + eagleMakeCurrent (ctx->display, surface->eagle, surface->eagle, ctx->context); +} + +static void +_eagle_swap_buffers (void *abstract_ctx, + cairo_gl_surface_t *abstract_surface) +{ + cairo_eagle_context_t *ctx = abstract_ctx; + cairo_eagle_surface_t *surface = (cairo_eagle_surface_t *) abstract_surface; + + eagleSwapBuffers (ctx->display, surface->eagle); +} + +static void +_eagle_destroy (void *abstract_ctx) +{ +} + +static cairo_bool_t +_eagle_init (EGLDisplay display, EGLContext context) +{ + const EGLint config_attribs[] = { + EGL_CONFIG_CAVEAT, EGL_NONE, + EGL_NONE + }; + const EGLint surface_attribs[] = { + EGL_RENDER_BUFFER, EGL_BACK_BUFFER, + EGL_NONE + }; + EGLConfig config; + EGLSurface dummy; + struct drm_i915_gem_create create; + struct drm_gem_flink flink; + int fd; + GLenum err; + + if (! eagleChooseConfig (display, config_attribs, &config, 1, NULL)) { + fprintf (stderr, "Unable to choose config\n"); + return FALSE; + } + + /* XXX */ + fd = eagleGetDisplayFD (display); + + create.size = 4096; + if (ioctl (fd, DRM_IOCTL_I915_GEM_CREATE, &create) != 0) { + fprintf (stderr, "gem create failed: %m\n"); + return FALSE; + } + flink.handle = create.handle; + if (ioctl (fd, DRM_IOCTL_GEM_FLINK, &flink) != 0) { + fprintf (stderr, "gem flink failed: %m\n"); + return FALSE; + } + + dummy = eagleCreateSurfaceForName (display, config, flink.name, + 1, 1, 4, surface_attribs); + if (dummy == NULL) { + fprintf (stderr, "Failed to create dummy surface\n"); + return FALSE; + } + + eagleMakeCurrent (display, dummy, dummy, context); +} + +cairo_gl_context_t * +cairo_eagle_context_create (EGLDisplay display, EGLContext context) +{ + cairo_eagle_context_t *ctx; + cairo_status_t status; + + if (! _eagle_init (display, context)) { + return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); + } + + ctx = calloc (1, sizeof (cairo_eagle_context_t)); + if (ctx == NULL) + return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); + + ctx->display = display; + ctx->context = context; + + ctx->base.make_current = _eagle_make_current; + ctx->base.swap_buffers = _eagle_swap_buffers; + ctx->base.destroy = _eagle_destroy; + + status = _cairo_gl_context_init (&ctx->base); + if (status) { + free (ctx); + return _cairo_gl_context_create_in_error (status); + } + + return &ctx->base; +} + +cairo_surface_t * +cairo_gl_surface_create_for_eagle (cairo_gl_context_t *ctx, + EGLSurface eagle, + int width, + int height) +{ + cairo_eagle_surface_t *surface; + + if (ctx->status) + return _cairo_surface_create_in_error (ctx->status); + + surface = calloc (1, sizeof (cairo_eagle_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_gl_surface_init (ctx, &surface->base, + CAIRO_CONTENT_COLOR_ALPHA, width, height); + surface->eagle = eagle; + + return &surface->base.base; +} diff --git a/gfx/cairo/cairo/src/cairo-features-win32.h b/gfx/cairo/cairo/src/cairo-features-win32.h new file mode 100644 index 000000000000..ef202fc14758 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-features-win32.h @@ -0,0 +1,16 @@ +/* Generated by configure. Do not edit. */ +#ifndef CAIRO_FEATURES_H +#define CAIRO_FEATURES_H + +#define HAVE_WINDOWS_H 1 + +#define CAIRO_HAS_WIN32_SURFACE 1 +#define CAIRO_HAS_WIN32_FONT 1 +#define CAIRO_HAS_PNG_FUNCTIONS 1 +#define CAIRO_HAS_PS_SURFACE 1 +#define CAIRO_HAS_PDF_SURFACE 1 +#define CAIRO_HAS_SVG_SURFACE 1 +#define CAIRO_HAS_IMAGE_SURFACE 1 +#define CAIRO_HAS_USER_FONT 1 + +#endif diff --git a/gfx/cairo/cairo/src/cairo-fixed-private.h b/gfx/cairo/cairo/src/cairo-fixed-private.h index 27cb89988740..a37ca6a70a50 100644 --- a/gfx/cairo/cairo/src/cairo-fixed-private.h +++ b/gfx/cairo/cairo/src/cairo-fixed-private.h @@ -224,6 +224,15 @@ _cairo_fixed_16_16_from_double (double d) #endif } +static inline int +_cairo_fixed_16_16_floor (cairo_fixed_t f) +{ + if (f >= 0) + return f >> 16; + else + return -((-f - 1) >> 16) - 1; +} + #if CAIRO_FIXED_BITS == 32 static inline cairo_fixed_t @@ -233,15 +242,61 @@ _cairo_fixed_mul (cairo_fixed_t a, cairo_fixed_t b) return _cairo_int64_to_int32(_cairo_int64_rsl (temp, CAIRO_FIXED_FRAC_BITS)); } -/* computes a * b / c */ +/* computes round (a * b / c) */ static inline cairo_fixed_t _cairo_fixed_mul_div (cairo_fixed_t a, cairo_fixed_t b, cairo_fixed_t c) { cairo_int64_t ab = _cairo_int32x32_64_mul (a, b); cairo_int64_t c64 = _cairo_int32_to_int64 (c); - cairo_int64_t quo = _cairo_int64_divrem (ab, c64).quo; + return _cairo_int64_to_int32 (_cairo_int64_divrem (ab, c64).quo); +} - return _cairo_int64_to_int32(quo); +/* computes floor (a * b / c) */ +static inline cairo_fixed_t +_cairo_fixed_mul_div_floor (cairo_fixed_t a, cairo_fixed_t b, cairo_fixed_t c) +{ + return _cairo_int64_32_div (_cairo_int32x32_64_mul (a, b), c); +} + + +static inline cairo_fixed_t +_cairo_edge_compute_intersection_y_for_x (const cairo_point_t *p1, + const cairo_point_t *p2, + cairo_fixed_t x) +{ + cairo_fixed_t y, dx; + + if (x == p1->x) + return p1->y; + if (x == p2->x) + return p2->y; + + y = p1->y; + dx = p2->x - p1->x; + if (dx != 0) + y += _cairo_fixed_mul_div_floor (x - p1->x, p2->y - p1->y, dx); + + return y; +} + +static inline cairo_fixed_t +_cairo_edge_compute_intersection_x_for_y (const cairo_point_t *p1, + const cairo_point_t *p2, + cairo_fixed_t y) +{ + cairo_fixed_t x, dy; + + if (y == p1->y) + return p1->x; + if (y == p2->y) + return p2->x; + + x = p1->x; + dy = p2->y - p1->y; + if (dy != 0) + x += _cairo_fixed_mul_div_floor (y - p1->y, p2->x - p1->x, dy); + + return x; } #else diff --git a/gfx/cairo/cairo/src/cairo-fixed-type-private.h b/gfx/cairo/cairo/src/cairo-fixed-type-private.h index 0c0168975313..ce3c007f5851 100644 --- a/gfx/cairo/cairo/src/cairo-fixed-type-private.h +++ b/gfx/cairo/cairo/src/cairo-fixed-type-private.h @@ -67,4 +67,9 @@ typedef int32_t cairo_fixed_t; /* An unsigned type of the same size as #cairo_fixed_t */ typedef uint32_t cairo_fixed_unsigned_t; +typedef struct _cairo_point { + cairo_fixed_t x; + cairo_fixed_t y; +} cairo_point_t; + #endif /* CAIRO_FIXED_TYPE_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-font-face-twin.c b/gfx/cairo/cairo/src/cairo-font-face-twin.c index 3f82733245ad..56ebaeaa6a4a 100644 --- a/gfx/cairo/cairo/src/cairo-font-face-twin.c +++ b/gfx/cairo/cairo/src/cairo-font-face-twin.c @@ -294,6 +294,8 @@ twin_font_face_create_properties (cairo_font_face_t *twin_face, return _cairo_error (CAIRO_STATUS_NO_MEMORY); props->stretch = TWIN_STRETCH_NORMAL; + props->slant = CAIRO_FONT_SLANT_NORMAL; + props->weight = TWIN_WEIGHT_NORMAL; props->monospace = FALSE; props->smallcaps = FALSE; diff --git a/gfx/cairo/cairo/src/cairo-freelist-private.h b/gfx/cairo/cairo/src/cairo-freelist-private.h index 48791c2347f8..5be22b1f9646 100644 --- a/gfx/cairo/cairo/src/cairo-freelist-private.h +++ b/gfx/cairo/cairo/src/cairo-freelist-private.h @@ -25,19 +25,40 @@ #include "cairo-types-private.h" #include "cairo-compiler-private.h" -/* Opaque implementation types. */ -typedef struct _cairo_freelist cairo_freelist_t; -typedef struct _cairo_freelist_node cairo_freelist_node_t; +/* for stand-alone compilation*/ +#ifndef VG +#define VG(x) +#endif +#ifndef NULL +#define NULL (void *) 0 +#endif + +typedef struct _cairo_freelist_node cairo_freelist_node_t; struct _cairo_freelist_node { cairo_freelist_node_t *next; }; -struct _cairo_freelist { +typedef struct _cairo_freelist { cairo_freelist_node_t *first_free_node; unsigned nodesize; +} cairo_freelist_t; + +typedef struct _cairo_freelist_pool cairo_freelist_pool_t; +struct _cairo_freelist_pool { + cairo_freelist_pool_t *next; + unsigned size, rem; + uint8_t *data; }; +typedef struct _cairo_freepool { + cairo_freelist_node_t *first_free_node; + cairo_freelist_pool_t *pools; + unsigned nodesize; + cairo_freelist_pool_t embedded_pool; + uint8_t embedded_data[1000]; +} cairo_freepool_t; + /* Initialise a freelist that will be responsible for allocating * nodes of size nodesize. */ @@ -68,4 +89,62 @@ _cairo_freelist_calloc (cairo_freelist_t *freelist); cairo_private void _cairo_freelist_free (cairo_freelist_t *freelist, void *node); + +cairo_private void +_cairo_freepool_init (cairo_freepool_t *freepool, unsigned nodesize); + +cairo_private void +_cairo_freepool_fini (cairo_freepool_t *freepool); + +cairo_private void * +_cairo_freepool_alloc_from_new_pool (cairo_freepool_t *freepool); + +static inline void * +_cairo_freepool_alloc_from_pool (cairo_freepool_t *freepool) +{ + cairo_freelist_pool_t *pool; + uint8_t *ptr; + + pool = freepool->pools; + if (unlikely (freepool->nodesize > pool->rem)) + return _cairo_freepool_alloc_from_new_pool (freepool); + + ptr = pool->data; + pool->data += freepool->nodesize; + pool->rem -= freepool->nodesize; + VG (VALGRIND_MAKE_MEM_UNDEFINED (ptr, freepool->nodesize)); + return ptr; +} + +static inline void * +_cairo_freepool_alloc (cairo_freepool_t *freepool) +{ + cairo_freelist_node_t *node; + + node = freepool->first_free_node; + if (unlikely (node == NULL)) + return _cairo_freepool_alloc_from_pool (freepool); + + VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next))); + freepool->first_free_node = node->next; + VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freepool->nodesize)); + + return node; +} + +cairo_private cairo_status_t +_cairo_freepool_alloc_array (cairo_freepool_t *freepool, + int count, + void **array); + +static inline void +_cairo_freepool_free (cairo_freepool_t *freepool, void *ptr) +{ + cairo_freelist_node_t *node = ptr; + + node->next = freepool->first_free_node; + freepool->first_free_node = node; + VG (VALGRIND_MAKE_MEM_NOACCESS (node, freepool->nodesize)); +} + #endif /* CAIRO_FREELIST_H */ diff --git a/gfx/cairo/cairo/src/cairo-freelist.c b/gfx/cairo/cairo/src/cairo-freelist.c index f6eb4fed17f4..acf31573f048 100644 --- a/gfx/cairo/cairo/src/cairo-freelist.c +++ b/gfx/cairo/cairo/src/cairo-freelist.c @@ -82,3 +82,92 @@ _cairo_freelist_free (cairo_freelist_t *freelist, void *voidnode) VG (VALGRIND_MAKE_MEM_NOACCESS (node, freelist->nodesize)); } } + + +void +_cairo_freepool_init (cairo_freepool_t *freepool, unsigned nodesize) +{ + freepool->first_free_node = NULL; + freepool->pools = &freepool->embedded_pool; + freepool->nodesize = nodesize; + + freepool->embedded_pool.next = NULL; + freepool->embedded_pool.size = sizeof (freepool->embedded_data); + freepool->embedded_pool.rem = sizeof (freepool->embedded_data); + freepool->embedded_pool.data = freepool->embedded_data; + + VG (VALGRIND_MAKE_MEM_NOACCESS (freepool->embedded_data, + sizeof (freepool->embedded_data))); +} + +void +_cairo_freepool_fini (cairo_freepool_t *freepool) +{ + cairo_freelist_pool_t *pool = freepool->pools; + while (pool != &freepool->embedded_pool) { + cairo_freelist_pool_t *next = pool->next; + free (pool); + pool = next; + } + VG (VALGRIND_MAKE_MEM_NOACCESS (freepool, sizeof (freepool))); +} + +void * +_cairo_freepool_alloc_from_new_pool (cairo_freepool_t *freepool) +{ + cairo_freelist_pool_t *pool; + int poolsize; + + if (freepool->pools != &freepool->embedded_pool) + poolsize = 2 * freepool->pools->size; + else + poolsize = (128 * freepool->nodesize + 8191) & -8192; + pool = malloc (sizeof (cairo_freelist_pool_t) + poolsize); + if (unlikely (pool == NULL)) + return pool; + + pool->next = freepool->pools; + freepool->pools = pool; + + pool->size = poolsize; + pool->rem = poolsize - freepool->nodesize; + pool->data = (uint8_t *) (pool + 1) + freepool->nodesize; + + VG (VALGRIND_MAKE_MEM_NOACCESS (pool->data, poolsize)); + VG (VALGRIND_MAKE_MEM_UNDEFINED (pool->data, freepool->nodesize)); + + return pool + 1; +} + +cairo_status_t +_cairo_freepool_alloc_array (cairo_freepool_t *freepool, + int count, + void **array) +{ + int i; + + for (i = 0; i < count; i++) { + cairo_freelist_node_t *node; + + node = freepool->first_free_node; + if (likely (node != NULL)) { + VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next))); + freepool->first_free_node = node->next; + VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freepool->nodesize)); + } else { + node = _cairo_freepool_alloc_from_pool (freepool); + if (unlikely (node == NULL)) + goto CLEANUP; + } + + array[i] = node; + } + + return CAIRO_STATUS_SUCCESS; + + CLEANUP: + while (i--) + _cairo_freepool_free (freepool, array[i]); + + return _cairo_error (CAIRO_STATUS_NO_MEMORY); +} diff --git a/gfx/cairo/cairo/src/cairo-ft-font.c b/gfx/cairo/cairo/src/cairo-ft-font.c index 9de44bfffbf5..e94cdb73aec3 100644 --- a/gfx/cairo/cairo/src/cairo-ft-font.c +++ b/gfx/cairo/cairo/src/cairo-ft-font.c @@ -808,7 +808,6 @@ _get_bitmap_surface (FT_Bitmap *bitmap, int width, height, stride; unsigned char *data; int format = CAIRO_FORMAT_A8; - cairo_bool_t subpixel = FALSE; width = bitmap->width; height = bitmap->rows; @@ -976,7 +975,6 @@ _get_bitmap_surface (FT_Bitmap *bitmap, data = data_rgba; stride = stride_rgba; format = CAIRO_FORMAT_ARGB32; - subpixel = TRUE; break; } } @@ -999,9 +997,6 @@ _get_bitmap_surface (FT_Bitmap *bitmap, return (*surface)->base.status; } - if (subpixel) - pixman_image_set_component_alpha ((*surface)->pixman_image, TRUE); - _cairo_image_surface_assume_ownership_of_data ((*surface)); _cairo_debug_check_image_surface_is_defined (&(*surface)->base); @@ -1152,7 +1147,7 @@ _render_glyph_bitmap (FT_Face face, cairo_image_surface_t **surface) { FT_GlyphSlot glyphslot = face->glyph; - cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_status_t status; FT_Error error; /* According to the FreeType docs, glyphslot->format could be @@ -1168,7 +1163,9 @@ _render_glyph_bitmap (FT_Face face, if (error == FT_Err_Out_Of_Memory) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - status = _get_bitmap_surface (&glyphslot->bitmap, FALSE, font_options, surface); + status = _get_bitmap_surface (&glyphslot->bitmap, + FALSE, font_options, + surface); if (unlikely (status)) return status; @@ -1183,7 +1180,7 @@ _render_glyph_bitmap (FT_Face face, -glyphslot->bitmap_left, +glyphslot->bitmap_top); - return status; + return CAIRO_STATUS_SUCCESS; } static cairo_status_t @@ -1281,11 +1278,8 @@ _transform_glyph_bitmap (cairo_matrix_t * shape, _cairo_pattern_init_for_surface (&pattern, &(*surface)->base); cairo_pattern_set_matrix (&pattern.base, &transformed_to_original); - status = _cairo_surface_composite (CAIRO_OPERATOR_OVER, - &pattern.base, NULL, image, - 0, 0, 0, 0, 0, 0, - width, - height); + status = _cairo_surface_paint (image, CAIRO_OPERATOR_OVER, + &pattern.base, NULL); _cairo_pattern_fini (&pattern.base); @@ -1307,7 +1301,7 @@ _transform_glyph_bitmap (cairo_matrix_t * shape, cairo_surface_set_device_offset (&(*surface)->base, _cairo_lround (origin_x), _cairo_lround (origin_y)); - return status; + return CAIRO_STATUS_SUCCESS; } static const cairo_unscaled_font_backend_t cairo_ft_unscaled_font_backend = { @@ -1396,8 +1390,8 @@ _get_pattern_ft_options (FcPattern *pattern, cairo_ft_options_t *ret) ft_options.base.antialias = CAIRO_ANTIALIAS_SUBPIXEL; } -#ifdef FC_HINT_STYLE - if (FcPatternGetInteger (pattern, +#ifdef FC_HINT_STYLE + if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hintstyle) != FcResultMatch) hintstyle = FC_HINT_FULL; @@ -1406,7 +1400,7 @@ _get_pattern_ft_options (FcPattern *pattern, cairo_ft_options_t *ret) switch (hintstyle) { case FC_HINT_NONE: - ft_options.base.hint_style = CAIRO_HINT_STYLE_NONE; + ft_options.base.hint_style = CAIRO_HINT_STYLE_NONE; break; case FC_HINT_SLIGHT: ft_options.base.hint_style = CAIRO_HINT_STYLE_SLIGHT; @@ -1450,14 +1444,14 @@ _get_pattern_ft_options (FcPattern *pattern, cairo_ft_options_t *ret) if (vertical_layout) ft_options.load_flags |= FT_LOAD_VERTICAL_LAYOUT; - + #ifndef FC_EMBOLDEN #define FC_EMBOLDEN "embolden" #endif if (FcPatternGetBool (pattern, FC_EMBOLDEN, 0, &embolden) != FcResultMatch) embolden = FcFalse; - + if (embolden) ft_options.extra_flags |= CAIRO_FT_OPTIONS_EMBOLDEN; @@ -1474,7 +1468,7 @@ _cairo_ft_options_merge (cairo_ft_options_t *options, /* clear load target mode */ load_flags &= ~(FT_LOAD_TARGET_(FT_LOAD_TARGET_MODE(other->load_flags))); - + if (load_flags & FT_LOAD_NO_HINTING) other->base.hint_style = CAIRO_HINT_STYLE_NONE; @@ -1485,7 +1479,7 @@ _cairo_ft_options_merge (cairo_ft_options_t *options, } if (other->base.antialias == CAIRO_ANTIALIAS_SUBPIXEL && - (options->base.antialias == CAIRO_ANTIALIAS_DEFAULT || + (options->base.antialias == CAIRO_ANTIALIAS_DEFAULT || options->base.antialias == CAIRO_ANTIALIAS_GRAY)) { options->base.antialias = CAIRO_ANTIALIAS_SUBPIXEL; options->base.subpixel_order = other->base.subpixel_order; @@ -1940,12 +1934,12 @@ _cairo_ft_scaled_glyph_init (void *abstract_font, x2 = (metrics->horiBearingX + metrics->width + 63) & -64; y1 = (-metrics->horiBearingY) & -64; y2 = (-metrics->horiBearingY + metrics->height + 63) & -64; - + advance = ((metrics->horiAdvance + 32) & -64); - + fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor; fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor; - + fs_metrics.width = DOUBLE_FROM_26_6 (x2 - x1) * x_factor; fs_metrics.height = DOUBLE_FROM_26_6 (y2 - y1) * y_factor; @@ -1956,12 +1950,12 @@ _cairo_ft_scaled_glyph_init (void *abstract_font, x2 = (metrics->vertBearingX + metrics->width + 63) & -64; y1 = (metrics->vertBearingY) & -64; y2 = (metrics->vertBearingY + metrics->height + 63) & -64; - + advance = ((metrics->vertAdvance + 32) & -64); - + fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor; fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor; - + fs_metrics.width = DOUBLE_FROM_26_6 (x2 - x1) * x_factor; fs_metrics.height = DOUBLE_FROM_26_6 (y2 - y1) * y_factor; @@ -1975,7 +1969,7 @@ _cairo_ft_scaled_glyph_init (void *abstract_font, if (!vertical_layout) { fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) * x_factor; fs_metrics.y_bearing = DOUBLE_FROM_26_6 (-metrics->horiBearingY) * y_factor; - + if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE) fs_metrics.x_advance = DOUBLE_FROM_26_6 (metrics->horiAdvance) * x_factor; else @@ -1984,7 +1978,7 @@ _cairo_ft_scaled_glyph_init (void *abstract_font, } else { fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingX) * x_factor; fs_metrics.y_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingY) * y_factor; - + fs_metrics.x_advance = 0 * x_factor; if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE) fs_metrics.y_advance = DOUBLE_FROM_26_6 (metrics->vertAdvance) * y_factor; @@ -2142,11 +2136,11 @@ _cairo_ft_index_to_ucs4(void *abstract_font, *ucs4 = (uint32_t) -1; charcode = FT_Get_First_Char(face, &gindex); while (gindex != 0) { - charcode = FT_Get_Next_Char (face, charcode, &gindex); if (gindex == index) { *ucs4 = charcode; break; } + charcode = FT_Get_Next_Char (face, charcode, &gindex); } _cairo_ft_unscaled_font_unlock_face (unscaled); @@ -2160,7 +2154,7 @@ static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend = { _cairo_ft_scaled_glyph_init, NULL, /* text_to_glyphs */ _cairo_ft_ucs4_to_index, - NULL, /* show_glyphs */ + NULL, /* show_glyphs */ _cairo_ft_load_truetype_table, _cairo_ft_index_to_ucs4 }; @@ -2241,12 +2235,6 @@ _cairo_ft_font_face_destroy (void *abstract_face) { cairo_ft_font_face_t *font_face = abstract_face; - cairo_ft_font_face_t *tmp_face = NULL; - cairo_ft_font_face_t *last_face = NULL; - - if (font_face == NULL) - return; - /* When destroying a face created by cairo_ft_font_face_create_for_ft_face, * we have a special "zombie" state for the face when the unscaled font * is still alive but there are no other references to a font face with @@ -2277,6 +2265,9 @@ _cairo_ft_font_face_destroy (void *abstract_face) } if (font_face->unscaled) { + cairo_ft_font_face_t *tmp_face = NULL; + cairo_ft_font_face_t *last_face = NULL; + /* Remove face from linked list */ for (tmp_face = font_face->unscaled->faces; tmp_face; @@ -2341,12 +2332,15 @@ _cairo_ft_font_face_get_implementation (void *abstract_face, return cairo_font_face_reference (resolved); cairo_font_face_destroy (resolved); + font_face->resolved_font_face = NULL; } resolved = _cairo_ft_resolve_pattern (font_face->pattern, font_matrix, ctm, options); + if (unlikely (resolved->status)) + return resolved; font_face->resolved_font_face = cairo_font_face_reference (resolved); font_face->resolved_config = FcConfigGetCurrent (); @@ -2917,10 +2911,10 @@ cairo_bool_t _cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font) { cairo_ft_scaled_font_t *ft_scaled_font; - + if (!_cairo_scaled_font_is_ft (scaled_font)) return FALSE; - + ft_scaled_font = (cairo_ft_scaled_font_t *) scaled_font; if (ft_scaled_font->ft_options.load_flags & FT_LOAD_VERTICAL_LAYOUT) return TRUE; diff --git a/gfx/cairo/cairo/src/cairo-gl-glyphs.c b/gfx/cairo/cairo/src/cairo-gl-glyphs.c new file mode 100644 index 000000000000..dbc71003355b --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-glyphs.c @@ -0,0 +1,797 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Chris Wilson. + */ + +#include "cairoint.h" + +#include "cairo-gl-private.h" +#include "cairo-rtree-private.h" + +#define GLYPH_CACHE_WIDTH 1024 +#define GLYPH_CACHE_HEIGHT 1024 +#define GLYPH_CACHE_MIN_SIZE 4 +#define GLYPH_CACHE_MAX_SIZE 128 + +typedef struct _cairo_gl_glyph_private { + cairo_rtree_node_t node; + cairo_gl_glyph_cache_t *cache; + struct { float x, y; } p1, p2; +} cairo_gl_glyph_private_t; + +static cairo_status_t +_cairo_gl_glyph_cache_add_glyph (cairo_gl_glyph_cache_t *cache, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_image_surface_t *glyph_surface = scaled_glyph->surface; + cairo_gl_glyph_private_t *glyph_private; + cairo_rtree_node_t *node = NULL; + cairo_image_surface_t *clone = NULL; + cairo_status_t status; + int width, height; + GLenum internal_format, format, type; + cairo_bool_t has_alpha; + + if (! _cairo_gl_get_image_format_and_type (glyph_surface->pixman_format, + &internal_format, + &format, + &type, + &has_alpha)) + { + cairo_bool_t is_supported; + + clone = _cairo_image_surface_coerce (glyph_surface, + _cairo_format_from_content (glyph_surface->base.content)); + if (unlikely (clone->base.status)) + return clone->base.status; + + is_supported = + _cairo_gl_get_image_format_and_type (clone->pixman_format, + &internal_format, + &format, + &type, + &has_alpha); + assert (is_supported); + glyph_surface = clone; + } + + width = glyph_surface->width; + if (width < GLYPH_CACHE_MIN_SIZE) + width = GLYPH_CACHE_MIN_SIZE; + height = glyph_surface->height; + if (height < GLYPH_CACHE_MIN_SIZE) + height = GLYPH_CACHE_MIN_SIZE; + + /* search for an available slot */ + status = _cairo_rtree_insert (&cache->rtree, width, height, &node); + /* search for an unlocked slot */ + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = _cairo_rtree_evict_random (&cache->rtree, + width, height, &node); + if (status == CAIRO_STATUS_SUCCESS) { + status = _cairo_rtree_node_insert (&cache->rtree, + node, width, height, &node); + } + } + if (status) + return status; + + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + glPixelStorei (GL_UNPACK_ROW_LENGTH, + glyph_surface->stride / + (PIXMAN_FORMAT_BPP (glyph_surface->pixman_format) / 8)); + glTexSubImage2D (GL_TEXTURE_2D, 0, + node->x, node->y, + glyph_surface->width, glyph_surface->height, + format, type, + glyph_surface->data); + glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); + + scaled_glyph->surface_private = node; + node->owner = &scaled_glyph->surface_private; + + glyph_private = (cairo_gl_glyph_private_t *) node; + glyph_private->cache = cache; + + /* compute tex coords */ + glyph_private->p1.x = node->x / (double) cache->width; + glyph_private->p1.y = node->y / (double) cache->height; + glyph_private->p2.x = + (node->x + glyph_surface->width) / (double) cache->width; + glyph_private->p2.y = + (node->y + glyph_surface->height) / (double) cache->height; + + cairo_surface_destroy (&clone->base); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_gl_glyph_private_t * +_cairo_gl_glyph_cache_lock (cairo_gl_glyph_cache_t *cache, + cairo_scaled_glyph_t *scaled_glyph) +{ + return _cairo_rtree_pin (&cache->rtree, scaled_glyph->surface_private); +} + +static cairo_gl_glyph_cache_t * +cairo_gl_context_get_glyph_cache (cairo_gl_context_t *ctx, + cairo_format_t format) +{ + cairo_gl_glyph_cache_t *cache; + + switch (format) { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + cache = &ctx->glyph_cache[0]; + format = CAIRO_FORMAT_ARGB32; + break; + case CAIRO_FORMAT_A8: + case CAIRO_FORMAT_A1: + cache = &ctx->glyph_cache[1]; + format = CAIRO_FORMAT_A8; + break; + } + + if (unlikely (cache->tex == 0)) { + GLenum internal_format; + + cache->width = GLYPH_CACHE_WIDTH; + cache->height = GLYPH_CACHE_HEIGHT; + + switch (format) { + case CAIRO_FORMAT_A1: + case CAIRO_FORMAT_RGB24: + ASSERT_NOT_REACHED; + case CAIRO_FORMAT_ARGB32: + internal_format = GL_RGBA; + break; + case CAIRO_FORMAT_A8: + internal_format = GL_ALPHA; + break; + } + + glGenTextures (1, &cache->tex); + glBindTexture (GL_TEXTURE_2D, cache->tex); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D (GL_TEXTURE_2D, 0, internal_format, + GLYPH_CACHE_WIDTH, GLYPH_CACHE_HEIGHT, 0, + internal_format, GL_FLOAT, NULL); + } + + return cache; +} + +static void +_cairo_gl_glyph_cache_unlock (cairo_gl_glyph_cache_t *cache) +{ + _cairo_rtree_unpin (&cache->rtree); +} + +static cairo_bool_t +_cairo_gl_surface_owns_font (cairo_gl_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + cairo_gl_context_t *font_private; + + font_private = scaled_font->surface_private; + if ((scaled_font->surface_backend != NULL && + scaled_font->surface_backend != &_cairo_gl_surface_backend) || + (font_private != NULL && font_private != surface->ctx)) + { + return FALSE; + } + + return TRUE; +} + +void +_cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font) +{ + cairo_gl_glyph_private_t *glyph_private; + + glyph_private = scaled_glyph->surface_private; + if (glyph_private != NULL) { + glyph_private->node.owner = NULL; + if (! glyph_private->node.pinned) { + /* XXX thread-safety? Probably ok due to the frozen scaled-font. */ + _cairo_rtree_node_remove (&glyph_private->cache->rtree, + &glyph_private->node); + } + } +} + +typedef struct _cairo_gl_glyphs_setup +{ + unsigned int vbo_size; /* units of floats */ + unsigned int vb_offset; /* units of floats */ + unsigned int vertex_size; /* units of floats */ + unsigned int num_prims; + float *vb; + cairo_gl_composite_setup_t *composite; + cairo_region_t *clip; + cairo_gl_surface_t *dst; +} cairo_gl_glyphs_setup_t; + +static void +_cairo_gl_flush_glyphs (cairo_gl_context_t *ctx, + cairo_gl_glyphs_setup_t *setup) +{ + int i; + + if (setup->vb != NULL) { + glUnmapBufferARB (GL_ARRAY_BUFFER_ARB); + setup->vb = NULL; + + if (setup->num_prims != 0) { + if (setup->clip) { + int num_rectangles = cairo_region_num_rectangles (setup->clip); + + glEnable (GL_SCISSOR_TEST); + for (i = 0; i < num_rectangles; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (setup->clip, i, &rect); + + glScissor (rect.x, + _cairo_gl_y_flip (setup->dst, rect.y), + rect.x + rect.width, + _cairo_gl_y_flip (setup->dst, + rect.y + rect.height)); + glDrawArrays (GL_QUADS, 0, 4 * setup->num_prims); + } + glDisable (GL_SCISSOR_TEST); + } else { + glDrawArrays (GL_QUADS, 0, 4 * setup->num_prims); + } + setup->num_prims = 0; + } + } +} + +static void +_cairo_gl_glyphs_emit_vertex (cairo_gl_glyphs_setup_t *setup, + int x, int y, float glyph_x, float glyph_y) +{ + int i = 0; + float *vb = &setup->vb[setup->vb_offset]; + cairo_surface_attributes_t *src_attributes; + + src_attributes = &setup->composite->src.operand.texture.attributes; + + vb[i++] = x; + vb[i++] = y; + + vb[i++] = glyph_x; + vb[i++] = glyph_y; + + if (setup->composite->src.type == OPERAND_TEXTURE) { + double s = x; + double t = y; + cairo_matrix_transform_point (&src_attributes->matrix, &s, &t); + vb[i++] = s; + vb[i++] = t; + } + + setup->vb_offset += setup->vertex_size; +} + + +static void +_cairo_gl_emit_glyph_rectangle (cairo_gl_context_t *ctx, + cairo_gl_glyphs_setup_t *setup, + int x1, int y1, + int x2, int y2, + cairo_gl_glyph_private_t *glyph) +{ + if (setup->vb != NULL && + setup->vb_offset + 4 * setup->vertex_size > setup->vbo_size) { + _cairo_gl_flush_glyphs (ctx, setup); + } + + if (setup->vb == NULL) { + glBufferDataARB (GL_ARRAY_BUFFER_ARB, + setup->vbo_size * sizeof (GLfloat), + NULL, GL_STREAM_DRAW_ARB); + setup->vb = glMapBufferARB (GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); + setup->vb_offset = 0; + } + + _cairo_gl_glyphs_emit_vertex (setup, x1, y1, glyph->p1.x, glyph->p1.y); + _cairo_gl_glyphs_emit_vertex (setup, x2, y1, glyph->p2.x, glyph->p1.y); + _cairo_gl_glyphs_emit_vertex (setup, x2, y2, glyph->p2.x, glyph->p2.y); + _cairo_gl_glyphs_emit_vertex (setup, x1, y2, glyph->p1.x, glyph->p2.y); + setup->num_prims++; +} + +static cairo_status_t +_render_glyphs (cairo_gl_surface_t *dst, + int dst_x, int dst_y, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_rectangle_int_t *glyph_extents, + cairo_scaled_font_t *scaled_font, + cairo_bool_t *has_component_alpha, + cairo_region_t *clip_region, + int *remaining_glyphs) +{ + cairo_format_t last_format = (cairo_format_t) -1; + cairo_gl_glyph_cache_t *cache = NULL; + cairo_gl_context_t *ctx; + cairo_gl_glyphs_setup_t setup; + cairo_gl_composite_setup_t composite_setup; + cairo_status_t status; + int i = 0; + GLuint vbo = 0; + + *has_component_alpha = FALSE; + + status = _cairo_gl_operand_init (&composite_setup.src, source, dst, + glyph_extents->x, glyph_extents->y, + dst_x, dst_y, + glyph_extents->width, + glyph_extents->height); + if (unlikely (status)) + return status; + + ctx = _cairo_gl_context_acquire (dst->ctx); + + /* Set up the IN operator for source IN mask. + * + * IN (normal, any op): dst.argb = src.argb * mask.aaaa + * IN (component, ADD): dst.argb = src.argb * mask.argb + * + * The mask channel selection for component alpha ADD will be updated in + * the loop over glyphs below. + */ + glActiveTexture (GL_TEXTURE1); + glEnable (GL_TEXTURE_2D); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE1); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE1); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + + glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_PREVIOUS); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + + _cairo_gl_set_destination (dst); + /* If we're doing some CA glyphs, we're only doing it for ADD, + * which doesn't require the source alpha value in blending. + */ + _cairo_gl_set_operator (dst, op, FALSE); + _cairo_gl_set_src_operand (ctx, &composite_setup); + + _cairo_scaled_font_freeze_cache (scaled_font); + if (! _cairo_gl_surface_owns_font (dst, scaled_font)) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto CLEANUP_FONT; + } + + if (scaled_font->surface_private == NULL) { + /* XXX couple into list to remove on context destruction */ + scaled_font->surface_private = ctx; + scaled_font->surface_backend = &_cairo_gl_surface_backend; + } + + /* Create our VBO so that we can accumulate a bunch of glyph primitives + * into one giant DrawArrays. + */ + memset(&setup, 0, sizeof(setup)); + setup.composite = &composite_setup; + setup.clip = clip_region; + setup.dst = dst; + setup.vertex_size = 4; + if (composite_setup.src.type == OPERAND_TEXTURE) + setup.vertex_size += 2; + setup.vbo_size = num_glyphs * 4 * setup.vertex_size; + if (setup.vbo_size > 4096) + setup.vbo_size = 4096; + + glGenBuffersARB (1, &vbo); + glBindBufferARB (GL_ARRAY_BUFFER_ARB, vbo); + + glVertexPointer (2, GL_FLOAT, setup.vertex_size * sizeof (GLfloat), + (void *)(uintptr_t)(0)); + glEnableClientState (GL_VERTEX_ARRAY); + if (composite_setup.src.type == OPERAND_TEXTURE) { + /* Note that we're packing texcoord 0 after texcoord 1, for + * convenience. + */ + glClientActiveTexture (GL_TEXTURE0); + glTexCoordPointer (2, GL_FLOAT, setup.vertex_size * sizeof (GLfloat), + (void *)(uintptr_t)(4 * sizeof (GLfloat))); + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + } + glClientActiveTexture (GL_TEXTURE1); + glTexCoordPointer (2, GL_FLOAT, setup.vertex_size * sizeof (GLfloat), + (void *)(uintptr_t)(2 * sizeof (GLfloat))); + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + + for (i = 0; i < num_glyphs; i++) { + cairo_scaled_glyph_t *scaled_glyph; + double x_offset, y_offset; + double x1, x2, y1, y2; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (unlikely (status)) + goto FINISH; + + if (scaled_glyph->surface->width == 0 || + scaled_glyph->surface->height == 0) + { + continue; + } + if (scaled_glyph->surface->width > GLYPH_CACHE_MAX_SIZE || + scaled_glyph->surface->height > GLYPH_CACHE_MAX_SIZE) + { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto FINISH; + } + + if (scaled_glyph->surface->format != last_format) { + /* Switching textures, so flush any queued prims. */ + _cairo_gl_flush_glyphs (ctx, &setup); + + glActiveTexture (GL_TEXTURE1); + cache = cairo_gl_context_get_glyph_cache (ctx, + scaled_glyph->surface->format); + + glBindTexture (GL_TEXTURE_2D, cache->tex); + + last_format = scaled_glyph->surface->format; + /* If we're doing component alpha in this function, it should + * only be in the case of CAIRO_OPERATOR_ADD. In that case, we just + * need to make sure we send the rgb bits down to the destination. + */ + if (last_format == CAIRO_FORMAT_ARGB32) { + assert (op == CAIRO_OPERATOR_ADD); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + *has_component_alpha = TRUE; + } else { + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA); + } + + /* XXX component alpha */ + } + + if (scaled_glyph->surface_private == NULL) { + status = _cairo_gl_glyph_cache_add_glyph (cache, scaled_glyph); + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + /* Cache is full, so flush existing prims and try again. */ + _cairo_gl_flush_glyphs (ctx, &setup); + _cairo_gl_glyph_cache_unlock (cache); + status = _cairo_gl_glyph_cache_add_glyph (cache, scaled_glyph); + } + + if (unlikely (_cairo_status_is_error (status))) + goto FINISH; + } + + x_offset = scaled_glyph->surface->base.device_transform.x0; + y_offset = scaled_glyph->surface->base.device_transform.y0; + + x1 = _cairo_lround (glyphs[i].x - x_offset); + y1 = _cairo_lround (glyphs[i].y - y_offset); + x2 = x1 + scaled_glyph->surface->width; + y2 = y1 + scaled_glyph->surface->height; + + _cairo_gl_emit_glyph_rectangle (ctx, &setup, + x1, y1, x2, y2, + _cairo_gl_glyph_cache_lock (cache, scaled_glyph)); + } + + status = CAIRO_STATUS_SUCCESS; + FINISH: + _cairo_gl_flush_glyphs (ctx, &setup); + CLEANUP_FONT: + _cairo_scaled_font_thaw_cache (scaled_font); + + glDisable (GL_BLEND); + glDisable (GL_SCISSOR_TEST); + + glDisableClientState (GL_VERTEX_ARRAY); + + glClientActiveTexture (GL_TEXTURE0); + glDisableClientState (GL_TEXTURE_COORD_ARRAY); + glActiveTexture (GL_TEXTURE0); + glDisable (GL_TEXTURE_2D); + + glClientActiveTexture (GL_TEXTURE1); + glDisableClientState (GL_TEXTURE_COORD_ARRAY); + glActiveTexture (GL_TEXTURE1); + glDisable (GL_TEXTURE_2D); + + if (vbo != 0) { + glBindBufferARB (GL_ARRAY_BUFFER_ARB, 0); + glDeleteBuffersARB (1, &vbo); + } + + _cairo_gl_context_release (ctx); + + _cairo_gl_operand_destroy (&composite_setup.src); + + *remaining_glyphs = num_glyphs - i; + return status; +} + +static cairo_int_status_t +_cairo_gl_surface_show_glyphs_via_mask (cairo_gl_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_rectangle_int_t *glyph_extents, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs) +{ + cairo_surface_t *mask; + cairo_status_t status; + cairo_solid_pattern_t solid; + cairo_bool_t has_component_alpha; + int i; + + /* XXX: For non-CA, this should be CAIRO_CONTENT_ALPHA to save memory */ + mask = cairo_gl_surface_create (dst->ctx, + CAIRO_CONTENT_COLOR_ALPHA, + glyph_extents->width, + glyph_extents->height); + if (unlikely (mask->status)) + return mask->status; + + for (i = 0; i < num_glyphs; i++) { + glyphs[i].x -= glyph_extents->x; + glyphs[i].y -= glyph_extents->y; + } + + _cairo_pattern_init_solid (&solid, CAIRO_COLOR_WHITE, CAIRO_CONTENT_COLOR_ALPHA); + status = _render_glyphs ((cairo_gl_surface_t *) mask, 0, 0, + CAIRO_OPERATOR_ADD, &solid.base, + glyphs, num_glyphs, glyph_extents, + scaled_font, &has_component_alpha, + NULL, remaining_glyphs); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + cairo_surface_pattern_t mask_pattern; + + mask->is_clear = FALSE; + _cairo_pattern_init_for_surface (&mask_pattern, mask); + mask_pattern.base.has_component_alpha = has_component_alpha; + cairo_matrix_init_translate (&mask_pattern.base.matrix, + -glyph_extents->x, -glyph_extents->y); + status = _cairo_surface_mask (&dst->base, op, + source, &mask_pattern.base, clip); + _cairo_pattern_fini (&mask_pattern.base); + } else { + for (i = 0; i < num_glyphs; i++) { + glyphs[i].x += glyph_extents->x; + glyphs[i].y += glyph_extents->y; + } + *remaining_glyphs = num_glyphs; + } + + cairo_surface_destroy (mask); + return status; +} + +cairo_int_status_t +_cairo_gl_surface_show_glyphs (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs) +{ + cairo_gl_surface_t *dst = abstract_dst; + cairo_rectangle_int_t surface_extents; + cairo_rectangle_int_t extents; + cairo_region_t *clip_region = NULL; + cairo_solid_pattern_t solid_pattern; + cairo_bool_t overlap, use_mask = FALSE; + cairo_bool_t has_component_alpha; + cairo_status_t status; + int i; + + if (! GLEW_ARB_vertex_buffer_object) + return UNSUPPORTED ("requires ARB_vertex_buffer_object"); + + if (! _cairo_gl_operator_is_supported (op)) + return UNSUPPORTED ("unsupported operator"); + + if (! _cairo_operator_bounded_by_mask (op)) + use_mask |= TRUE; + + /* If any of the glyphs are component alpha, we have to go through a mask, + * since only _cairo_gl_surface_composite() currently supports component + * alpha. + */ + for (i = 0; i < num_glyphs; i++) { + cairo_scaled_glyph_t *scaled_glyph; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (!_cairo_status_is_error (status) && + scaled_glyph->surface->format == CAIRO_FORMAT_ARGB32) + { + use_mask = TRUE; + break; + } + } + + /* For CLEAR, cairo's rendering equation (quoting Owen's description in: + * http://lists.cairographics.org/archives/cairo/2005-August/004992.html) + * is: + * mask IN clip ? src OP dest : dest + * or more simply: + * mask IN CLIP ? 0 : dest + * + * where the ternary operator A ? B : C is (A * B) + ((1 - A) * C). + * + * The model we use in _cairo_gl_set_operator() is Render's: + * src IN mask IN clip OP dest + * which would boil down to: + * 0 (bounded by the extents of the drawing). + * + * However, we can do a Render operation using an opaque source + * and DEST_OUT to produce: + * 1 IN mask IN clip DEST_OUT dest + * which is + * mask IN clip ? 0 : dest + */ + if (op == CAIRO_OPERATOR_CLEAR) { + _cairo_pattern_init_solid (&solid_pattern, CAIRO_COLOR_WHITE, + CAIRO_CONTENT_COLOR); + source = &solid_pattern.base; + op = CAIRO_OPERATOR_DEST_OUT; + } + + /* For SOURCE, cairo's rendering equation is: + * (mask IN clip) ? src OP dest : dest + * or more simply: + * (mask IN clip) ? src : dest. + * + * If we just used the Render equation, we would get: + * (src IN mask IN clip) OP dest + * or: + * (src IN mask IN clip) bounded by extents of the drawing. + * + * The trick is that for GL blending, we only get our 4 source values + * into the blender, and since we need all 4 components of source, we + * can't also get the mask IN clip into the blender. But if we did + * two passes we could make it work: + * dest = (mask IN clip) DEST_OUT dest + * dest = src IN mask IN clip ADD dest + * + * But for now, composite via an intermediate mask. + */ + if (op == CAIRO_OPERATOR_SOURCE) + use_mask |= TRUE; + + /* XXX we don't need ownership of the font as we use a global + * glyph cache -- but we do need scaled_glyph eviction notification. :-( + */ + if (! _cairo_gl_surface_owns_font (dst, scaled_font)) + return UNSUPPORTED ("do not control font"); + + /* If the glyphs overlap, we need to build an intermediate mask rather + * then perform the compositing directly. + */ + status = _cairo_scaled_font_glyph_device_extents (scaled_font, + glyphs, num_glyphs, + &extents, + &overlap); + if (unlikely (status)) + return status; + + use_mask |= overlap; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + /* the empty clip should never be propagated this far */ + assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); + if (unlikely (_cairo_status_is_error (status))) + return status; + + use_mask |= status == CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_rectangle_intersect (&extents, + _cairo_clip_get_extents (clip))) + goto EMPTY; + } + + surface_extents.x = surface_extents.y = 0; + surface_extents.width = dst->width; + surface_extents.height = dst->height; + if (! _cairo_rectangle_intersect (&extents, &surface_extents)) + goto EMPTY; + + if (use_mask) { + return _cairo_gl_surface_show_glyphs_via_mask (dst, op, + source, + glyphs, num_glyphs, + &extents, + scaled_font, + clip, + remaining_glyphs); + } + + return _render_glyphs (dst, extents.x, extents.y, + op, source, + glyphs, num_glyphs, &extents, + scaled_font, &has_component_alpha, + clip_region, remaining_glyphs); + +EMPTY: + *remaining_glyphs = 0; + if (! _cairo_operator_bounded_by_mask (op)) + return _cairo_surface_paint (&dst->base, op, source, clip); + else + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_gl_glyph_cache_fini (cairo_gl_glyph_cache_t *cache) +{ + if (cache->tex == 0) + return; + + glDeleteTextures (1, &cache->tex); + + _cairo_rtree_fini (&cache->rtree); +} + +void +_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache) +{ + cache->tex = 0; + + _cairo_rtree_init (&cache->rtree, + GLYPH_CACHE_WIDTH, + GLYPH_CACHE_HEIGHT, + GLYPH_CACHE_MIN_SIZE, + sizeof (cairo_gl_glyph_private_t), + NULL); +} diff --git a/gfx/cairo/cairo/src/cairo-gl-private.h b/gfx/cairo/cairo/src/cairo-gl-private.h new file mode 100644 index 000000000000..490bc5c49039 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-private.h @@ -0,0 +1,222 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Eric Anholt + * Copyright © 2009 Chris Wilson + * Copyright © 2005 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Carl Worth + * Chris Wilson + */ + +#ifndef CAIRO_GL_PRIVATE_H +#define CAIRO_GL_PRIVATE_H + +#include "cairoint.h" +#include "cairo-rtree-private.h" + +#include + +#include "cairo-gl.h" + +#include +#define GL_GLEXT_PROTOTYPES +#include + +#define DEBUG_GL 0 + +#if DEBUG_GL && __GNUC__ +#define UNSUPPORTED(reason) ({ \ + fprintf (stderr, \ + "cairo-gl: hit unsupported operation in %s(), line %d: %s\n", \ + __FUNCTION__, __LINE__, reason); \ + CAIRO_INT_STATUS_UNSUPPORTED; \ +}) +#else +#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED +#endif + +typedef struct _cairo_gl_surface { + cairo_surface_t base; + + cairo_gl_context_t *ctx; + int width, height; + + GLuint tex; /* GL texture object containing our data. */ + GLuint fb; /* GL framebuffer object wrapping our data. */ +} cairo_gl_surface_t; + +typedef struct cairo_gl_glyph_cache { + cairo_rtree_t rtree; + GLuint tex; + unsigned int width, height; +} cairo_gl_glyph_cache_t; + +struct _cairo_gl_context { + cairo_reference_count_t ref_count; + cairo_status_t status; + + cairo_mutex_t mutex; /* needed? */ + GLuint dummy_tex; + GLint fill_rectangles_shader; + GLint fill_rectangles_color_uniform; + GLint max_framebuffer_size; + GLint max_texture_size; + + cairo_gl_surface_t *current_target; + cairo_gl_glyph_cache_t glyph_cache[2]; + + void (*make_current)(void *ctx, cairo_gl_surface_t *surface); + void (*swap_buffers)(void *ctx, cairo_gl_surface_t *surface); + void (*destroy) (void *ctx); +}; + +enum cairo_gl_composite_operand_type { + OPERAND_CONSTANT, + OPERAND_TEXTURE, +}; + +/* This union structure describes a potential source or mask operand to the + * compositing equation. + */ +typedef struct cairo_gl_composite_operand { + enum cairo_gl_composite_operand_type type; + union { + struct { + GLuint tex; + cairo_gl_surface_t *surface; + cairo_surface_attributes_t attributes; + } texture; + struct { + GLfloat color[4]; + } constant; + } operand; + + const cairo_pattern_t *pattern; +} cairo_gl_composite_operand_t; + +typedef struct _cairo_gl_composite_setup { + cairo_gl_composite_operand_t src; + cairo_gl_composite_operand_t mask; +} cairo_gl_composite_setup_t; + +cairo_private extern const cairo_surface_backend_t _cairo_gl_surface_backend; + +cairo_private cairo_gl_context_t * +_cairo_gl_context_create_in_error (cairo_status_t status); + +cairo_private cairo_status_t +_cairo_gl_context_init (cairo_gl_context_t *ctx); + +cairo_private void +_cairo_gl_surface_init (cairo_gl_context_t *ctx, + cairo_gl_surface_t *surface, + cairo_content_t content, + int width, int height); + +cairo_private cairo_status_t +_cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, + cairo_image_surface_t *src, + int src_x, int src_y, + int width, int height, + int dst_x, int dst_y); + +cairo_private cairo_int_status_t +_cairo_gl_operand_init (cairo_gl_composite_operand_t *operand, + const cairo_pattern_t *pattern, + cairo_gl_surface_t *dst, + int src_x, int src_y, + int dst_x, int dst_y, + int width, int height); + +cairo_private cairo_gl_context_t * +_cairo_gl_context_acquire (cairo_gl_context_t *ctx); + +cairo_private void +_cairo_gl_context_release (cairo_gl_context_t *ctx); + +cairo_private void +_cairo_gl_set_destination (cairo_gl_surface_t *surface); + +cairo_private cairo_bool_t +_cairo_gl_operator_is_supported (cairo_operator_t op); + +cairo_private void +_cairo_gl_set_operator (cairo_gl_surface_t *dst, cairo_operator_t op, + cairo_bool_t component_alpha); + +cairo_private void +_cairo_gl_set_src_operand (cairo_gl_context_t *ctx, + cairo_gl_composite_setup_t *setup); + +cairo_private void +_cairo_gl_operand_destroy (cairo_gl_composite_operand_t *operand); + +cairo_private cairo_bool_t +_cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format, + GLenum *internal_format, GLenum *format, + GLenum *type, cairo_bool_t *has_alpha); + +cairo_private void +_cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font); + +cairo_private void +_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache); + +cairo_private cairo_int_status_t +_cairo_gl_surface_show_glyphs (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs); + +cairo_private void +_cairo_gl_glyph_cache_fini (cairo_gl_glyph_cache_t *cache); + +cairo_private cairo_status_t +_cairo_gl_load_glsl (GLint *shader, + const char *vs_source, const char *fs_source); + +static inline int +_cairo_gl_y_flip (cairo_gl_surface_t *surface, int y) +{ + if (surface->fb) + return y; + else + return (surface->height - 1) - y; +} + +slim_hidden_proto (cairo_gl_surface_create); + +#endif /* CAIRO_GL_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-gl-shaders.c b/gfx/cairo/cairo/src/cairo-gl-shaders.c new file mode 100644 index 000000000000..f06991eefe59 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-shaders.c @@ -0,0 +1,117 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2010 Eric Anholt + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Eric Anholt. + */ + +#include "cairoint.h" +#include "cairo-gl-private.h" + +static GLint +_cairo_gl_compile_glsl(GLenum type, GLint *shader_out, const char *source) +{ + GLint ok; + GLint shader; + + shader = glCreateShaderObjectARB (type); + glShaderSourceARB (shader, 1, (const GLchar **)&source, NULL); + glCompileShaderARB (shader); + glGetObjectParameterivARB (shader, GL_OBJECT_COMPILE_STATUS_ARB, &ok); + if (!ok) { + GLchar *info; + GLint size; + + glGetObjectParameterivARB (shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, + &size); + info = malloc (size); + + if (info) + glGetInfoLogARB (shader, size, NULL, info); + fprintf (stderr, "Failed to compile %s: %s\n", + type == GL_FRAGMENT_SHADER ? "FS" : "VS", + info); + fprintf (stderr, "Shader source:\n%s", source); + fprintf (stderr, "GLSL compile failure\n"); + + glDeleteObjectARB (shader); + + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + *shader_out = shader; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gl_load_glsl (GLint *shader_out, + const char *vs_source, const char *fs_source) +{ + GLint ok; + GLint shader, vs, fs; + cairo_status_t status; + + shader = glCreateProgramObjectARB (); + + status = _cairo_gl_compile_glsl (GL_VERTEX_SHADER_ARB, &vs, vs_source); + if (_cairo_status_is_error (status)) + goto fail; + status = _cairo_gl_compile_glsl (GL_FRAGMENT_SHADER_ARB, &fs, fs_source); + if (_cairo_status_is_error (status)) + goto fail; + + glAttachObjectARB (shader, vs); + glAttachObjectARB (shader, fs); + glLinkProgram (shader); + glGetObjectParameterivARB (shader, GL_OBJECT_LINK_STATUS_ARB, &ok); + if (!ok) { + GLchar *info; + GLint size; + + glGetObjectParameterivARB (shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, + &size); + info = malloc (size); + + if (info) + glGetInfoLogARB (shader, size, NULL, info); + fprintf (stderr, "Failed to link: %s\n", info); + free (info); + status = CAIRO_INT_STATUS_UNSUPPORTED; + + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + *shader_out = shader; + + return CAIRO_STATUS_SUCCESS; + +fail: + glDeleteObjectARB (shader); + return status; +} diff --git a/gfx/cairo/cairo/src/cairo-gl-surface.c b/gfx/cairo/cairo/src/cairo-gl-surface.c new file mode 100644 index 000000000000..019249e44521 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-surface.c @@ -0,0 +1,2435 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Eric Anholt + * Copyright © 2009 Chris Wilson + * Copyright © 2005 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Carl Worth + */ + +#include "cairoint.h" + +#include "cairo-gl-private.h" + +slim_hidden_proto (cairo_gl_context_reference); +slim_hidden_proto (cairo_gl_context_destroy); + +static cairo_int_status_t +_cairo_gl_surface_fill_rectangles (void *abstract_surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects); + +#define BIAS .375 + +static inline float +int_as_float (uint32_t val) +{ + union fi { + float f; + uint32_t u; + } fi; + + fi.u = val; + return fi.f; +} + +static const cairo_gl_context_t _nil_context = { + CAIRO_REFERENCE_COUNT_INVALID, + CAIRO_STATUS_NO_MEMORY +}; + +static const cairo_gl_context_t _nil_context__invalid_format = { + CAIRO_REFERENCE_COUNT_INVALID, + CAIRO_STATUS_INVALID_FORMAT +}; + +static cairo_bool_t _cairo_surface_is_gl (cairo_surface_t *surface) +{ + return surface->backend == &_cairo_gl_surface_backend; +} + +cairo_gl_context_t * +_cairo_gl_context_create_in_error (cairo_status_t status) +{ + if (status == CAIRO_STATUS_NO_MEMORY) + return (cairo_gl_context_t *) &_nil_context; + + if (status == CAIRO_STATUS_INVALID_FORMAT) + return (cairo_gl_context_t *) &_nil_context__invalid_format; + + ASSERT_NOT_REACHED; + return NULL; +} + +cairo_status_t +_cairo_gl_context_init (cairo_gl_context_t *ctx) +{ + int n; + + ctx->status = CAIRO_STATUS_SUCCESS; + CAIRO_REFERENCE_COUNT_INIT (&ctx->ref_count, 1); + CAIRO_MUTEX_INIT (ctx->mutex); + + memset (ctx->glyph_cache, 0, sizeof (ctx->glyph_cache)); + + if (glewInit () != GLEW_OK) + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); /* XXX */ + + if (! GLEW_EXT_framebuffer_object || + ! GLEW_ARB_texture_env_combine || + ! GLEW_ARB_texture_non_power_of_two) + { + fprintf (stderr, + "Required GL extensions not available:\n"); + if (! GLEW_EXT_framebuffer_object) + fprintf (stderr, " GL_EXT_framebuffer_object\n"); + if (! GLEW_ARB_texture_env_combine) + fprintf (stderr, " GL_ARB_texture_env_combine\n"); + if (! GLEW_ARB_texture_non_power_of_two) + fprintf (stderr, " GL_ARB_texture_non_power_of_two\n"); + + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); /* XXX */ + } + + /* Set up the dummy texture for tex_env_combine with constant color. */ + glGenTextures (1, &ctx->dummy_tex); + glBindTexture (GL_TEXTURE_2D, ctx->dummy_tex); + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + ctx->max_framebuffer_size = 0; + glGetIntegerv (GL_MAX_RENDERBUFFER_SIZE, &ctx->max_framebuffer_size); + ctx->max_texture_size = 0; + glGetIntegerv (GL_MAX_TEXTURE_SIZE, &ctx->max_texture_size); + + for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++) + _cairo_gl_glyph_cache_init (&ctx->glyph_cache[n]); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_gl_context_t * +cairo_gl_context_reference (cairo_gl_context_t *context) +{ + if (context == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&context->ref_count)) + { + return context; + } + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count)); + _cairo_reference_count_inc (&context->ref_count); + + return context; +} +slim_hidden_def (cairo_gl_context_reference); + +void +cairo_gl_context_destroy (cairo_gl_context_t *context) +{ + int n; + + if (context == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&context->ref_count)) + { + return; + } + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count)); + if (! _cairo_reference_count_dec_and_test (&context->ref_count)) + return; + + glDeleteTextures (1, &context->dummy_tex); + + for (n = 0; n < ARRAY_LENGTH (context->glyph_cache); n++) + _cairo_gl_glyph_cache_fini (&context->glyph_cache[n]); + + context->destroy (context); + + free (context); +} +slim_hidden_def (cairo_gl_context_destroy); + +cairo_gl_context_t * +_cairo_gl_context_acquire (cairo_gl_context_t *ctx) +{ + CAIRO_MUTEX_LOCK (ctx->mutex); + return ctx; +} + +cairo_bool_t +_cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format, + GLenum *internal_format, GLenum *format, + GLenum *type, cairo_bool_t *has_alpha) +{ + *has_alpha = TRUE; + + switch (pixman_format) { + case PIXMAN_a8r8g8b8: + *internal_format = GL_RGBA; + *format = GL_BGRA; + *type = GL_UNSIGNED_INT_8_8_8_8_REV; + return TRUE; + case PIXMAN_x8r8g8b8: + *internal_format = GL_RGB; + *format = GL_BGRA; + *type = GL_UNSIGNED_INT_8_8_8_8_REV; + *has_alpha = FALSE; + return TRUE; + case PIXMAN_a8b8g8r8: + *internal_format = GL_RGBA; + *format = GL_RGBA; + *type = GL_UNSIGNED_INT_8_8_8_8_REV; + return TRUE; + case PIXMAN_x8b8g8r8: + *internal_format = GL_RGB; + *format = GL_RGBA; + *type = GL_UNSIGNED_INT_8_8_8_8_REV; + *has_alpha = FALSE; + return TRUE; + case PIXMAN_b8g8r8a8: + *internal_format = GL_BGRA; + *format = GL_BGRA; + *type = GL_UNSIGNED_INT_8_8_8_8; + return TRUE; + case PIXMAN_b8g8r8x8: + *internal_format = GL_RGB; + *format = GL_BGRA; + *type = GL_UNSIGNED_INT_8_8_8_8; + *has_alpha = FALSE; + return TRUE; + case PIXMAN_r8g8b8: + *internal_format = GL_RGB; + *format = GL_RGB; + *type = GL_UNSIGNED_BYTE; + return TRUE; + case PIXMAN_b8g8r8: + *internal_format = GL_RGB; + *format = GL_BGR; + *type = GL_UNSIGNED_BYTE; + return TRUE; + case PIXMAN_r5g6b5: + *internal_format = GL_RGB; + *format = GL_RGB; + *type = GL_UNSIGNED_SHORT_5_6_5; + return TRUE; + case PIXMAN_b5g6r5: + *internal_format = GL_RGB; + *format = GL_RGB; + *type = GL_UNSIGNED_SHORT_5_6_5_REV; + return TRUE; + case PIXMAN_a1r5g5b5: + *internal_format = GL_RGBA; + *format = GL_BGRA; + *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + return TRUE; + case PIXMAN_x1r5g5b5: + *internal_format = GL_RGB; + *format = GL_BGRA; + *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + *has_alpha = FALSE; + return TRUE; + case PIXMAN_a1b5g5r5: + *internal_format = GL_RGBA; + *format = GL_RGBA; + *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + return TRUE; + case PIXMAN_x1b5g5r5: + *internal_format = GL_RGB; + *format = GL_RGBA; + *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + *has_alpha = FALSE; + return TRUE; + case PIXMAN_a8: + *internal_format = GL_ALPHA; + *format = GL_ALPHA; + *type = GL_UNSIGNED_BYTE; + return TRUE; + + case PIXMAN_a2b10g10r10: + case PIXMAN_x2b10g10r10: + case PIXMAN_a4r4g4b4: + case PIXMAN_x4r4g4b4: + case PIXMAN_a4b4g4r4: + case PIXMAN_x4b4g4r4: + case PIXMAN_r3g3b2: + case PIXMAN_b2g3r3: + case PIXMAN_a2r2g2b2: + case PIXMAN_a2b2g2r2: + case PIXMAN_c8: + case PIXMAN_x4a4: + /* case PIXMAN_x4c4: */ + case PIXMAN_x4g4: + case PIXMAN_a4: + case PIXMAN_r1g2b1: + case PIXMAN_b1g2r1: + case PIXMAN_a1r1g1b1: + case PIXMAN_a1b1g1r1: + case PIXMAN_c4: + case PIXMAN_g4: + case PIXMAN_a1: + case PIXMAN_g1: + case PIXMAN_yuy2: + case PIXMAN_yv12: +#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,15,16) + case PIXMAN_x2r10g10b10: + case PIXMAN_a2r10g10b10: +#endif + default: + return FALSE; + } +} + +void +_cairo_gl_context_release (cairo_gl_context_t *ctx) +{ + CAIRO_MUTEX_UNLOCK (ctx->mutex); +} + +void +_cairo_gl_set_destination (cairo_gl_surface_t *surface) +{ + cairo_gl_context_t *ctx = surface->ctx; + + if (ctx->current_target != surface) { + ctx->current_target = surface; + + if (surface->fb) { + glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, surface->fb); + glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT); + glReadBuffer (GL_COLOR_ATTACHMENT0_EXT); + } else { + ctx->make_current (ctx, surface); + glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0); + glDrawBuffer (GL_BACK_LEFT); + glReadBuffer (GL_BACK_LEFT); + } + } + + glViewport (0, 0, surface->width, surface->height); + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + if (surface->fb) + glOrtho (0, surface->width, 0, surface->height, -1.0, 1.0); + else + glOrtho (0, surface->width, surface->height, 0, -1.0, 1.0); + + glMatrixMode (GL_MODELVIEW); + glLoadIdentity (); +} + +cairo_bool_t +_cairo_gl_operator_is_supported (cairo_operator_t op) +{ + return op < CAIRO_OPERATOR_SATURATE; +} + +void +_cairo_gl_set_operator (cairo_gl_surface_t *dst, cairo_operator_t op, + cairo_bool_t component_alpha) +{ + struct { + GLenum src; + GLenum dst; + } blend_factors[] = { + { GL_ZERO, GL_ZERO }, /* Clear */ + { GL_ONE, GL_ZERO }, /* Source */ + { GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, /* Over */ + { GL_DST_ALPHA, GL_ZERO }, /* In */ + { GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, /* Out */ + { GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, /* Atop */ + + { GL_ZERO, GL_ONE }, /* Dest */ + { GL_ONE_MINUS_DST_ALPHA, GL_ONE }, /* DestOver */ + { GL_ZERO, GL_SRC_ALPHA }, /* DestIn */ + { GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, /* DestOut */ + { GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, /* DestAtop */ + + { GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, /* Xor */ + { GL_ONE, GL_ONE }, /* Add */ + }; + GLenum src_factor, dst_factor; + + assert (op < ARRAY_LENGTH (blend_factors)); + + src_factor = blend_factors[op].src; + dst_factor = blend_factors[op].dst; + + /* Even when the user requests CAIRO_CONTENT_COLOR, we use GL_RGBA + * due to texture filtering of GL_CLAMP_TO_BORDER. So fix those + * bits in that case. + */ + if (dst->base.content == CAIRO_CONTENT_COLOR) { + if (src_factor == GL_ONE_MINUS_DST_ALPHA) + src_factor = GL_ZERO; + if (src_factor == GL_DST_ALPHA) + src_factor = GL_ONE; + } + + if (component_alpha) { + if (dst_factor == GL_ONE_MINUS_SRC_ALPHA) + dst_factor = GL_ONE_MINUS_SRC_COLOR; + if (dst_factor == GL_SRC_ALPHA) + dst_factor = GL_SRC_COLOR; + } + + glEnable (GL_BLEND); + glBlendFunc (src_factor, dst_factor); +} + +static void +_cairo_gl_set_texture_surface (int tex_unit, GLuint tex, + cairo_surface_attributes_t *attributes) +{ + glActiveTexture (GL_TEXTURE0 + tex_unit); + glBindTexture (GL_TEXTURE_2D, tex); + switch (attributes->extend) { + case CAIRO_EXTEND_NONE: + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + break; + case CAIRO_EXTEND_PAD: + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + break; + case CAIRO_EXTEND_REPEAT: + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + break; + case CAIRO_EXTEND_REFLECT: + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); + break; + } + switch (attributes->filter) { + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + break; + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + break; + default: + case CAIRO_FILTER_GAUSSIAN: + ASSERT_NOT_REACHED; + } + glEnable (GL_TEXTURE_2D); +} + +void +_cairo_gl_surface_init (cairo_gl_context_t *ctx, + cairo_gl_surface_t *surface, + cairo_content_t content, + int width, int height) +{ + _cairo_surface_init (&surface->base, + &_cairo_gl_surface_backend, + content); + + surface->ctx = cairo_gl_context_reference (ctx); + surface->width = width; + surface->height = height; +} + +static cairo_surface_t * +_cairo_gl_surface_create_scratch (cairo_gl_context_t *ctx, + cairo_content_t content, + int width, + int height) +{ + cairo_gl_surface_t *surface; + GLenum err, format; + cairo_status_t status; + + if (ctx->status) + return _cairo_surface_create_in_error (ctx->status); + + assert (width <= ctx->max_framebuffer_size && height <= ctx->max_framebuffer_size); + + surface = calloc (1, sizeof (cairo_gl_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_gl_surface_init (ctx, surface, content, width, height); + + switch (content) { + default: + ASSERT_NOT_REACHED; + case CAIRO_CONTENT_COLOR_ALPHA: + format = GL_RGBA; + break; + case CAIRO_CONTENT_ALPHA: + /* We want to be trying GL_ALPHA framebuffer objects here. */ + format = GL_RGBA; + break; + case CAIRO_CONTENT_COLOR: + /* GL_RGB is almost what we want here -- sampling 1 alpha when + * texturing, using 1 as destination alpha factor in blending, + * etc. However, when filtering with GL_CLAMP_TO_BORDER, the + * alpha channel of the border color will also be clamped to + * 1, when we actually want the border color we explicitly + * specified. So, we have to store RGBA, and fill the alpha + * channel with 1 when blending. + */ + format = GL_RGBA; + break; + } + + /* Create the texture used to store the surface's data. */ + glGenTextures (1, &surface->tex); + glBindTexture (GL_TEXTURE_2D, surface->tex); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D (GL_TEXTURE_2D, 0, format, width, height, 0, + format, GL_UNSIGNED_BYTE, NULL); + + /* Create a framebuffer object wrapping the texture so that we can render + * to it. + */ + glGenFramebuffersEXT (1, &surface->fb); + glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, surface->fb); + glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, + surface->tex, + 0); + ctx->current_target = NULL; + + while ((err = glGetError ())) { + fprintf (stderr, "GL error in surface create: 0x%08x\n", err); + } + + status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT); + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) + fprintf (stderr, "destination is framebuffer incomplete\n"); + + return &surface->base; +} + +static void +_cairo_gl_surface_clear (cairo_gl_surface_t *surface) +{ + cairo_gl_context_t *ctx; + + ctx = _cairo_gl_context_acquire (surface->ctx); + _cairo_gl_set_destination (surface); + if (surface->base.content == CAIRO_CONTENT_COLOR) + glClearColor (0.0, 0.0, 0.0, 1.0); + else + glClearColor (0.0, 0.0, 0.0, 0.0); + glClear (GL_COLOR_BUFFER_BIT); + _cairo_gl_context_release (ctx); + + surface->base.is_clear = TRUE; + +} + +cairo_surface_t * +cairo_gl_surface_create (cairo_gl_context_t *ctx, + cairo_content_t content, + int width, + int height) +{ + cairo_gl_surface_t *surface; + + if (!CAIRO_CONTENT_VALID (content)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); + + if (ctx == NULL) { + return cairo_image_surface_create (_cairo_format_from_content (content), + width, height); + } + + surface = (cairo_gl_surface_t *) + _cairo_gl_surface_create_scratch (ctx, content, width, height); + if (unlikely (surface->base.status)) + return &surface->base; + + /* Cairo surfaces start out initialized to transparent (black) */ + _cairo_gl_surface_clear (surface); + + return &surface->base; +} +slim_hidden_def (cairo_gl_surface_create); + +void +cairo_gl_surface_set_size (cairo_surface_t *abstract_surface, + int width, + int height) +{ + cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; + cairo_status_t status; + + if (! _cairo_surface_is_gl (abstract_surface) || surface->fb) { + status = _cairo_surface_set_error (abstract_surface, + CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return; + } + + surface->width = width; + surface->height = height; +} + +int +cairo_gl_surface_get_width (cairo_surface_t *abstract_surface) +{ + cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; + + if (! _cairo_surface_is_gl (abstract_surface)) + return 0; + + return surface->width; +} + +int +cairo_gl_surface_get_height (cairo_surface_t *abstract_surface) +{ + cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; + + if (! _cairo_surface_is_gl (abstract_surface)) + return 0; + + return surface->height; +} + + +void +cairo_gl_surface_swapbuffers (cairo_surface_t *abstract_surface) +{ + cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; + cairo_status_t status; + + if (! _cairo_surface_is_gl (abstract_surface)) { + status = _cairo_surface_set_error (abstract_surface, + CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return; + } + + if (! surface->fb) + surface->ctx->swap_buffers (surface->ctx, surface); +} + +static cairo_surface_t * +_cairo_gl_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_gl_surface_t *surface = abstract_surface; + + assert (CAIRO_CONTENT_VALID (content)); + + if (width > surface->ctx->max_framebuffer_size || + height > surface->ctx->max_framebuffer_size) + { + return NULL; + } + + if (width < 1) + width = 1; + if (height < 1) + height = 1; + + return _cairo_gl_surface_create_scratch (surface->ctx, content, width, height); +} + +cairo_status_t +_cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, + cairo_image_surface_t *src, + int src_x, int src_y, + int width, int height, + int dst_x, int dst_y) +{ + GLenum internal_format, format, type; + cairo_bool_t has_alpha; + cairo_image_surface_t *clone = NULL; + int cpp; + + if (! _cairo_gl_get_image_format_and_type (src->pixman_format, + &internal_format, + &format, + &type, + &has_alpha)) + { + cairo_bool_t is_supported; + + clone = _cairo_image_surface_coerce (src, + _cairo_format_from_content (src->base.content)); + if (unlikely (clone->base.status)) + return clone->base.status; + + is_supported = + _cairo_gl_get_image_format_and_type (clone->pixman_format, + &internal_format, + &format, + &type, + &has_alpha); + assert (is_supported); + src = clone; + } + + cpp = PIXMAN_FORMAT_BPP (src->pixman_format) / 8; + + glBindTexture (GL_TEXTURE_2D, dst->tex); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + glPixelStorei (GL_UNPACK_ROW_LENGTH, src->stride / cpp); + glTexSubImage2D (GL_TEXTURE_2D, 0, + dst_x, dst_y, width, height, + format, GL_UNSIGNED_BYTE, + src->data + src_y * src->stride + src_x * cpp); + glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); + + cairo_surface_destroy (&clone->base); + + /* If we just treated some rgb-only data as rgba, then we have to + * go back and fix up the alpha channel where we filled in this + * texture data. + */ + if (!has_alpha) { + cairo_rectangle_int_t rect; + cairo_color_t color; + + rect.x = dst_x; + rect.y = dst_y; + rect.width = width; + rect.height = height; + + color.red = 0.0; + color.green = 0.0; + color.blue = 0.0; + color.alpha = 1.0; + + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); + _cairo_gl_surface_fill_rectangles (dst, + CAIRO_OPERATOR_SOURCE, + &color, + &rect, 1); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_gl_surface_get_image (cairo_gl_surface_t *surface, + cairo_rectangle_int_t *interest, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *rect_out) +{ + cairo_image_surface_t *image; + GLenum err; + GLenum format, type; + cairo_format_t cairo_format; + unsigned int cpp; + + /* Want to use a switch statement here but the compiler gets whiny. */ + if (surface->base.content == CAIRO_CONTENT_COLOR_ALPHA) { + format = GL_BGRA; + cairo_format = CAIRO_FORMAT_ARGB32; + type = GL_UNSIGNED_INT_8_8_8_8_REV; + cpp = 4; + } else if (surface->base.content == CAIRO_CONTENT_COLOR) { + format = GL_BGRA; + cairo_format = CAIRO_FORMAT_RGB24; + type = GL_UNSIGNED_INT_8_8_8_8_REV; + cpp = 4; + } else if (surface->base.content == CAIRO_CONTENT_ALPHA) { + format = GL_ALPHA; + cairo_format = CAIRO_FORMAT_A8; + type = GL_UNSIGNED_BYTE; + cpp = 1; + } else { + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + image = (cairo_image_surface_t*) + cairo_image_surface_create (cairo_format, + interest->width, interest->height); + if (unlikely (image->base.status)) + return image->base.status; + + /* This is inefficient, as we'd rather just read the thing without making + * it the destination. But then, this is the fallback path, so let's not + * fall back instead. + */ + _cairo_gl_set_destination (surface); + + glPixelStorei (GL_PACK_ALIGNMENT, 1); + glPixelStorei (GL_PACK_ROW_LENGTH, image->stride / cpp); + glReadPixels (interest->x, interest->y, + interest->width, interest->height, + format, type, image->data); + + while ((err = glGetError ())) + fprintf (stderr, "GL error 0x%08x\n", (int) err); + + *image_out = image; + if (rect_out != NULL) + *rect_out = *interest; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_gl_surface_finish (void *abstract_surface) +{ + cairo_gl_surface_t *surface = abstract_surface; + + glDeleteFramebuffersEXT (1, &surface->fb); + glDeleteTextures (1, &surface->tex); + + if (surface->ctx->current_target == surface) + surface->ctx->current_target = NULL; + + cairo_gl_context_destroy (surface->ctx); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_gl_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_gl_surface_t *surface = abstract_surface; + cairo_rectangle_int_t extents; + + *image_extra = NULL; + + extents.x = extents.y = 0; + extents.width = surface->width; + extents.height = surface->height; + return _cairo_gl_surface_get_image (surface, &extents, image_out, NULL); +} + +static void +_cairo_gl_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_cairo_gl_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect_out, + void **image_extra) +{ + cairo_gl_surface_t *surface = abstract_surface; + + *image_extra = NULL; + return _cairo_gl_surface_get_image (surface, interest_rect, image_out, + image_rect_out); +} + +static void +_cairo_gl_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int_t *image_rect, + void *image_extra) +{ + cairo_status_t status; + + status = _cairo_gl_surface_draw_image (abstract_surface, image, + 0, 0, + image->width, image->height, + image_rect->x, image_rect->y); + /* as we created the image, its format should be directly applicable */ + assert (status == CAIRO_STATUS_SUCCESS); + + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_cairo_gl_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + int src_x, + int src_y, + int width, + int height, + int *clone_offset_x, + int *clone_offset_y, + cairo_surface_t **clone_out) +{ + cairo_gl_surface_t *surface = abstract_surface; + + if (src->backend == surface->base.backend) { + *clone_offset_x = 0; + *clone_offset_y = 0; + *clone_out = cairo_surface_reference (src); + + return CAIRO_STATUS_SUCCESS; + } else if (_cairo_surface_is_image (src)) { + cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; + cairo_gl_surface_t *clone; + cairo_status_t status; + + clone = (cairo_gl_surface_t *) + _cairo_gl_surface_create_similar (&surface->base, + src->content, + width, height); + if (clone == NULL) + return UNSUPPORTED ("create_similar failed"); + if (clone->base.status) + return clone->base.status; + + status = _cairo_gl_surface_draw_image (clone, image_src, + src_x, src_y, + width, height, + 0, 0); + if (status) { + cairo_surface_destroy (&clone->base); + return status; + } + + *clone_out = &clone->base; + *clone_offset_x = src_x; + *clone_offset_y = src_y; + + return CAIRO_STATUS_SUCCESS; + } + + return UNSUPPORTED ("unknown src surface type in clone_similar"); +} + +/** Creates a cairo-gl pattern surface for the given trapezoids */ +static cairo_status_t +_cairo_gl_get_traps_pattern (cairo_gl_surface_t *dst, + int dst_x, int dst_y, + int width, int height, + cairo_trapezoid_t *traps, + int num_traps, + cairo_antialias_t antialias, + cairo_surface_pattern_t *pattern) +{ + pixman_format_code_t pixman_format; + pixman_image_t *image; + cairo_surface_t *surface; + int i; + + pixman_format = antialias != CAIRO_ANTIALIAS_NONE ? PIXMAN_a8 : PIXMAN_a1, + image = pixman_image_create_bits (pixman_format, width, height, NULL, 0); + if (unlikely (image == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + for (i = 0; i < num_traps; i++) { + pixman_trapezoid_t trap; + + trap.top = _cairo_fixed_to_16_16 (traps[i].top); + trap.bottom = _cairo_fixed_to_16_16 (traps[i].bottom); + + trap.left.p1.x = _cairo_fixed_to_16_16 (traps[i].left.p1.x); + trap.left.p1.y = _cairo_fixed_to_16_16 (traps[i].left.p1.y); + trap.left.p2.x = _cairo_fixed_to_16_16 (traps[i].left.p2.x); + trap.left.p2.y = _cairo_fixed_to_16_16 (traps[i].left.p2.y); + + trap.right.p1.x = _cairo_fixed_to_16_16 (traps[i].right.p1.x); + trap.right.p1.y = _cairo_fixed_to_16_16 (traps[i].right.p1.y); + trap.right.p2.x = _cairo_fixed_to_16_16 (traps[i].right.p2.x); + trap.right.p2.y = _cairo_fixed_to_16_16 (traps[i].right.p2.y); + + pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y); + } + + surface = _cairo_image_surface_create_for_pixman_image (image, + pixman_format); + if (unlikely (surface->status)) { + pixman_image_unref (image); + return surface->status; + } + + _cairo_pattern_init_for_surface (pattern, surface); + cairo_surface_destroy (surface); + + return CAIRO_STATUS_SUCCESS; +} + +/** + * Like cairo_pattern_acquire_surface(), but returns a matrix that transforms + * from dest to src coords. + */ +static cairo_status_t +_cairo_gl_pattern_texture_setup (cairo_gl_composite_operand_t *operand, + const cairo_pattern_t *src, + cairo_gl_surface_t *dst, + int src_x, int src_y, + int dst_x, int dst_y, + int width, int height) +{ + cairo_status_t status; + cairo_matrix_t m; + cairo_gl_surface_t *surface; + cairo_surface_attributes_t *attributes; + + attributes = &operand->operand.texture.attributes; + + status = _cairo_pattern_acquire_surface (src, &dst->base, + src_x, src_y, + width, height, + CAIRO_PATTERN_ACQUIRE_NONE, + (cairo_surface_t **) + &surface, + attributes); + if (unlikely (status)) + return status; + + assert (surface->base.backend == &_cairo_gl_surface_backend); + + operand->operand.texture.surface = surface; + operand->operand.texture.tex = surface->tex; + + /* Translate the matrix from + * (unnormalized src -> unnormalized src) to + * (unnormalized dst -> unnormalized src) + */ + cairo_matrix_init_translate (&m, + src_x - dst_x + attributes->x_offset, + src_y - dst_y + attributes->y_offset); + cairo_matrix_multiply (&attributes->matrix, + &m, + &attributes->matrix); + + + /* Translate the matrix from + * (unnormalized src -> unnormalized src) to + * (unnormalized dst -> normalized src) + */ + cairo_matrix_init_scale (&m, + 1.0 / surface->width, + 1.0 / surface->height); + cairo_matrix_multiply (&attributes->matrix, + &attributes->matrix, + &m); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_gl_solid_operand_init (cairo_gl_composite_operand_t *operand, + const cairo_color_t *color) +{ + operand->type = OPERAND_CONSTANT; + operand->operand.constant.color[0] = color->red * color->alpha; + operand->operand.constant.color[1] = color->green * color->alpha; + operand->operand.constant.color[2] = color->blue * color->alpha; + operand->operand.constant.color[3] = color->alpha; + return CAIRO_STATUS_SUCCESS; +} + +cairo_int_status_t +_cairo_gl_operand_init (cairo_gl_composite_operand_t *operand, + const cairo_pattern_t *pattern, + cairo_gl_surface_t *dst, + int src_x, int src_y, + int dst_x, int dst_y, + int width, int height) +{ + operand->pattern = pattern; + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return _cairo_gl_solid_operand_init (operand, + &((cairo_solid_pattern_t *) pattern)->color); + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + { + cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) pattern; + + /* Fast path for gradients with less than 2 color stops. + * Required to prevent _cairo_pattern_acquire_surface() returning + * a solid color which is cached beyond the life of the context. + */ + if (src->n_stops < 2) { + if (src->n_stops) { + return _cairo_gl_solid_operand_init (operand, + &src->stops->color); + } else { + return _cairo_gl_solid_operand_init (operand, + CAIRO_COLOR_TRANSPARENT); + } + } else { + unsigned int i; + + /* Is the gradient a uniform colour? + * Happens more often than you would believe. + */ + for (i = 1; i < src->n_stops; i++) { + if (! _cairo_color_equal (&src->stops[0].color, + &src->stops[i].color)) + { + break; + } + } + if (i == src->n_stops) { + return _cairo_gl_solid_operand_init (operand, + &src->stops->color); + } + } + } + + /* fall through */ + + default: + case CAIRO_PATTERN_TYPE_SURFACE: + operand->type = OPERAND_TEXTURE; + return _cairo_gl_pattern_texture_setup (operand, + pattern, dst, + src_x, src_y, + dst_x, dst_y, + width, height); + } +} + +void +_cairo_gl_operand_destroy (cairo_gl_composite_operand_t *operand) +{ + switch (operand->type) { + case OPERAND_CONSTANT: + break; + case OPERAND_TEXTURE: + if (operand->operand.texture.surface != NULL) { + cairo_gl_surface_t *surface = operand->operand.texture.surface; + + _cairo_pattern_release_surface (operand->pattern, + &surface->base, + &operand->operand.texture.attributes); + } + break; + } +} + +static void +_cairo_gl_set_tex_combine_constant_color (cairo_gl_context_t *ctx, int tex_unit, + GLfloat *color) +{ + glActiveTexture (GL_TEXTURE0 + tex_unit); + /* Have to have a dummy texture bound in order to use the combiner unit. */ + glBindTexture (GL_TEXTURE_2D, ctx->dummy_tex); + glEnable (GL_TEXTURE_2D); + + glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + if (tex_unit == 0) { + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + } else { + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + } + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_CONSTANT); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_CONSTANT); + if (tex_unit == 0) { + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + } else { + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + + glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_PREVIOUS); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + } +} + +void +_cairo_gl_set_src_operand (cairo_gl_context_t *ctx, + cairo_gl_composite_setup_t *setup) +{ + cairo_surface_attributes_t *src_attributes; + GLfloat constant_color[4] = {0.0, 0.0, 0.0, 0.0}; + + src_attributes = &setup->src.operand.texture.attributes; + + switch (setup->src.type) { + case OPERAND_CONSTANT: + _cairo_gl_set_tex_combine_constant_color (ctx, 0, + setup->src.operand.constant.color); + break; + case OPERAND_TEXTURE: + _cairo_gl_set_texture_surface (0, setup->src.operand.texture.tex, + src_attributes); + /* Set up the constant color we use to set color to 0 if needed. */ + glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant_color); + /* Set up the combiner to just set color to the sampled texture. */ + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + + /* Force the src color to 0 if the surface should be alpha-only. + * We may have a teximage with color bits if the implementation doesn't + * support GL_ALPHA FBOs. + */ + if (setup->src.operand.texture.surface->base.content != + CAIRO_CONTENT_ALPHA) + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE0); + else + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_CONSTANT); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE0); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + break; + } +} + +/* This is like _cairo_gl_set_src_operand, but instead swizzles the source + * for creating the "source alpha" value (src.aaaa * mask.argb) required by + * component alpha rendering. + */ +static void +_cairo_gl_set_src_alpha_operand (cairo_gl_context_t *ctx, + cairo_gl_composite_setup_t *setup) +{ + GLfloat constant_color[4] = {0.0, 0.0, 0.0, 0.0}; + cairo_surface_attributes_t *src_attributes; + + src_attributes = &setup->src.operand.texture.attributes; + + switch (setup->src.type) { + case OPERAND_CONSTANT: + constant_color[0] = setup->src.operand.constant.color[3]; + constant_color[1] = setup->src.operand.constant.color[3]; + constant_color[2] = setup->src.operand.constant.color[3]; + constant_color[3] = setup->src.operand.constant.color[3]; + _cairo_gl_set_tex_combine_constant_color (ctx, 0, constant_color); + break; + case OPERAND_TEXTURE: + constant_color[0] = 0.0; + constant_color[1] = 0.0; + constant_color[2] = 0.0; + constant_color[3] = 1.0; + _cairo_gl_set_texture_surface (0, setup->src.operand.texture.tex, + src_attributes); + /* Set up the combiner to just set color to the sampled texture. */ + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE0); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE0); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + break; + } +} + +/* This is like _cairo_gl_set_src_alpha_operand, for component alpha setup + * of the mask part of IN to produce a "source alpha" value. + */ +static void +_cairo_gl_set_component_alpha_mask_operand (cairo_gl_context_t *ctx, + cairo_gl_composite_setup_t *setup) +{ + cairo_surface_attributes_t *mask_attributes; + GLfloat constant_color[4] = {0.0, 0.0, 0.0, 0.0}; + + mask_attributes = &setup->mask.operand.texture.attributes; + + glActiveTexture (GL_TEXTURE1); + glEnable (GL_TEXTURE_2D); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + + glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_PREVIOUS); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + + switch (setup->mask.type) { + case OPERAND_CONSTANT: + /* Have to have a dummy texture bound in order to use the combiner unit. */ + glBindTexture (GL_TEXTURE_2D, ctx->dummy_tex); + + glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, + setup->mask.operand.constant.color); + + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_CONSTANT); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_CONSTANT); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + + break; + case OPERAND_TEXTURE: + _cairo_gl_set_texture_surface (1, setup->mask.operand.texture.tex, + mask_attributes); + /* Set up the constant color we use to set color to 0 if needed. */ + glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant_color); + + /* Force the mask color to 0 if the surface should be alpha-only. + * We may have a teximage with color bits if the implementation doesn't + * support GL_ALPHA FBOs. + */ + if (setup->mask.operand.texture.surface->base.content != + CAIRO_CONTENT_ALPHA) + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE1); + else + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_CONSTANT); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE1); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + break; + } +} + +/** + * implements component-alpha CAIRO_OPERATOR_SOURCE using two passes of + * the simpler operations CAIRO_OPERATOR_DEST_OUT and CAIRO_OPERATOR_ADD. + * + * From http://anholt.livejournal.com/32058.html: + * + * The trouble is that component-alpha rendering requires two different sources + * for blending: one for the source value to the blender, which is the + * per-channel multiplication of source and mask, and one for the source alpha + * for multiplying with the destination channels, which is the multiplication + * of the source channels by the mask alpha. So the equation for Over is: + * + * dst.A = src.A * mask.A + (1 - (src.A * mask.A)) * dst.A + * dst.R = src.R * mask.R + (1 - (src.A * mask.R)) * dst.R + * dst.G = src.G * mask.G + (1 - (src.A * mask.G)) * dst.G + * dst.B = src.B * mask.B + (1 - (src.A * mask.B)) * dst.B + * + * But we can do some simpler operations, right? How about PictOpOutReverse, + * which has a source factor of 0 and dest factor of (1 - source alpha). We + * can get the source alpha value (srca.X = src.A * mask.X) out of the texture + * blenders pretty easily. So we can do a component-alpha OutReverse, which + * gets us: + * + * dst.A = 0 + (1 - (src.A * mask.A)) * dst.A + * dst.R = 0 + (1 - (src.A * mask.R)) * dst.R + * dst.G = 0 + (1 - (src.A * mask.G)) * dst.G + * dst.B = 0 + (1 - (src.A * mask.B)) * dst.B + * + * OK. And if an op doesn't use the source alpha value for the destination + * factor, then we can do the channel multiplication in the texture blenders + * to get the source value, and ignore the source alpha that we wouldn't use. + * We've supported this in the Radeon driver for a long time. An example would + * be PictOpAdd, which does: + * + * dst.A = src.A * mask.A + dst.A + * dst.R = src.R * mask.R + dst.R + * dst.G = src.G * mask.G + dst.G + * dst.B = src.B * mask.B + dst.B + * + * Hey, this looks good! If we do a PictOpOutReverse and then a PictOpAdd right + * after it, we get: + * + * dst.A = src.A * mask.A + ((1 - (src.A * mask.A)) * dst.A) + * dst.R = src.R * mask.R + ((1 - (src.A * mask.R)) * dst.R) + * dst.G = src.G * mask.G + ((1 - (src.A * mask.G)) * dst.G) + * dst.B = src.B * mask.B + ((1 - (src.A * mask.B)) * dst.B) + * + * This two-pass trickery could be avoided using a new GL extension that + * lets two values come out of the shader and into the blend unit. + */ +static cairo_int_status_t +_cairo_gl_surface_composite_component_alpha (cairo_operator_t op, + const cairo_pattern_t *src, + const cairo_pattern_t *mask, + void *abstract_dst, + 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_region_t *clip_region) +{ + cairo_gl_surface_t *dst = abstract_dst; + cairo_surface_attributes_t *src_attributes, *mask_attributes = NULL; + cairo_gl_context_t *ctx; + struct gl_point { + GLfloat x, y; + } vertices_stack[8], texcoord_src_stack[8], texcoord_mask_stack[8]; + struct gl_point *vertices = vertices_stack; + struct gl_point *texcoord_src = texcoord_src_stack; + struct gl_point *texcoord_mask = texcoord_mask_stack; + cairo_status_t status; + int num_vertices, i; + GLenum err; + cairo_gl_composite_setup_t setup; + + if (op != CAIRO_OPERATOR_OVER) + return UNSUPPORTED ("unsupported component alpha operator"); + + memset (&setup, 0, sizeof (setup)); + + status = _cairo_gl_operand_init (&setup.src, src, dst, + src_x, src_y, + dst_x, dst_y, + width, height); + if (unlikely (status)) + return status; + src_attributes = &setup.src.operand.texture.attributes; + + status = _cairo_gl_operand_init (&setup.mask, mask, dst, + mask_x, mask_y, + dst_x, dst_y, + width, height); + if (unlikely (status)) { + _cairo_gl_operand_destroy (&setup.src); + return status; + } + mask_attributes = &setup.mask.operand.texture.attributes; + + ctx = _cairo_gl_context_acquire (dst->ctx); + _cairo_gl_set_destination (dst); + + if (clip_region != NULL) { + int num_rectangles; + + num_rectangles = cairo_region_num_rectangles (clip_region); + if (num_rectangles * 4 > ARRAY_LENGTH (vertices_stack)) { + vertices = _cairo_malloc_ab (num_rectangles, + 4*3*sizeof (vertices[0])); + if (unlikely (vertices == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CONTEXT_RELEASE; + } + + texcoord_src = vertices + num_rectangles * 4; + texcoord_mask = texcoord_src + num_rectangles * 4; + } + + for (i = 0; i < num_rectangles; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (clip_region, i, &rect); + vertices[4*i + 0].x = rect.x; + vertices[4*i + 0].y = rect.y; + vertices[4*i + 1].x = rect.x + rect.width; + vertices[4*i + 1].y = rect.y; + vertices[4*i + 2].x = rect.x + rect.width; + vertices[4*i + 2].y = rect.y + rect.height; + vertices[4*i + 3].x = rect.x; + vertices[4*i + 3].y = rect.y + rect.height; + } + + num_vertices = 4 * num_rectangles; + } else { + vertices[0].x = dst_x; + vertices[0].y = dst_y; + vertices[1].x = dst_x + width; + vertices[1].y = dst_y; + vertices[2].x = dst_x + width; + vertices[2].y = dst_y + height; + vertices[3].x = dst_x; + vertices[3].y = dst_y + height; + + num_vertices = 4; + } + + glVertexPointer (2, GL_FLOAT, sizeof (GLfloat) * 2, vertices); + glEnableClientState (GL_VERTEX_ARRAY); + + if (setup.src.type == OPERAND_TEXTURE) { + for (i = 0; i < num_vertices; i++) { + double s, t; + + s = vertices[i].x; + t = vertices[i].y; + cairo_matrix_transform_point (&src_attributes->matrix, &s, &t); + texcoord_src[i].x = s; + texcoord_src[i].y = t; + } + + glClientActiveTexture (GL_TEXTURE0); + glTexCoordPointer (2, GL_FLOAT, sizeof (GLfloat)*2, texcoord_src); + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + } + + if (setup.mask.type == OPERAND_TEXTURE) { + for (i = 0; i < num_vertices; i++) { + double s, t; + + s = vertices[i].x; + t = vertices[i].y; + cairo_matrix_transform_point (&mask_attributes->matrix, &s, &t); + texcoord_mask[i].x = s; + texcoord_mask[i].y = t; + } + + glClientActiveTexture (GL_TEXTURE1); + glTexCoordPointer (2, GL_FLOAT, sizeof (GLfloat)*2, texcoord_mask); + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + } + + _cairo_gl_set_operator (dst, CAIRO_OPERATOR_DEST_OUT, TRUE); + _cairo_gl_set_src_alpha_operand (ctx, &setup); + _cairo_gl_set_component_alpha_mask_operand (ctx, &setup); + glDrawArrays (GL_QUADS, 0, num_vertices); + + _cairo_gl_set_operator (dst, CAIRO_OPERATOR_ADD, TRUE); + _cairo_gl_set_src_operand (ctx, &setup); + glDrawArrays (GL_QUADS, 0, num_vertices); + + glDisable (GL_BLEND); + + glDisableClientState (GL_VERTEX_ARRAY); + + glClientActiveTexture (GL_TEXTURE0); + glDisableClientState (GL_TEXTURE_COORD_ARRAY); + glActiveTexture (GL_TEXTURE0); + glDisable (GL_TEXTURE_2D); + + glClientActiveTexture (GL_TEXTURE1); + glDisableClientState (GL_TEXTURE_COORD_ARRAY); + glActiveTexture (GL_TEXTURE1); + glDisable (GL_TEXTURE_2D); + + while ((err = glGetError ())) + fprintf (stderr, "GL error 0x%08x\n", (int) err); + + CONTEXT_RELEASE: + _cairo_gl_context_release (ctx); + + _cairo_gl_operand_destroy (&setup.src); + if (mask != NULL) + _cairo_gl_operand_destroy (&setup.mask); + + if (vertices != vertices_stack) + free (vertices); + + return status; +} + +static cairo_int_status_t +_cairo_gl_surface_composite (cairo_operator_t op, + const cairo_pattern_t *src, + const cairo_pattern_t *mask, + void *abstract_dst, + 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_region_t *clip_region) +{ + cairo_gl_surface_t *dst = abstract_dst; + cairo_surface_attributes_t *src_attributes, *mask_attributes = NULL; + cairo_gl_context_t *ctx; + struct gl_point { + GLfloat x, y; + } vertices_stack[8], texcoord_src_stack[8], texcoord_mask_stack[8]; + struct gl_point *vertices = vertices_stack; + struct gl_point *texcoord_src = texcoord_src_stack; + struct gl_point *texcoord_mask = texcoord_mask_stack; + cairo_status_t status; + int num_vertices, i; + GLenum err; + cairo_gl_composite_setup_t setup; + + if (! _cairo_gl_operator_is_supported (op)) + return UNSUPPORTED ("unsupported operator"); + + if (mask && mask->has_component_alpha) { + /* Try two-pass component alpha support, or bail. */ + return _cairo_gl_surface_composite_component_alpha(op, + src, + mask, + abstract_dst, + src_x, + src_y, + mask_x, + mask_y, + dst_x, + dst_y, + width, + height, + clip_region); + } + + memset (&setup, 0, sizeof (setup)); + + status = _cairo_gl_operand_init (&setup.src, src, dst, + src_x, src_y, + dst_x, dst_y, + width, height); + if (unlikely (status)) + return status; + src_attributes = &setup.src.operand.texture.attributes; + + if (mask != NULL) { + status = _cairo_gl_operand_init (&setup.mask, mask, dst, + mask_x, mask_y, + dst_x, dst_y, + width, height); + if (unlikely (status)) { + _cairo_gl_operand_destroy (&setup.src); + return status; + } + mask_attributes = &setup.mask.operand.texture.attributes; + } + + ctx = _cairo_gl_context_acquire (dst->ctx); + _cairo_gl_set_destination (dst); + _cairo_gl_set_operator (dst, op, FALSE); + + _cairo_gl_set_src_operand (ctx, &setup); + + if (mask != NULL) { + switch (setup.mask.type) { + case OPERAND_CONSTANT: + _cairo_gl_set_tex_combine_constant_color (ctx, 1, + setup.mask.operand.constant.color); + break; + + case OPERAND_TEXTURE: + _cairo_gl_set_texture_surface (1, setup.mask.operand.texture.tex, + mask_attributes); + + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + + /* IN: dst.argb = src.argb * mask.aaaa */ + glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE1); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE1); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + break; + } + } + + if (clip_region != NULL) { + int num_rectangles; + + num_rectangles = cairo_region_num_rectangles (clip_region); + if (num_rectangles * 4 > ARRAY_LENGTH (vertices_stack)) { + vertices = _cairo_malloc_ab (num_rectangles, + 4*3*sizeof (vertices[0])); + if (unlikely (vertices == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CONTEXT_RELEASE; + } + + texcoord_src = vertices + num_rectangles * 4; + texcoord_mask = texcoord_src + num_rectangles * 4; + } + + for (i = 0; i < num_rectangles; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (clip_region, i, &rect); + vertices[4*i + 0].x = rect.x; + vertices[4*i + 0].y = rect.y; + vertices[4*i + 1].x = rect.x + rect.width; + vertices[4*i + 1].y = rect.y; + vertices[4*i + 2].x = rect.x + rect.width; + vertices[4*i + 2].y = rect.y + rect.height; + vertices[4*i + 3].x = rect.x; + vertices[4*i + 3].y = rect.y + rect.height; + } + + num_vertices = 4 * num_rectangles; + } else { + vertices[0].x = dst_x; + vertices[0].y = dst_y; + vertices[1].x = dst_x + width; + vertices[1].y = dst_y; + vertices[2].x = dst_x + width; + vertices[2].y = dst_y + height; + vertices[3].x = dst_x; + vertices[3].y = dst_y + height; + + num_vertices = 4; + } + + glVertexPointer (2, GL_FLOAT, sizeof (GLfloat) * 2, vertices); + glEnableClientState (GL_VERTEX_ARRAY); + + if (setup.src.type == OPERAND_TEXTURE) { + for (i = 0; i < num_vertices; i++) { + double s, t; + + s = vertices[i].x; + t = vertices[i].y; + cairo_matrix_transform_point (&src_attributes->matrix, &s, &t); + texcoord_src[i].x = s; + texcoord_src[i].y = t; + } + + glClientActiveTexture (GL_TEXTURE0); + glTexCoordPointer (2, GL_FLOAT, sizeof (GLfloat)*2, texcoord_src); + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + } + + if (mask != NULL) { + if (setup.mask.type == OPERAND_TEXTURE) { + for (i = 0; i < num_vertices; i++) { + double s, t; + + s = vertices[i].x; + t = vertices[i].y; + cairo_matrix_transform_point (&mask_attributes->matrix, &s, &t); + texcoord_mask[i].x = s; + texcoord_mask[i].y = t; + } + + glClientActiveTexture (GL_TEXTURE1); + glTexCoordPointer (2, GL_FLOAT, sizeof (GLfloat)*2, texcoord_mask); + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + } + } + + glDrawArrays (GL_QUADS, 0, num_vertices); + + glDisable (GL_BLEND); + + glDisableClientState (GL_VERTEX_ARRAY); + + glClientActiveTexture (GL_TEXTURE0); + glDisableClientState (GL_TEXTURE_COORD_ARRAY); + glActiveTexture (GL_TEXTURE0); + glDisable (GL_TEXTURE_2D); + + glClientActiveTexture (GL_TEXTURE1); + glDisableClientState (GL_TEXTURE_COORD_ARRAY); + glActiveTexture (GL_TEXTURE1); + glDisable (GL_TEXTURE_2D); + + while ((err = glGetError ())) + fprintf (stderr, "GL error 0x%08x\n", (int) err); + + CONTEXT_RELEASE: + _cairo_gl_context_release (ctx); + + _cairo_gl_operand_destroy (&setup.src); + if (mask != NULL) + _cairo_gl_operand_destroy (&setup.mask); + + if (vertices != vertices_stack) + free (vertices); + + return status; +} + +static cairo_int_status_t +_cairo_gl_surface_composite_trapezoids (cairo_operator_t op, + const cairo_pattern_t *pattern, + void *abstract_dst, + cairo_antialias_t antialias, + int src_x, int src_y, + int dst_x, int dst_y, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int num_traps, + cairo_region_t *clip_region) +{ + cairo_gl_surface_t *dst = abstract_dst; + cairo_surface_pattern_t traps_pattern; + cairo_int_status_t status; + + if (! _cairo_gl_operator_is_supported (op)) + return UNSUPPORTED ("unsupported operator"); + + if (_cairo_surface_check_span_renderer (op,pattern,&dst->base, antialias)) { + status = + _cairo_surface_composite_trapezoids_as_polygon (&dst->base, + op, pattern, + antialias, + src_x, src_y, + dst_x, dst_y, + width, height, + traps, num_traps, + clip_region); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + status = _cairo_gl_get_traps_pattern (dst, + dst_x, dst_y, width, height, + traps, num_traps, antialias, + &traps_pattern); + if (unlikely (status)) + return status; + + status = _cairo_gl_surface_composite (op, + pattern, &traps_pattern.base, dst, + src_x, src_y, + 0, 0, + dst_x, dst_y, + width, height, + clip_region); + + _cairo_pattern_fini (&traps_pattern.base); + + return status; +} + +static cairo_int_status_t +_cairo_gl_surface_fill_rectangles_fixed (void *abstract_surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects) +{ +#define N_STACK_RECTS 4 + cairo_gl_surface_t *surface = abstract_surface; + GLfloat vertices_stack[N_STACK_RECTS*4*2]; + GLfloat colors_stack[N_STACK_RECTS*4*4]; + cairo_gl_context_t *ctx; + int i; + GLfloat *vertices; + GLfloat *colors; + + if (! _cairo_gl_operator_is_supported (op)) + return UNSUPPORTED ("unsupported operator"); + + ctx = _cairo_gl_context_acquire (surface->ctx); + + _cairo_gl_set_destination (surface); + _cairo_gl_set_operator (surface, op, FALSE); + + if (num_rects > N_STACK_RECTS) { + vertices = _cairo_malloc_ab (num_rects, sizeof (GLfloat) * 4 * 2); + colors = _cairo_malloc_ab (num_rects, sizeof (GLfloat) * 4 * 4); + if (!vertices || !colors) { + _cairo_gl_context_release (ctx); + free (vertices); + free (colors); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } else { + vertices = vertices_stack; + colors = colors_stack; + } + + /* This should be loaded in as either a blend constant and an operator + * setup specific to this, or better, a fragment shader constant. + */ + colors[0] = color->red * color->alpha; + colors[1] = color->green * color->alpha; + colors[2] = color->blue * color->alpha; + colors[3] = color->alpha; + for (i = 1; i < num_rects * 4; i++) { + colors[i*4 + 0] = colors[0]; + colors[i*4 + 1] = colors[1]; + colors[i*4 + 2] = colors[2]; + colors[i*4 + 3] = colors[3]; + } + + for (i = 0; i < num_rects; i++) { + vertices[i * 8 + 0] = rects[i].x; + vertices[i * 8 + 1] = rects[i].y; + vertices[i * 8 + 2] = rects[i].x + rects[i].width; + vertices[i * 8 + 3] = rects[i].y; + vertices[i * 8 + 4] = rects[i].x + rects[i].width; + vertices[i * 8 + 5] = rects[i].y + rects[i].height; + vertices[i * 8 + 6] = rects[i].x; + vertices[i * 8 + 7] = rects[i].y + rects[i].height; + } + + glVertexPointer (2, GL_FLOAT, sizeof (GLfloat)*2, vertices); + glEnableClientState (GL_VERTEX_ARRAY); + glColorPointer (4, GL_FLOAT, sizeof (GLfloat)*4, colors); + glEnableClientState (GL_COLOR_ARRAY); + + glDrawArrays (GL_QUADS, 0, 4 * num_rects); + + glDisableClientState (GL_COLOR_ARRAY); + glDisableClientState (GL_VERTEX_ARRAY); + glDisable (GL_BLEND); + + _cairo_gl_context_release (ctx); + if (vertices != vertices_stack) + free (vertices); + if (colors != colors_stack) + free (colors); + + return CAIRO_STATUS_SUCCESS; +#undef N_STACK_RECTS +} + +static cairo_int_status_t +_cairo_gl_surface_fill_rectangles_glsl (void *abstract_surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects) +{ +#define N_STACK_RECTS 4 + cairo_gl_surface_t *surface = abstract_surface; + GLfloat vertices_stack[N_STACK_RECTS*4*2]; + GLfloat gl_color[4]; + cairo_gl_context_t *ctx; + int i; + GLfloat *vertices; + static const char *fill_vs_source = + "void main()\n" + "{\n" + " gl_Position = ftransform();\n" + "}\n"; + static const char *fill_fs_source = + "uniform vec4 color;\n" + "void main()\n" + "{\n" + " gl_FragColor = color;\n" + "}\n"; + cairo_status_t status; + + if (! _cairo_gl_operator_is_supported (op)) + return UNSUPPORTED ("unsupported operator"); + + ctx = _cairo_gl_context_acquire (surface->ctx); + + if (ctx->fill_rectangles_shader == 0) { + status = _cairo_gl_load_glsl (&ctx->fill_rectangles_shader, + fill_vs_source, fill_fs_source); + if (_cairo_status_is_error (status)) + return status; + + ctx->fill_rectangles_color_uniform = + glGetUniformLocationARB (ctx->fill_rectangles_shader, "color"); + } + + if (num_rects > N_STACK_RECTS) { + vertices = _cairo_malloc_ab (num_rects, sizeof (GLfloat) * 4 * 2); + if (!vertices) { + _cairo_gl_context_release (ctx); + free (vertices); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } else { + vertices = vertices_stack; + } + + glUseProgramObjectARB (ctx->fill_rectangles_shader); + + _cairo_gl_set_destination (surface); + _cairo_gl_set_operator (surface, op, FALSE); + + gl_color[0] = color->red * color->alpha; + gl_color[1] = color->green * color->alpha; + gl_color[2] = color->blue * color->alpha; + gl_color[3] = color->alpha; + glUniform4fvARB (ctx->fill_rectangles_color_uniform, 1, gl_color); + + for (i = 0; i < num_rects; i++) { + vertices[i * 8 + 0] = rects[i].x; + vertices[i * 8 + 1] = rects[i].y; + vertices[i * 8 + 2] = rects[i].x + rects[i].width; + vertices[i * 8 + 3] = rects[i].y; + vertices[i * 8 + 4] = rects[i].x + rects[i].width; + vertices[i * 8 + 5] = rects[i].y + rects[i].height; + vertices[i * 8 + 6] = rects[i].x; + vertices[i * 8 + 7] = rects[i].y + rects[i].height; + } + + glVertexPointer (2, GL_FLOAT, sizeof (GLfloat)*2, vertices); + glEnableClientState (GL_VERTEX_ARRAY); + + glDrawArrays (GL_QUADS, 0, 4 * num_rects); + + glDisableClientState (GL_VERTEX_ARRAY); + glDisable (GL_BLEND); + glUseProgramObjectARB (0); + + _cairo_gl_context_release (ctx); + if (vertices != vertices_stack) + free (vertices); + + return CAIRO_STATUS_SUCCESS; +#undef N_STACK_RECTS +} + + +static cairo_int_status_t +_cairo_gl_surface_fill_rectangles (void *abstract_surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects) +{ + if (GLEW_ARB_fragment_shader && GLEW_ARB_vertex_shader) { + return _cairo_gl_surface_fill_rectangles_glsl(abstract_surface, + op, + color, + rects, + num_rects); + } else { + return _cairo_gl_surface_fill_rectangles_fixed(abstract_surface, + op, + color, + rects, + num_rects); + } +} + +typedef struct _cairo_gl_surface_span_renderer { + cairo_span_renderer_t base; + + cairo_gl_composite_setup_t setup; + + int xmin, xmax; + + cairo_operator_t op; + cairo_antialias_t antialias; + + cairo_gl_surface_t *dst; + cairo_region_t *clip; + + GLuint vbo; + void *vbo_base; + unsigned int vbo_size; + unsigned int vbo_offset; + unsigned int vertex_size; +} cairo_gl_surface_span_renderer_t; + +static void +_cairo_gl_span_renderer_flush (cairo_gl_surface_span_renderer_t *renderer) +{ + int count; + + if (renderer->vbo_offset == 0) + return; + + glUnmapBufferARB (GL_ARRAY_BUFFER_ARB); + + count = renderer->vbo_offset / renderer->vertex_size; + renderer->vbo_offset = 0; + + if (renderer->clip) { + int i, num_rectangles = cairo_region_num_rectangles (renderer->clip); + + glEnable (GL_SCISSOR_TEST); + for (i = 0; i < num_rectangles; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (renderer->clip, i, &rect); + + glScissor (rect.x, rect.y, rect.width, rect.height); + glDrawArrays (GL_QUADS, 0, count); + } + glDisable (GL_SCISSOR_TEST); + } else { + glDrawArrays (GL_QUADS, 0, count); + } +} + +static void * +_cairo_gl_span_renderer_get_vbo (cairo_gl_surface_span_renderer_t *renderer, + unsigned int num_vertices) +{ + unsigned int offset; + + if (renderer->vbo == 0) { + renderer->vbo_size = 16384; + glGenBuffersARB (1, &renderer->vbo); + glBindBufferARB (GL_ARRAY_BUFFER_ARB, renderer->vbo); + + if (renderer->setup.src.type == OPERAND_TEXTURE) + renderer->vertex_size = 4 * sizeof (float) + sizeof (uint32_t); + else + renderer->vertex_size = 2 * sizeof (float) + sizeof (uint32_t); + + glVertexPointer (2, GL_FLOAT, renderer->vertex_size, 0); + glEnableClientState (GL_VERTEX_ARRAY); + + glColorPointer (4, GL_UNSIGNED_BYTE, renderer->vertex_size, + (void *) (uintptr_t) (2 * sizeof (float))); + glEnableClientState (GL_COLOR_ARRAY); + + if (renderer->setup.src.type == OPERAND_TEXTURE) { + glClientActiveTexture (GL_TEXTURE0); + glTexCoordPointer (2, GL_FLOAT, renderer->vertex_size, + (void *) (uintptr_t) (2 * sizeof (float) + + sizeof (uint32_t))); + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + } + } + + if (renderer->vbo_offset + num_vertices * renderer->vertex_size > + renderer->vbo_size) { + _cairo_gl_span_renderer_flush (renderer); + } + + if (renderer->vbo_offset == 0) { + /* We'll only be using these vertices once. */ + glBufferDataARB (GL_ARRAY_BUFFER_ARB, renderer->vbo_size, NULL, + GL_STREAM_DRAW_ARB); + renderer->vbo_base = glMapBufferARB (GL_ARRAY_BUFFER_ARB, + GL_WRITE_ONLY_ARB); + } + + offset = renderer->vbo_offset; + renderer->vbo_offset += num_vertices * renderer->vertex_size; + + return (char *) renderer->vbo_base + offset; +} + +static void +_cairo_gl_emit_span_vertex (cairo_gl_surface_span_renderer_t *renderer, + int dst_x, int dst_y, uint8_t alpha, + float *vertices) +{ + cairo_surface_attributes_t *src_attributes; + int v = 0; + + src_attributes = &renderer->setup.src.operand.texture.attributes; + + vertices[v++] = dst_x + BIAS; + vertices[v++] = dst_y + BIAS; + vertices[v++] = int_as_float (alpha << 24); + if (renderer->setup.src.type == OPERAND_TEXTURE) { + double s, t; + + s = dst_x + BIAS; + t = dst_y + BIAS; + cairo_matrix_transform_point (&src_attributes->matrix, &s, &t); + vertices[v++] = s; + vertices[v++] = t; + } +} + +static void +_cairo_gl_emit_span (cairo_gl_surface_span_renderer_t *renderer, + int x, int y1, int y2, + uint8_t alpha) +{ + float *vertices = _cairo_gl_span_renderer_get_vbo (renderer, 2); + + _cairo_gl_emit_span_vertex (renderer, x, y1, alpha, vertices); + _cairo_gl_emit_span_vertex (renderer, x, y2, alpha, + vertices + renderer->vertex_size / 4); +} + +static void +_cairo_gl_emit_rectangle (cairo_gl_surface_span_renderer_t *renderer, + int x1, int y1, + int x2, int y2, + int coverage) +{ + _cairo_gl_emit_span (renderer, x1, y1, y2, coverage); + _cairo_gl_emit_span (renderer, x2, y2, y1, coverage); +} + +static cairo_status_t +_cairo_gl_render_bounded_spans (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + do { + if (spans[0].coverage) { + _cairo_gl_emit_rectangle (renderer, + spans[0].x, y, + spans[1].x, y + height, + spans[0].coverage); + } + + spans++; + } while (--num_spans > 1); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_gl_render_unbounded_spans (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; + + if (num_spans == 0) { + _cairo_gl_emit_rectangle (renderer, + renderer->xmin, y, + renderer->xmax, y + height, + 0); + return CAIRO_STATUS_SUCCESS; + } + + if (spans[0].x != renderer->xmin) { + _cairo_gl_emit_rectangle (renderer, + renderer->xmin, y, + spans[0].x, y + height, + 0); + } + + do { + _cairo_gl_emit_rectangle (renderer, + spans[0].x, y, + spans[1].x, y + height, + spans[0].coverage); + spans++; + } while (--num_spans > 1); + + if (spans[0].x != renderer->xmax) { + _cairo_gl_emit_rectangle (renderer, + spans[0].x, y, + renderer->xmax, y + height, + 0); + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_gl_surface_span_renderer_destroy (void *abstract_renderer) +{ + cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; + + if (!renderer) + return; + + _cairo_gl_operand_destroy (&renderer->setup.src); + _cairo_gl_context_release (renderer->dst->ctx); + + free (renderer); +} + +static cairo_status_t +_cairo_gl_surface_span_renderer_finish (void *abstract_renderer) +{ + cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; + + _cairo_gl_span_renderer_flush (renderer); + + glBindBufferARB (GL_ARRAY_BUFFER_ARB, 0); + glDeleteBuffersARB (1, &renderer->vbo); + glDisableClientState (GL_VERTEX_ARRAY); + glDisableClientState (GL_COLOR_ARRAY); + + glClientActiveTexture (GL_TEXTURE0); + glDisableClientState (GL_TEXTURE_COORD_ARRAY); + glActiveTexture (GL_TEXTURE0); + glDisable (GL_TEXTURE_2D); + + glActiveTexture (GL_TEXTURE1); + glDisable (GL_TEXTURE_2D); + + glDisable (GL_BLEND); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_gl_surface_check_span_renderer (cairo_operator_t op, + const cairo_pattern_t *pattern, + void *abstract_dst, + cairo_antialias_t antialias) +{ + if (! _cairo_gl_operator_is_supported (op)) + return FALSE; + + if (! GLEW_ARB_vertex_buffer_object) + return FALSE; + + return TRUE; + + (void) pattern; + (void) abstract_dst; + (void) antialias; +} + +static cairo_span_renderer_t * +_cairo_gl_surface_create_span_renderer (cairo_operator_t op, + const cairo_pattern_t *src, + void *abstract_dst, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects, + cairo_region_t *clip_region) +{ + cairo_gl_surface_t *dst = abstract_dst; + cairo_gl_surface_span_renderer_t *renderer; + cairo_status_t status; + cairo_surface_attributes_t *src_attributes; + GLenum err; + + renderer = calloc (1, sizeof (*renderer)); + if (unlikely (renderer == NULL)) + return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY); + + renderer->base.destroy = _cairo_gl_surface_span_renderer_destroy; + renderer->base.finish = _cairo_gl_surface_span_renderer_finish; + renderer->base.render_row = + _cairo_gl_surface_span_renderer_render_row; + renderer->op = op; + renderer->antialias = antialias; + renderer->dst = dst; + renderer->clip = clip_region; + + renderer->composite_rectangles = *rects; + + status = _cairo_gl_operand_init (&renderer->setup.src, src, dst, + rects->src.x, rects->src.y, + rects->dst.x, rects->dst.y, + width, height); + if (unlikely (status)) { + _cairo_gl_context_acquire (dst->ctx); + _cairo_gl_surface_span_renderer_destroy (renderer); + return _cairo_span_renderer_create_in_error (status); + } + + _cairo_gl_context_acquire (dst->ctx); + _cairo_gl_set_destination (dst); + + src_attributes = &renderer->setup.src.operand.texture.attributes; + + _cairo_gl_set_operator (dst, op, FALSE); + _cairo_gl_set_src_operand (dst->ctx, &renderer->setup); + + /* Set up the mask to source from the incoming vertex color. */ + glActiveTexture (GL_TEXTURE1); + /* Have to have a dummy texture bound in order to use the combiner unit. */ + glBindTexture (GL_TEXTURE_2D, dst->ctx->dummy_tex); + glEnable (GL_TEXTURE_2D); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + + glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PRIMARY_COLOR); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + + while ((err = glGetError ())) + fprintf (stderr, "GL error 0x%08x\n", (int) err); + + return &renderer->base; +} + +static cairo_bool_t +_cairo_gl_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_gl_surface_t *surface = abstract_surface; + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = surface->width; + rectangle->height = surface->height; + + return TRUE; +} + +static void +_cairo_gl_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + _cairo_font_options_init_default (options); + + cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); +} + + +static cairo_int_status_t +_cairo_gl_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + /* simplify the common case of clearing the surface */ + if (op == CAIRO_OPERATOR_CLEAR && clip == NULL) { + _cairo_gl_surface_clear (abstract_surface); + + return CAIRO_STATUS_SUCCESS; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +const cairo_surface_backend_t _cairo_gl_surface_backend = { + CAIRO_SURFACE_TYPE_GL, + _cairo_gl_surface_create_similar, + _cairo_gl_surface_finish, + + _cairo_gl_surface_acquire_source_image, + _cairo_gl_surface_release_source_image, + _cairo_gl_surface_acquire_dest_image, + _cairo_gl_surface_release_dest_image, + + _cairo_gl_surface_clone_similar, + _cairo_gl_surface_composite, + _cairo_gl_surface_fill_rectangles, + _cairo_gl_surface_composite_trapezoids, + _cairo_gl_surface_create_span_renderer, + _cairo_gl_surface_check_span_renderer, + + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_gl_surface_get_extents, + NULL, /* old_show_glyphs */ + _cairo_gl_surface_get_font_options, + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + _cairo_gl_surface_scaled_glyph_fini, + _cairo_gl_surface_paint, + NULL, /* mask */ + NULL, /* stroke */ + NULL, /* fill */ + _cairo_gl_surface_show_glyphs, /* show_glyphs */ + NULL /* snapshot */ +}; + +/** Call glFinish(), used for accurate performance testing. */ +cairo_status_t +cairo_gl_surface_glfinish (cairo_surface_t *surface) +{ + glFinish (); + + return CAIRO_STATUS_SUCCESS; +} diff --git a/gfx/cairo/cairo/src/cairo-gl.h b/gfx/cairo/cairo/src/cairo-gl.h new file mode 100644 index 000000000000..1b8f79554a15 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl.h @@ -0,0 +1,101 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Eric Anholt + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Eric Anholt. + */ + +#ifndef CAIRO_GL_H +#define CAIRO_GL_H + +#include "cairo.h" + +#if CAIRO_HAS_GL_SURFACE + +CAIRO_BEGIN_DECLS + +typedef struct _cairo_gl_context cairo_gl_context_t; + +cairo_public cairo_gl_context_t * +cairo_gl_context_reference (cairo_gl_context_t *context); + +cairo_public void +cairo_gl_context_destroy (cairo_gl_context_t *context); + +cairo_public cairo_surface_t * +cairo_gl_surface_create (cairo_gl_context_t *ctx, + cairo_content_t content, + int width, int height); + +cairo_public void +cairo_gl_surface_set_size (cairo_surface_t *surface, int width, int height); + +cairo_public int +cairo_gl_surface_get_width (cairo_surface_t *abstract_surface); + +cairo_public int +cairo_gl_surface_get_height (cairo_surface_t *abstract_surface); + +cairo_public void +cairo_gl_surface_swapbuffers (cairo_surface_t *surface); + +cairo_public cairo_status_t +cairo_gl_surface_glfinish (cairo_surface_t *surface); + +#if CAIRO_HAS_GLX_FUNCTIONS +#include + +cairo_public cairo_gl_context_t * +cairo_glx_context_create (Display *dpy, GLXContext gl_ctx); + +cairo_public cairo_surface_t * +cairo_gl_surface_create_for_window (cairo_gl_context_t *ctx, + Window win, + int width, int height); +#endif + +#if CAIRO_HAS_EAGLE_FUNCTIONS +#include + +cairo_public cairo_gl_context_t * +cairo_eagle_context_create (EGLDisplay display, EGLContext context); + +cairo_public cairo_surface_t * +cairo_gl_surface_create_for_eagle (cairo_gl_context_t *ctx, + EGLSurface surface, + int width, int height); +#endif + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_GL_SURFACE */ +# error Cairo was not compiled with support for the GL backend +#endif /* CAIRO_HAS_GL_SURFACE */ + +#endif /* CAIRO_GL_H */ diff --git a/gfx/cairo/cairo/src/cairo-glitz-surface.c b/gfx/cairo/cairo/src/cairo-glitz-surface.c index cd0d254d80ed..5f97f65e8a08 100644 --- a/gfx/cairo/cairo/src/cairo-glitz-surface.c +++ b/gfx/cairo/cairo/src/cairo-glitz-surface.c @@ -27,6 +27,7 @@ #include "cairoint.h" #include "cairo-glitz.h" #include "cairo-glitz-private.h" +#include "cairo-region-private.h" typedef struct _cairo_glitz_surface { cairo_surface_t base; @@ -34,6 +35,7 @@ typedef struct _cairo_glitz_surface { glitz_surface_t *surface; glitz_format_t *format; + cairo_region_t *clip_region; cairo_bool_t has_clip; glitz_box_t *clip_boxes; int num_clip_boxes; @@ -50,6 +52,7 @@ _cairo_glitz_surface_finish (void *abstract_surface) if (surface->clip_boxes) free (surface->clip_boxes); + cairo_region_destroy (surface->clip_region); glitz_surface_destroy (surface->surface); return CAIRO_STATUS_SUCCESS; @@ -106,43 +109,6 @@ _cairo_glitz_surface_create_similar (void *abstract_src, return crsurface; } -static cairo_status_t -_cairo_glitz_get_boxes_from_region (cairo_region_t *region, - glitz_box_t **boxes, - int *nboxes) -{ - pixman_box32_t *pboxes; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - int n, i; - - n = 0; - pboxes = pixman_region32_rectangles (®ion->rgn, &n); - if (n == 0) { - *nboxes = 0; - return CAIRO_STATUS_SUCCESS; - } - - if (n > *nboxes) { - *boxes = _cairo_malloc_ab (n, sizeof (glitz_box_t)); - if (*boxes == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto done; - } - } - - for (i = 0; i < n; i++) { - (*boxes)[i].x1 = pboxes[i].x1; - (*boxes)[i].y1 = pboxes[i].y1; - (*boxes)[i].x2 = pboxes[i].x2; - (*boxes)[i].y2 = pboxes[i].y2; - } - - *nboxes = n; -done: - return status; -} - static cairo_status_t _cairo_glitz_surface_get_image (cairo_glitz_surface_t *surface, cairo_rectangle_int_t *interest, @@ -366,7 +332,6 @@ _cairo_glitz_surface_release_dest_image (void *abstract_surfa static cairo_status_t _cairo_glitz_surface_clone_similar (void *abstract_surface, cairo_surface_t *src, - cairo_content_t content, int src_x, int src_y, int width, @@ -441,10 +406,57 @@ _cairo_glitz_surface_set_matrix (cairo_glitz_surface_t *surface, glitz_surface_set_transform (surface->surface, &transform); } +static cairo_bool_t +_is_supported_operator (cairo_operator_t op) +{ + /* This is really just a if (op < SATURATE), but we use a switch + * so the compiler will warn if we ever add more operators. + */ + switch (op) { + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_ATOP: + case CAIRO_OPERATOR_DEST: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_DEST_ATOP: + case CAIRO_OPERATOR_XOR: + case CAIRO_OPERATOR_ADD: + return TRUE; + + default: + ASSERT_NOT_REACHED; + case CAIRO_OPERATOR_SATURATE: + /* nobody likes saturate, expect that it's required to do + * seamless polygons! + */ + case CAIRO_OPERATOR_MULTIPLY: + case CAIRO_OPERATOR_SCREEN: + case CAIRO_OPERATOR_OVERLAY: + case CAIRO_OPERATOR_DARKEN: + case CAIRO_OPERATOR_LIGHTEN: + case CAIRO_OPERATOR_COLOR_DODGE: + case CAIRO_OPERATOR_COLOR_BURN: + case CAIRO_OPERATOR_HARD_LIGHT: + case CAIRO_OPERATOR_SOFT_LIGHT: + case CAIRO_OPERATOR_DIFFERENCE: + case CAIRO_OPERATOR_EXCLUSION: + case CAIRO_OPERATOR_HSL_HUE: + case CAIRO_OPERATOR_HSL_SATURATION: + case CAIRO_OPERATOR_HSL_COLOR: + case CAIRO_OPERATOR_HSL_LUMINOSITY: + return FALSE; + } +} + static glitz_operator_t _glitz_operator (cairo_operator_t op) { - switch (op) { + switch ((int) op) { case CAIRO_OPERATOR_CLEAR: return GLITZ_OPERATOR_CLEAR; @@ -474,24 +486,17 @@ _glitz_operator (cairo_operator_t op) return GLITZ_OPERATOR_XOR; case CAIRO_OPERATOR_ADD: return GLITZ_OPERATOR_ADD; - case CAIRO_OPERATOR_SATURATE: - /* XXX: This line should never be reached. Glitz backend should bail - out earlier if saturate operator is used. OpenGL can't do saturate - with pre-multiplied colors. Solid colors can still be done as we - can just un-pre-multiply them. However, support for that will have - to be added to glitz. */ - /* fall-through */ - break; + default: + ASSERT_NOT_REACHED; + + /* Something's very broken if this line of code can be reached, so + * we want to return something that would give a noticeably + * incorrect result. The XOR operator seems so rearely desired + * that it should fit the bill here. + */ + return CAIRO_OPERATOR_XOR; } - - ASSERT_NOT_REACHED; - - /* Something's very broken if this line of code can be reached, so - we want to return something that would give a noticeably - incorrect result. The XOR operator seems so rearely desired - that it should fit the bill here. */ - return CAIRO_OPERATOR_XOR; } #define CAIRO_GLITZ_FEATURE_OK(surface, name) \ @@ -649,9 +654,9 @@ _cairo_glitz_pattern_acquire_surface (const cairo_pattern_t *pattern, } src = (cairo_glitz_surface_t *) - _cairo_surface_create_similar_scratch (&dst->base, - CAIRO_CONTENT_COLOR_ALPHA, - gradient->n_stops, 1); + _cairo_glitz_surface_create_similar (&dst->base, + CAIRO_CONTENT_COLOR_ALPHA, + gradient->n_stops, 1); if (src->base.status) { glitz_buffer_destroy (buffer); free (data); @@ -731,7 +736,6 @@ _cairo_glitz_pattern_acquire_surface (const cairo_pattern_t *pattern, cairo_int_status_t status; status = _cairo_pattern_acquire_surface (pattern, &dst->base, - CAIRO_CONTENT_COLOR_ALPHA, x, y, width, height, CAIRO_PATTERN_ACQUIRE_NONE, (cairo_surface_t **) &src, @@ -877,6 +881,77 @@ _cairo_glitz_surface_set_attributes (cairo_glitz_surface_t *surface, a->params, a->n_params); } +static cairo_status_t +_cairo_glitz_get_boxes_from_region (cairo_region_t *region, + glitz_box_t **boxes, + int *nboxes) +{ + pixman_box32_t *pboxes; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + int n, i; + + n = 0; + pboxes = pixman_region32_rectangles (®ion->rgn, &n); + if (n == 0) { + *nboxes = 0; + return CAIRO_STATUS_SUCCESS; + } + + if (n > *nboxes) { + *boxes = _cairo_malloc_ab (n, sizeof (glitz_box_t)); + if (*boxes == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto done; + } + } + + for (i = 0; i < n; i++) { + (*boxes)[i].x1 = pboxes[i].x1; + (*boxes)[i].y1 = pboxes[i].y1; + (*boxes)[i].x2 = pboxes[i].x2; + (*boxes)[i].y2 = pboxes[i].y2; + } + + *nboxes = n; +done: + return status; +} + +static cairo_status_t +_cairo_glitz_surface_set_clip_region (void *abstract_surface, + cairo_region_t *region) +{ + cairo_glitz_surface_t *surface = abstract_surface; + + if (region == surface->clip_region) + return CAIRO_STATUS_SUCCESS; + + cairo_region_destroy (surface->clip_region); + surface->clip_region = cairo_region_reference (region); + + if (region != NULL) { + cairo_status_t status; + + status = _cairo_glitz_get_boxes_from_region (region, + &surface->clip_boxes, + &surface->num_clip_boxes); + if (status) + return status; + + glitz_surface_set_clip_region (surface->surface, + 0, 0, + surface->clip_boxes, + surface->num_clip_boxes); + surface->has_clip = TRUE; + } else { + glitz_surface_set_clip_region (surface->surface, 0, 0, NULL, 0); + surface->has_clip = FALSE; + } + + return CAIRO_STATUS_SUCCESS; +} + static cairo_int_status_t _cairo_glitz_surface_composite (cairo_operator_t op, const cairo_pattern_t *src_pattern, @@ -889,7 +964,8 @@ _cairo_glitz_surface_composite (cairo_operator_t op, int dst_x, int dst_y, unsigned int width, - unsigned int height) + unsigned int height, + cairo_region_t *clip_region) { cairo_glitz_surface_attributes_t src_attr, mask_attr; cairo_glitz_surface_t *dst = abstract_dst; @@ -897,12 +973,16 @@ _cairo_glitz_surface_composite (cairo_operator_t op, cairo_glitz_surface_t *mask; cairo_int_status_t status; - if (op == CAIRO_OPERATOR_SATURATE) + if (! _is_supported_operator (op)) return CAIRO_INT_STATUS_UNSUPPORTED; if (_glitz_ensure_target (dst->surface)) return CAIRO_INT_STATUS_UNSUPPORTED; + status = _cairo_glitz_surface_set_clip_region (dst, clip_region); + if (status) + return status; + status = _cairo_glitz_pattern_acquire_surfaces (src_pattern, mask_pattern, dst, src_x, src_y, @@ -969,7 +1049,8 @@ _cairo_glitz_surface_composite (cairo_operator_t op, mask_width, mask_height, src_x, src_y, mask_x, mask_y, - dst_x, dst_y, width, height); + dst_x, dst_y, width, height, + clip_region); } if (mask) @@ -1000,8 +1081,15 @@ _cairo_glitz_surface_fill_rectangles (void *abstract_dst, glitz_rectangle_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (glitz_rectangle_t)]; glitz_rectangle_t *glitz_rects = stack_rects; glitz_rectangle_t *current_rect; + cairo_status_t status; int i; + if (! _is_supported_operator (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_glitz_surface_set_clip_region (dst, NULL); + assert (status == CAIRO_STATUS_SUCCESS); + if (n_rects > ARRAY_LENGTH (stack_rects)) { glitz_rects = _cairo_malloc_ab (n_rects, sizeof (glitz_rectangle_t)); if (glitz_rects == NULL) @@ -1064,12 +1152,12 @@ _cairo_glitz_surface_fill_rectangles (void *abstract_dst, _cairo_surface_create_similar_solid (&dst->base, CAIRO_CONTENT_COLOR_ALPHA, 1, 1, - (cairo_color_t *) color); - if (src->base.status) - { + (cairo_color_t *) color, + FALSE); + if (src == NULL || src->base.status) { if (glitz_rects != stack_rects) free (glitz_rects); - return src->base.status; + return src ? src->base.status : CAIRO_INT_STATUS_UNSUPPORTED; } glitz_surface_set_fill (src->surface, GLITZ_FILL_REPEAT); @@ -1113,7 +1201,8 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, unsigned int width, unsigned int height, cairo_trapezoid_t *traps, - int n_traps) + int n_traps, + cairo_region_t *clip_region) { cairo_glitz_surface_attributes_t attributes; cairo_glitz_surface_t *dst = abstract_dst; @@ -1133,12 +1222,16 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, return CAIRO_INT_STATUS_UNSUPPORTED; } - if (op == CAIRO_OPERATOR_SATURATE) + if (! _is_supported_operator (op)) return CAIRO_INT_STATUS_UNSUPPORTED; if (_glitz_ensure_target (dst->surface)) return CAIRO_INT_STATUS_UNSUPPORTED; + status = _cairo_glitz_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + return status; + /* Convert traps to pixman traps */ if (n_traps > ARRAY_LENGTH (stack_traps)) { pixman_traps = _cairo_malloc_ab (n_traps, sizeof (pixman_trapezoid_t)); @@ -1281,7 +1374,7 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, n_traps, (pixman_trapezoid_t *) pixman_traps); mask = (cairo_glitz_surface_t *) - _cairo_surface_create_similar_scratch (&dst->base, + _cairo_glitz_surface_create_similar (&dst->base, CAIRO_CONTENT_ALPHA, width, height); status = mask->base.status; @@ -1338,7 +1431,8 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, src_x, src_y, 0, 0, dst_x, dst_y, - width, height); + width, height, + clip_region); } FAIL: @@ -1353,35 +1447,7 @@ FAIL: return status; } -static cairo_int_status_t -_cairo_glitz_surface_set_clip_region (void *abstract_surface, - cairo_region_t *region) -{ - cairo_glitz_surface_t *surface = abstract_surface; - - if (region != NULL) { - cairo_status_t status; - - status = _cairo_glitz_get_boxes_from_region (region, - &surface->clip_boxes, - &surface->num_clip_boxes); - if (status) - return status; - - glitz_surface_set_clip_region (surface->surface, - 0, 0, - surface->clip_boxes, - surface->num_clip_boxes); - surface->has_clip = TRUE; - } else { - glitz_surface_set_clip_region (surface->surface, 0, 0, NULL, 0); - surface->has_clip = FALSE; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t +static cairo_bool_t _cairo_glitz_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { @@ -1392,7 +1458,7 @@ _cairo_glitz_surface_get_extents (void *abstract_surface, rectangle->width = glitz_surface_get_width (surface->surface); rectangle->height = glitz_surface_get_height (surface->surface); - return CAIRO_STATUS_SUCCESS; + return TRUE; } #define CAIRO_GLITZ_AREA_AVAILABLE 0 @@ -1995,7 +2061,8 @@ _cairo_glitz_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, unsigned int width, unsigned int height, cairo_glyph_t *glyphs, - int num_glyphs) + int num_glyphs, + cairo_region_t *clip_region) { cairo_glitz_surface_attributes_t attributes; cairo_glitz_surface_glyph_private_t *glyph_private; @@ -2028,7 +2095,7 @@ _cairo_glitz_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, scaled_font->surface_backend != _cairo_glitz_surface_get_backend ()) return CAIRO_INT_STATUS_UNSUPPORTED; - if (op == CAIRO_OPERATOR_SATURATE) + if (! _is_supported_operator (op)) return CAIRO_INT_STATUS_UNSUPPORTED; /* XXX Unbounded operators are not handled correctly */ @@ -2038,6 +2105,10 @@ _cairo_glitz_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, if (_glitz_ensure_target (dst->surface)) return CAIRO_INT_STATUS_UNSUPPORTED; + status = _cairo_glitz_surface_set_clip_region (dst, NULL); + if (unlikely (status)) + return status; + status = _cairo_glitz_pattern_acquire_surface (pattern, dst, src_x, src_y, width, height, @@ -2150,7 +2221,6 @@ _cairo_glitz_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, status = _cairo_glitz_surface_clone_similar (abstract_surface, image, - CAIRO_CONTENT_COLOR_ALPHA, 0, 0, glyph_width, @@ -2286,25 +2356,13 @@ _cairo_glitz_surface_is_similar (void *surface_a, return drawable_a == drawable_b; } -static cairo_status_t -_cairo_glitz_surface_reset (void *abstract_surface) -{ - cairo_glitz_surface_t *surface = abstract_surface; - cairo_status_t status; - - status = _cairo_glitz_surface_set_clip_region (surface, NULL); - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; -} - static const cairo_surface_backend_t cairo_glitz_surface_backend = { CAIRO_SURFACE_TYPE_GLITZ, _cairo_glitz_surface_create_similar, _cairo_glitz_surface_finish, _cairo_glitz_surface_acquire_source_image, _cairo_glitz_surface_release_source_image, + _cairo_glitz_surface_acquire_dest_image, _cairo_glitz_surface_release_dest_image, _cairo_glitz_surface_clone_similar, @@ -2313,10 +2371,9 @@ static const cairo_surface_backend_t cairo_glitz_surface_backend = { _cairo_glitz_surface_composite_trapezoids, NULL, /* create_span_renderer */ NULL, /* check_span_renderer */ + NULL, /* copy_page */ NULL, /* show_page */ - _cairo_glitz_surface_set_clip_region, - NULL, /* intersect_clip_path */ _cairo_glitz_surface_get_extents, _cairo_glitz_surface_old_show_glyphs, NULL, /* get_font_options */ @@ -2333,8 +2390,6 @@ static const cairo_surface_backend_t cairo_glitz_surface_backend = { _cairo_glitz_surface_snapshot, _cairo_glitz_surface_is_similar, - - _cairo_glitz_surface_reset }; static const cairo_surface_backend_t * @@ -2384,6 +2439,7 @@ cairo_glitz_surface_create (glitz_surface_t *surface) crsurface->has_clip = FALSE; crsurface->clip_boxes = NULL; crsurface->num_clip_boxes = 0; + crsurface->clip_region = NULL; return &crsurface->base; } diff --git a/gfx/cairo/cairo/src/cairo-glx-context.c b/gfx/cairo/cairo/src/cairo-glx-context.c new file mode 100644 index 000000000000..b442a1fc5ed1 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-glx-context.c @@ -0,0 +1,195 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Eric Anholt + * Copyright © 2009 Chris Wilson + * Copyright © 2005 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Carl Worth + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-gl-private.h" + +#include + +/* XXX needs hooking into XCloseDisplay() */ + +typedef struct _cairo_glx_context { + cairo_gl_context_t base; + + Display *display; + Window dummy_window; + GLXContext context; +} cairo_glx_context_t; + +typedef struct _cairo_glx_surface { + cairo_gl_surface_t base; + + Window win; +} cairo_glx_surface_t; + +static void +_glx_make_current (void *abstract_ctx, + cairo_gl_surface_t *abstract_surface) +{ + cairo_glx_context_t *ctx = abstract_ctx; + cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface; + + /* Set the window as the target of our context. */ + glXMakeCurrent (ctx->display, surface->win, ctx->context); +} + +static void +_glx_swap_buffers (void *abstract_ctx, + cairo_gl_surface_t *abstract_surface) +{ + cairo_glx_context_t *ctx = abstract_ctx; + cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface; + + glXSwapBuffers (ctx->display, surface->win); +} + +static void +_glx_destroy (void *abstract_ctx) +{ + cairo_glx_context_t *ctx = abstract_ctx; + + if (ctx->dummy_window != None) + XDestroyWindow (ctx->display, ctx->dummy_window); +} + +static cairo_status_t +_glx_dummy_ctx (Display *dpy, GLXContext gl_ctx, Window *dummy) +{ + int attr[3] = { GLX_FBCONFIG_ID, 0, None }; + GLXFBConfig *config; + XVisualInfo *vi; + Colormap cmap; + XSetWindowAttributes swa; + Window win = None; + int cnt; + + /* Create a dummy window created for the target GLX context that we can + * use to query the available GL/GLX extensions. + */ + glXQueryContext (dpy, gl_ctx, GLX_FBCONFIG_ID, &attr[1]); + + cnt = 0; + config = glXChooseFBConfig (dpy, DefaultScreen (dpy), attr, &cnt); + if (unlikely (cnt == 0)) + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); + + vi = glXGetVisualFromFBConfig (dpy, config[0]); + XFree (config); + + if (unlikely (vi == NULL)) + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); + + cmap = XCreateColormap (dpy, + RootWindow (dpy, vi->screen), + vi->visual, + AllocNone); + swa.colormap = cmap; + swa.border_pixel = 0; + win = XCreateWindow (dpy, RootWindow (dpy, vi->screen), + -1, -1, 1, 1, 0, + vi->depth, + InputOutput, + vi->visual, + CWBorderPixel | CWColormap, &swa); + XFreeColormap (dpy, cmap); + XFree (vi); + + XFlush (dpy); + if (unlikely (! glXMakeCurrent (dpy, win, gl_ctx))) { + XDestroyWindow (dpy, win); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + *dummy = win; + return CAIRO_STATUS_SUCCESS; +} + +cairo_gl_context_t * +cairo_glx_context_create (Display *dpy, GLXContext gl_ctx) +{ + cairo_glx_context_t *ctx; + cairo_status_t status; + Window dummy = None; + + status = _glx_dummy_ctx (dpy, gl_ctx, &dummy); + if (unlikely (status)) + return _cairo_gl_context_create_in_error (status); + + ctx = calloc (1, sizeof (cairo_glx_context_t)); + if (unlikely (ctx == NULL)) + return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); + + ctx->display = dpy; + ctx->dummy_window = dummy; + ctx->context = gl_ctx; + + ctx->base.make_current = _glx_make_current; + ctx->base.swap_buffers = _glx_swap_buffers; + ctx->base.destroy = _glx_destroy; + + status = _cairo_gl_context_init (&ctx->base); + if (unlikely (status)) { + free (ctx); + return _cairo_gl_context_create_in_error (status); + } + + return &ctx->base; +} + +cairo_surface_t * +cairo_gl_surface_create_for_window (cairo_gl_context_t *ctx, + Window win, + int width, + int height) +{ + cairo_glx_surface_t *surface; + + if (unlikely (ctx->status)) + return _cairo_surface_create_in_error (ctx->status); + + surface = calloc (1, sizeof (cairo_glx_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_gl_surface_init (ctx, &surface->base, + CAIRO_CONTENT_COLOR_ALPHA, width, height); + surface->win = win; + + return &surface->base.base; +} diff --git a/gfx/cairo/cairo/src/cairo-gstate.c b/gfx/cairo/cairo/src/cairo-gstate.c index f115080c0a02..fd16bb9def0e 100644 --- a/gfx/cairo/cairo/src/cairo-gstate.c +++ b/gfx/cairo/cairo/src/cairo-gstate.c @@ -100,7 +100,7 @@ _cairo_gstate_init (cairo_gstate_t *gstate, _cairo_font_options_init_default (&gstate->font_options); - _cairo_clip_init (&gstate->clip, target); + _cairo_clip_init (&gstate->clip); gstate->target = cairo_surface_reference (target); gstate->parent_target = NULL; @@ -164,14 +164,7 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) _cairo_font_options_init_copy (&gstate->font_options , &other->font_options); - status = _cairo_clip_init_copy (&gstate->clip, &other->clip); - if (unlikely (status)) { - _cairo_stroke_style_fini (&gstate->stroke_style); - cairo_font_face_destroy (gstate->font_face); - cairo_scaled_font_destroy (gstate->scaled_font); - cairo_scaled_font_destroy (gstate->previous_scaled_font); - return status; - } + _cairo_clip_init_copy (&gstate->clip, &other->clip); gstate->target = cairo_surface_reference (other->target); /* parent_target is always set to NULL; it's only ever set by redirect_target */ @@ -299,7 +292,7 @@ _cairo_gstate_restore (cairo_gstate_t **gstate, cairo_gstate_t **freelist) cairo_status_t _cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child) { - cairo_status_t status; + cairo_matrix_t matrix; /* If this gstate is already redirected, this is an error; we need a * new gstate to be able to redirect */ @@ -315,18 +308,15 @@ _cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child) * since its ref is now owned by gstate->parent_target */ gstate->target = cairo_surface_reference (child); - _cairo_clip_reset (&gstate->clip); - status = _cairo_clip_init_deep_copy (&gstate->clip, &gstate->next->clip, child); - if (unlikely (status)) - return status; - /* The clip is in surface backend coordinates for the previous target; * translate it into the child's backend coordinates. */ - _cairo_clip_translate (&gstate->clip, - _cairo_fixed_from_double (child->device_transform.x0 - gstate->parent_target->device_transform.x0), - _cairo_fixed_from_double (child->device_transform.y0 - gstate->parent_target->device_transform.y0)); - - return CAIRO_STATUS_SUCCESS; + cairo_matrix_init_translate (&matrix, + child->device_transform.x0 - gstate->parent_target->device_transform.x0, + child->device_transform.y0 - gstate->parent_target->device_transform.y0); + _cairo_clip_reset (&gstate->clip); + return _cairo_clip_init_copy_transformed (&gstate->clip, + &gstate->next->clip, + &matrix); } /** @@ -549,16 +539,19 @@ _cairo_gstate_set_dash (cairo_gstate_t *gstate, const double *dash, int num_dash if (dash_total == 0.0) return _cairo_error (CAIRO_STATUS_INVALID_DASH); - /* A single dash value indicate symmetric repeating, so the total + /* An odd dash value indicate symmetric repeating, so the total * is twice as long. */ - if (gstate->stroke_style.num_dashes == 1) + if (gstate->stroke_style.num_dashes & 1) dash_total *= 2; - /* The dashing code doesn't like a negative offset, so we compute - * the equivalent positive offset. */ - if (offset < 0) - offset += ceil (-offset / dash_total + 0.5) * dash_total; - + /* The dashing code doesn't like a negative offset or a big positive + * offset, so we compute an equivalent offset which is guaranteed to be + * positive and less than twice the pattern length. */ + offset = fmod (offset, dash_total); + if (offset < 0.0) + offset += dash_total; + if (offset <= 0.0) /* Take care of -0 */ + offset = 0.0; gstate->stroke_style.dash_offset = offset; return CAIRO_STATUS_SUCCESS; @@ -827,13 +820,83 @@ _cairo_gstate_path_extents (cairo_gstate_t *gstate, *y2 = py2; } +static void +_init_solid_for_color_stop (cairo_solid_pattern_t *solid, + const cairo_color_t *color) +{ + cairo_color_t premult; + + /* Color stops aren't premultiplied, so fix that here */ + _cairo_color_init_rgba (&premult, + color->red, + color->green, + color->blue, + color->alpha); + _cairo_pattern_init_solid (solid, &premult, CAIRO_CONTENT_COLOR_ALPHA); +} + +static void +_cairo_gstate_copy_pattern (cairo_pattern_t *pattern, + const cairo_pattern_t *original) +{ + /* First check if the we can replace the original with a much simpler + * pattern. For example, gradients that are uniform or just have a single + * stop can be replace with a solid. + */ + switch (original->type) { + case CAIRO_PATTERN_TYPE_SOLID: + case CAIRO_PATTERN_TYPE_SURFACE: + break; + + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + { + cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) original; + + /* fast path for gradients with less than 2 color stops */ + if (src->n_stops < 2) { + if (src->n_stops) { + _init_solid_for_color_stop ((cairo_solid_pattern_t *) pattern, + &src->stops->color); + } else { + _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern, + CAIRO_COLOR_TRANSPARENT, + CAIRO_CONTENT_ALPHA); + } + + return; + } else { + unsigned int i; + + /* Is the gradient a uniform colour? + * Happens more often than you would believe. + */ + for (i = 1; i < src->n_stops; i++) { + if (! _cairo_color_equal (&src->stops[0].color, + &src->stops[i].color)) + { + break; + } + } + if (i == src->n_stops) { + _init_solid_for_color_stop ((cairo_solid_pattern_t *) pattern, + &src->stops->color); + return; + } + } + } + } + + _cairo_pattern_init_static_copy (pattern, original); +} + static void _cairo_gstate_copy_transformed_pattern (cairo_gstate_t *gstate, cairo_pattern_t *pattern, - cairo_pattern_t *original, - cairo_matrix_t *ctm_inverse) + const cairo_pattern_t *original, + const cairo_matrix_t *ctm_inverse) { - _cairo_pattern_init_static_copy (pattern, original); + _cairo_gstate_copy_pattern (pattern, original); /* apply device_transform first so that it is transformed by ctm_inverse */ if (original->type == CAIRO_PATTERN_TYPE_SURFACE) { @@ -849,6 +912,10 @@ _cairo_gstate_copy_transformed_pattern (cairo_gstate_t *gstate, if (! _cairo_matrix_is_identity (ctm_inverse)) _cairo_pattern_transform (pattern, ctm_inverse); + + if (_cairo_surface_has_device_transform (gstate->target)) + _cairo_pattern_transform (pattern, + &gstate->target->device_transform_inverse); } static void @@ -870,82 +937,156 @@ _cairo_gstate_copy_transformed_mask (cairo_gstate_t *gstate, &gstate->ctm_inverse); } +/* We need to take a copy of the clip so that the lower layers may modify it + * by, perhaps, intersecting it with the operation extents and other paths. + */ +#define _gstate_get_clip(G, C) _cairo_clip_init_copy ((C), &(G)->clip) + +static cairo_bool_t +_clipped (cairo_gstate_t *gstate) +{ + cairo_rectangle_int_t extents; + + if (gstate->clip.all_clipped) + return TRUE; + + /* XXX consider applying a surface clip? */ + + if (gstate->clip.path == NULL) + return FALSE; + + if (_cairo_surface_get_extents (gstate->target, &extents)) { + if (extents.width == 0 || extents.height == 0) + return TRUE; + + if (! _cairo_rectangle_intersect (&extents, + &gstate->clip.path->extents)) + { + return TRUE; + } + } + + /* perform a simple query to exclude trivial all-clipped cases */ + return _cairo_clip_get_region (&gstate->clip, NULL) == CAIRO_INT_STATUS_NOTHING_TO_DO; +} + cairo_status_t _cairo_gstate_paint (cairo_gstate_t *gstate) { - cairo_status_t status; cairo_pattern_union_t pattern; + cairo_clip_t clip; + cairo_status_t status; - if (gstate->source->status) + if (unlikely (gstate->source->status)) return gstate->source->status; - status = _cairo_surface_set_clip (gstate->target, &gstate->clip); - if (unlikely (status)) - return status; + if (_clipped (gstate)) + return CAIRO_STATUS_SUCCESS; _cairo_gstate_copy_transformed_source (gstate, &pattern.base); - return _cairo_surface_paint (gstate->target, - gstate->op, - &pattern.base, - NULL); + status = _cairo_surface_paint (gstate->target, + gstate->op, + &pattern.base, + _gstate_get_clip (gstate, &clip)); + _cairo_clip_fini (&clip); + + return status; } cairo_status_t _cairo_gstate_mask (cairo_gstate_t *gstate, cairo_pattern_t *mask) { - cairo_status_t status; cairo_pattern_union_t source_pattern, mask_pattern; + cairo_clip_t clip; + cairo_status_t status; - if (mask->status) + if (unlikely (mask->status)) return mask->status; - if (gstate->source->status) + if (unlikely (gstate->source->status)) return gstate->source->status; - status = _cairo_surface_set_clip (gstate->target, &gstate->clip); - if (unlikely (status)) - return status; + if (_clipped (gstate)) + return CAIRO_STATUS_SUCCESS; + + if (_cairo_pattern_is_opaque (mask)) + return _cairo_gstate_paint (gstate); _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); _cairo_gstate_copy_transformed_mask (gstate, &mask_pattern.base, mask); - return _cairo_surface_mask (gstate->target, - gstate->op, - &source_pattern.base, - &mask_pattern.base, - NULL); + if (source_pattern.type == CAIRO_PATTERN_TYPE_SOLID && + mask_pattern.type == CAIRO_PATTERN_TYPE_SOLID) + { + cairo_color_t combined; + + if (mask_pattern.base.has_component_alpha) { +#define M(R, A, B, c) R.c = A.c * B.c + M(combined, source_pattern.solid.color, mask_pattern.solid.color, red); + M(combined, source_pattern.solid.color, mask_pattern.solid.color, green); + M(combined, source_pattern.solid.color, mask_pattern.solid.color, blue); + M(combined, source_pattern.solid.color, mask_pattern.solid.color, alpha); +#undef M + } else { + combined = source_pattern.solid.color; + _cairo_color_multiply_alpha (&combined, mask_pattern.solid.color.alpha); + } + + _cairo_pattern_init_solid (&source_pattern.solid, &combined, + source_pattern.solid.content | + mask_pattern.solid.content); + + status = _cairo_surface_paint (gstate->target, + gstate->op, + &source_pattern.base, + _gstate_get_clip (gstate, &clip)); + } + else + { + status = _cairo_surface_mask (gstate->target, + gstate->op, + &source_pattern.base, + &mask_pattern.base, + _gstate_get_clip (gstate, &clip)); + } + _cairo_clip_fini (&clip); + + return status; } cairo_status_t _cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path) { - cairo_status_t status; cairo_pattern_union_t source_pattern; + cairo_clip_t clip; + cairo_status_t status; - if (gstate->source->status) + if (unlikely (gstate->source->status)) return gstate->source->status; if (gstate->stroke_style.line_width <= 0.0) return CAIRO_STATUS_SUCCESS; - status = _cairo_surface_set_clip (gstate->target, &gstate->clip); - if (unlikely (status)) - return status; + if (_clipped (gstate)) + return CAIRO_STATUS_SUCCESS; _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); - return _cairo_surface_stroke (gstate->target, - gstate->op, - &source_pattern.base, - path, - &gstate->stroke_style, - &gstate->ctm, - &gstate->ctm_inverse, - gstate->tolerance, - gstate->antialias, - NULL); + status = _cairo_surface_stroke (gstate->target, + gstate->op, + &source_pattern.base, + path, + &gstate->stroke_style, + &gstate->ctm, + &gstate->ctm_inverse, + gstate->tolerance, + gstate->antialias, + _gstate_get_clip (gstate, &clip)); + _cairo_clip_fini (&clip); + + return status; } cairo_status_t @@ -981,13 +1122,13 @@ _cairo_gstate_in_stroke (cairo_gstate_t *gstate, return CAIRO_STATUS_SUCCESS; } - limit.p1.x = _cairo_fixed_from_double (x) - 1; - limit.p1.y = _cairo_fixed_from_double (y) - 1; - limit.p2.x = limit.p1.x + 2; - limit.p2.y = limit.p1.y + 2; + limit.p1.x = _cairo_fixed_from_double (x) - 5; + limit.p1.y = _cairo_fixed_from_double (y) - 5; + limit.p2.x = limit.p1.x + 5; + limit.p2.y = limit.p1.y + 5; _cairo_traps_init (&traps); - _cairo_traps_limit (&traps, &limit); + _cairo_traps_limit (&traps, &limit, 1); status = _cairo_path_fixed_stroke_to_traps (path, &gstate->stroke_style, @@ -1009,42 +1150,92 @@ BAIL: cairo_status_t _cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path) { - cairo_status_t status; cairo_pattern_union_t pattern; + cairo_clip_t clip; + cairo_status_t status; - if (gstate->source->status) + if (unlikely (gstate->source->status)) return gstate->source->status; - status = _cairo_surface_set_clip (gstate->target, &gstate->clip); - if (unlikely (status)) - return status; + if (_clipped (gstate)) + return CAIRO_STATUS_SUCCESS; - _cairo_gstate_copy_transformed_source (gstate, &pattern.base); + if (_cairo_path_fixed_fill_is_empty (path)) { + if (_cairo_operator_bounded_by_mask (gstate->op)) + return CAIRO_STATUS_SUCCESS; - return _cairo_surface_fill (gstate->target, - gstate->op, - &pattern.base, - path, - gstate->fill_rule, - gstate->tolerance, - gstate->antialias, - NULL); + _cairo_pattern_init_solid (&pattern.solid, + CAIRO_COLOR_TRANSPARENT, + CAIRO_CONTENT_COLOR_ALPHA); + status = _cairo_surface_paint (gstate->target, + CAIRO_OPERATOR_CLEAR, + &pattern.base, + _gstate_get_clip (gstate, &clip)); + } else { + _cairo_gstate_copy_transformed_source (gstate, &pattern.base); + + status = _cairo_surface_fill (gstate->target, + gstate->op, + &pattern.base, + path, + gstate->fill_rule, + gstate->tolerance, + gstate->antialias, + _gstate_get_clip (gstate, &clip)); + } + + _cairo_clip_fini (&clip); + + return status; } -void +cairo_bool_t _cairo_gstate_in_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path, double x, - double y, - cairo_bool_t *inside_ret) + double y) { _cairo_gstate_user_to_backend (gstate, &x, &y); - _cairo_path_fixed_in_fill (path, - gstate->fill_rule, - gstate->tolerance, - x, y, - inside_ret); + return _cairo_path_fixed_in_fill (path, + gstate->fill_rule, + gstate->tolerance, + x, y); +} + +cairo_bool_t +_cairo_gstate_in_clip (cairo_gstate_t *gstate, + double x, + double y) +{ + cairo_clip_path_t *clip_path; + + if (gstate->clip.all_clipped) + return FALSE; + + clip_path = gstate->clip.path; + if (clip_path == NULL) + return TRUE; + + _cairo_gstate_user_to_backend (gstate, &x, &y); + + if (x < clip_path->extents.x || + x >= clip_path->extents.x + clip_path->extents.width || + y < clip_path->extents.y || + y >= clip_path->extents.y + clip_path->extents.height) + { + return FALSE; + } + + do { + if (! _cairo_path_fixed_in_fill (&clip_path->path, + clip_path->fill_rule, + clip_path->tolerance, + x, y)) + return FALSE; + } while ((clip_path = clip_path->prev) != NULL); + + return TRUE; } cairo_status_t @@ -1132,7 +1323,7 @@ _cairo_gstate_stroke_extents (cairo_gstate_t *gstate, &gstate->ctm_inverse, gstate->tolerance, &traps); - if (status == CAIRO_STATUS_SUCCESS) { + if (likely (status == CAIRO_STATUS_SUCCESS)) { _cairo_gstate_traps_extents_to_user_rectangle (gstate, &traps, x1, y1, x2, y2); } @@ -1151,13 +1342,25 @@ _cairo_gstate_fill_extents (cairo_gstate_t *gstate, cairo_status_t status; cairo_traps_t traps; + if (path->is_empty_fill) { + if (x1) + *x1 = 0.0; + if (y1) + *y1 = 0.0; + if (x2) + *x2 = 0.0; + if (y2) + *y2 = 0.0; + return CAIRO_STATUS_SUCCESS; + } + _cairo_traps_init (&traps); status = _cairo_path_fixed_fill_to_traps (path, gstate->fill_rule, gstate->tolerance, &traps); - if (status == CAIRO_STATUS_SUCCESS) { + if (likely (status == CAIRO_STATUS_SUCCESS)) { _cairo_gstate_traps_extents_to_user_rectangle (gstate, &traps, x1, y1, x2, y2); } @@ -1179,26 +1382,31 @@ cairo_status_t _cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path) { return _cairo_clip_clip (&gstate->clip, - path, gstate->fill_rule, gstate->tolerance, - gstate->antialias, gstate->target); + path, gstate->fill_rule, + gstate->tolerance, gstate->antialias); } -static cairo_status_t +static cairo_bool_t _cairo_gstate_int_clip_extents (cairo_gstate_t *gstate, cairo_rectangle_int_t *extents) { - cairo_status_t status; + const cairo_rectangle_int_t *clip_extents; + cairo_bool_t is_bounded; - status = _cairo_surface_get_extents (gstate->target, extents); - if (unlikely (status)) - return status; + is_bounded = _cairo_surface_get_extents (gstate->target, extents); - status = _cairo_clip_intersect_to_rectangle (&gstate->clip, extents); + clip_extents = _cairo_clip_get_extents (&gstate->clip); + if (clip_extents != NULL) { + cairo_bool_t is_empty; - return status; + is_empty = _cairo_rectangle_intersect (extents, clip_extents); + is_bounded = TRUE; + } + + return is_bounded; } -cairo_status_t +cairo_bool_t _cairo_gstate_clip_extents (cairo_gstate_t *gstate, double *x1, double *y1, @@ -1207,11 +1415,9 @@ _cairo_gstate_clip_extents (cairo_gstate_t *gstate, { cairo_rectangle_int_t extents; double px1, py1, px2, py2; - cairo_status_t status; - status = _cairo_gstate_int_clip_extents (gstate, &extents); - if (unlikely (status)) - return status; + if (! _cairo_gstate_int_clip_extents (gstate, &extents)) + return FALSE; px1 = extents.x; py1 = extents.y; @@ -1231,7 +1437,7 @@ _cairo_gstate_clip_extents (cairo_gstate_t *gstate, if (y2) *y2 = py2; - return CAIRO_STATUS_SUCCESS; + return TRUE; } cairo_rectangle_list_t* @@ -1580,13 +1786,13 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, cairo_text_cluster_t stack_transformed_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)]; cairo_text_cluster_t *transformed_clusters; cairo_status_t status; + cairo_clip_t clip; - if (gstate->source->status) + if (unlikely (gstate->source->status)) return gstate->source->status; - status = _cairo_surface_set_clip (gstate->target, &gstate->clip); - if (unlikely (status)) - return status; + if (_clipped (gstate)) + return CAIRO_STATUS_SUCCESS; status = _cairo_gstate_ensure_scaled_font (gstate); if (unlikely (status)) @@ -1653,8 +1859,11 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, transformed_glyphs, num_glyphs, transformed_clusters, num_clusters, cluster_flags, - gstate->scaled_font, NULL); - } else { + gstate->scaled_font, + _gstate_get_clip (gstate, &clip)); + } + else + { cairo_path_fixed_t path; _cairo_path_fixed_init (&path); @@ -1663,18 +1872,22 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, transformed_glyphs, num_glyphs, &path); - if (status == CAIRO_STATUS_SUCCESS) - status = _cairo_surface_fill (gstate->target, - gstate->op, - &source_pattern.base, - &path, - CAIRO_FILL_RULE_WINDING, - gstate->tolerance, - gstate->scaled_font->options.antialias, NULL); + if (status == CAIRO_STATUS_SUCCESS) { + status = _cairo_surface_fill (gstate->target, + gstate->op, + &source_pattern.base, + &path, + CAIRO_FILL_RULE_WINDING, + gstate->tolerance, + gstate->scaled_font->options.antialias, + _gstate_get_clip (gstate, &clip)); + } _cairo_path_fixed_fini (&path); } + _cairo_clip_fini (&clip); + CLEANUP_GLYPHS: if (transformed_glyphs != stack_transformed_glyphs) cairo_glyph_free (transformed_glyphs); @@ -1775,17 +1988,12 @@ _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, cairo_matrix_t *device_transform = &gstate->target->device_transform; cairo_bool_t drop = FALSE; double x1 = 0, x2 = 0, y1 = 0, y2 = 0; - cairo_status_t status; if (num_transformed_glyphs != NULL) { cairo_rectangle_int_t surface_extents; drop = TRUE; - status = _cairo_gstate_int_clip_extents (gstate, &surface_extents); - if (_cairo_status_is_error (status)) - return status; - - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + if (! _cairo_gstate_int_clip_extents (gstate, &surface_extents)) { drop = FALSE; /* unbounded surface */ } else { double scale10 = 10 * _cairo_scaled_font_get_max_scale (gstate->scaled_font); diff --git a/gfx/cairo/cairo/src/cairo-hash.c b/gfx/cairo/cairo/src/cairo-hash.c index 51303f5a76d8..15159d928b34 100644 --- a/gfx/cairo/cairo/src/cairo-hash.c +++ b/gfx/cairo/cairo/src/cairo-hash.c @@ -191,9 +191,6 @@ _cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal) void _cairo_hash_table_destroy (cairo_hash_table_t *hash_table) { - if (hash_table == NULL) - return; - /* The hash table must be empty. Otherwise, halt. */ assert (hash_table->live_entries == 0); /* No iterators can be running. Otherwise, halt. */ @@ -525,9 +522,6 @@ _cairo_hash_table_foreach (cairo_hash_table_t *hash_table, unsigned long i; cairo_hash_entry_t *entry; - if (hash_table == NULL) - return; - /* Mark the table for iteration */ ++hash_table->iterating; for (i = 0; i < hash_table->arrangement->size; i++) { diff --git a/gfx/cairo/cairo/src/cairo-hull.c b/gfx/cairo/cairo/src/cairo-hull.c index 1fa919bf321a..8a1a31e24e33 100644 --- a/gfx/cairo/cairo/src/cairo-hull.c +++ b/gfx/cairo/cairo/src/cairo-hull.c @@ -36,6 +36,8 @@ #include "cairoint.h" +#include "cairo-slope-private.h" + typedef struct cairo_hull { cairo_point_t point; cairo_slope_t slope; diff --git a/gfx/cairo/cairo/src/cairo-image-surface.c b/gfx/cairo/cairo/src/cairo-image-surface.c index 27a3a34b315f..d52979dbd999 100644 --- a/gfx/cairo/cairo/src/cairo-image-surface.c +++ b/gfx/cairo/cairo/src/cairo-image-surface.c @@ -37,6 +37,21 @@ #include "cairoint.h" +#include "cairo-clip-private.h" +#include "cairo-region-private.h" + +/* Limit on the width / height of an image surface in pixels. This is + * mainly determined by coordinates of things sent to pixman at the + * moment being in 16.16 format. */ +#define MAX_IMAGE_SIZE 32767 + +static cairo_bool_t +_cairo_image_surface_is_size_valid (int width, int height) +{ + return 0 <= width && width <= MAX_IMAGE_SIZE && + 0 <= height && height <= MAX_IMAGE_SIZE; +} + static cairo_format_t _cairo_format_from_pixman_format (pixman_format_code_t pixman_format) { @@ -63,6 +78,14 @@ _cairo_format_from_pixman_format (pixman_format_code_t pixman_format) #if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,11,9) case PIXMAN_x2b10g10r10: case PIXMAN_a2b10g10r10: +#endif +#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,14,1) + case PIXMAN_b8g8r8x8: + case PIXMAN_b8g8r8a8: +#endif +#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,15,16) + case PIXMAN_x2r10g10b10: + case PIXMAN_a2r10g10b10: #endif default: return CAIRO_FORMAT_INVALID; @@ -87,6 +110,12 @@ _cairo_content_from_pixman_format (pixman_format_code_t pixman_format) case PIXMAN_a1b1g1r1: #if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,11,9) case PIXMAN_a2b10g10r10: +#endif +#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,14,1) + case PIXMAN_b8g8r8a8: +#endif +#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,15,16) + case PIXMAN_a2r10g10b10: #endif return CAIRO_CONTENT_COLOR_ALPHA; case PIXMAN_x8r8g8b8: @@ -112,6 +141,12 @@ _cairo_content_from_pixman_format (pixman_format_code_t pixman_format) case PIXMAN_yv12: #if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,11,9) case PIXMAN_x2b10g10r10: +#endif +#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,14,1) + case PIXMAN_b8g8r8x8: +#endif +#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,15,16) + case PIXMAN_x2r10g10b10: #endif return CAIRO_CONTENT_COLOR; case PIXMAN_a8: @@ -129,6 +164,12 @@ _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, pixman_format_code_t pixman_format) { cairo_image_surface_t *surface; + int width = pixman_image_get_width (pixman_image); + int height = pixman_image_get_height (pixman_image); + + if (! _cairo_image_surface_is_size_valid (width, height)) { + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + } surface = malloc (sizeof (cairo_image_surface_t)); if (unlikely (surface == NULL)) @@ -143,14 +184,15 @@ _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, surface->format = _cairo_format_from_pixman_format (pixman_format); surface->data = (unsigned char *) pixman_image_get_data (pixman_image); surface->owns_data = FALSE; - surface->has_clip = FALSE; surface->transparency = CAIRO_IMAGE_UNKNOWN; - surface->width = pixman_image_get_width (pixman_image); - surface->height = pixman_image_get_height (pixman_image); + surface->width = width; + surface->height = height; surface->stride = pixman_image_get_stride (pixman_image); surface->depth = pixman_image_get_depth (pixman_image); + surface->clip_region = NULL; + return &surface->base; } @@ -188,11 +230,10 @@ _pixman_format_from_masks (cairo_format_masks_t *masks, * expected. This avoid any problems from something bizarre like * alpha in the least-significant bits, or insane channel order, * or whatever. */ - _pixman_format_to_masks (format, &format_masks); - - if (masks->bpp != format_masks.bpp || - masks->red_mask != format_masks.red_mask || - masks->green_mask != format_masks.green_mask || + if (!_pixman_format_to_masks (format, &format_masks) || + masks->bpp != format_masks.bpp || + masks->red_mask != format_masks.red_mask || + masks->green_mask != format_masks.green_mask || masks->blue_mask != format_masks.blue_mask) { return FALSE; @@ -205,7 +246,7 @@ _pixman_format_from_masks (cairo_format_masks_t *masks, /* A mask consisting of N bits set to 1. */ #define MASK(N) ((1UL << (N))-1) -void +cairo_bool_t _pixman_format_to_masks (pixman_format_code_t format, cairo_format_masks_t *masks) { @@ -225,19 +266,27 @@ _pixman_format_to_masks (pixman_format_code_t format, masks->red_mask = MASK (r) << (g + b); masks->green_mask = MASK (g) << (b); masks->blue_mask = MASK (b); - return; + return TRUE; case PIXMAN_TYPE_ABGR: masks->alpha_mask = MASK (a) << (b + g + r); - masks->blue_mask = MASK (b) << (g +r); + masks->blue_mask = MASK (b) << (g + r); masks->green_mask = MASK (g) << (r); masks->red_mask = MASK (r); - return; + return TRUE; +#ifdef PIXMAN_TYPE_BGRA + case PIXMAN_TYPE_BGRA: + masks->blue_mask = MASK (b) << (masks->bpp - b); + masks->green_mask = MASK (g) << (masks->bpp - b - g); + masks->red_mask = MASK (r) << (masks->bpp - b - g - r); + masks->alpha_mask = MASK (a); + return TRUE; +#endif case PIXMAN_TYPE_A: masks->alpha_mask = MASK (a); masks->red_mask = 0; masks->green_mask = 0; masks->blue_mask = 0; - return; + return TRUE; case PIXMAN_TYPE_OTHER: case PIXMAN_TYPE_COLOR: case PIXMAN_TYPE_GRAY: @@ -248,7 +297,7 @@ _pixman_format_to_masks (pixman_format_code_t format, masks->red_mask = 0; masks->green_mask = 0; masks->blue_mask = 0; - return; + return FALSE; } } @@ -324,6 +373,11 @@ _cairo_image_surface_create_with_pixman_format (unsigned char *data, cairo_surface_t *surface; pixman_image_t *pixman_image; + if (! _cairo_image_surface_is_size_valid (width, height)) + { + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + } + pixman_image = pixman_image_create_bits (pixman_format, width ? width : 1, height ? height : 1, (uint32_t *) data, stride ? stride : 4); @@ -332,9 +386,13 @@ _cairo_image_surface_create_with_pixman_format (unsigned char *data, surface = _cairo_image_surface_create_for_pixman_image (pixman_image, pixman_format); - if (cairo_surface_status (surface)) + if (unlikely (surface->status)) { pixman_image_unref (pixman_image); + return surface; + } + /* we can not make any assumptions about the initial state of user data */ + surface->is_clear = data == NULL; return surface; } @@ -458,7 +516,7 @@ slim_hidden_def (cairo_format_stride_for_width); * and row. This alignment is required to allow high-performance rendering * within cairo. The correct way to obtain a legal stride value is to * call cairo_format_stride_for_width() with the desired format and - * maximum image width value, and the use the resulting stride value + * maximum image width value, and then use the resulting stride value * to allocate the data and to create the image surface. See * cairo_format_stride_for_width() for example code. * @@ -504,9 +562,10 @@ cairo_image_surface_create_for_data (unsigned char *data, } pixman_format = _cairo_format_to_pixman_format_code (format); - - return _cairo_image_surface_create_with_pixman_format (data, pixman_format, - width, height, stride); + return _cairo_image_surface_create_with_pixman_format (data, + pixman_format, + width, height, + stride); } slim_hidden_def (cairo_image_surface_create_for_data); @@ -574,6 +633,7 @@ cairo_image_surface_get_format (cairo_surface_t *surface) return image_surface->format; } +slim_hidden_def (cairo_image_surface_get_format); /** * cairo_image_surface_get_width: @@ -699,12 +759,19 @@ _cairo_format_bits_per_pixel (cairo_format_t format) } static cairo_surface_t * -_cairo_image_surface_create_similar (void *abstract_src, +_cairo_image_surface_create_similar (void *abstract_other, cairo_content_t content, int width, int height) { - assert (CAIRO_CONTENT_VALID (content)); + cairo_image_surface_t *other = abstract_other; + + if (content == other->base.content) { + return _cairo_image_surface_create_with_pixman_format (NULL, + other->pixman_format, + width, height, + 0); + } return _cairo_image_surface_create_with_content (content, width, height); @@ -725,6 +792,8 @@ _cairo_image_surface_finish (void *abstract_surface) surface->data = NULL; } + cairo_region_destroy (surface->clip_region); + return CAIRO_STATUS_SUCCESS; } @@ -784,7 +853,6 @@ _cairo_image_surface_release_dest_image (void *abstract_surfa static cairo_status_t _cairo_image_surface_clone_similar (void *abstract_surface, cairo_surface_t *src, - cairo_content_t content, int src_x, int src_y, int width, @@ -862,6 +930,39 @@ _cairo_image_surface_set_filter (cairo_image_surface_t *surface, return CAIRO_STATUS_SUCCESS; } +static cairo_status_t +_cairo_image_surface_set_extend (cairo_image_surface_t *surface, + cairo_extend_t extend) +{ + pixman_repeat_t pixman_repeat; + + switch (extend) { + case CAIRO_EXTEND_NONE: + pixman_repeat = PIXMAN_REPEAT_NONE; + break; + case CAIRO_EXTEND_REPEAT: + pixman_repeat = PIXMAN_REPEAT_NORMAL; + break; + case CAIRO_EXTEND_REFLECT: + pixman_repeat = PIXMAN_REPEAT_REFLECT; + break; + case CAIRO_EXTEND_PAD: + pixman_repeat = PIXMAN_REPEAT_PAD; + break; + } + + pixman_image_set_repeat (surface->pixman_image, pixman_repeat); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_image_surface_set_component_alpha (cairo_image_surface_t *surface, + cairo_bool_t ca) +{ + pixman_image_set_component_alpha (surface->pixman_image, ca); + return CAIRO_STATUS_SUCCESS; +} + static cairo_status_t _cairo_image_surface_set_attributes (cairo_image_surface_t *surface, cairo_surface_attributes_t *attributes, @@ -874,25 +975,19 @@ _cairo_image_surface_set_attributes (cairo_image_surface_t *surface, if (unlikely (status)) return status; - switch (attributes->extend) { - case CAIRO_EXTEND_NONE: - pixman_image_set_repeat (surface->pixman_image, PIXMAN_REPEAT_NONE); - break; - case CAIRO_EXTEND_REPEAT: - pixman_image_set_repeat (surface->pixman_image, PIXMAN_REPEAT_NORMAL); - break; - case CAIRO_EXTEND_REFLECT: - pixman_image_set_repeat (surface->pixman_image, PIXMAN_REPEAT_REFLECT); - break; - case CAIRO_EXTEND_PAD: - pixman_image_set_repeat (surface->pixman_image, PIXMAN_REPEAT_PAD); - break; - } - status = _cairo_image_surface_set_filter (surface, attributes->filter); if (unlikely (status)) return status; + status = _cairo_image_surface_set_extend (surface, attributes->extend); + if (unlikely (status)) + return status; + + status = _cairo_image_surface_set_component_alpha (surface, + attributes->has_component_alpha); + if (unlikely (status)) + return status; + return CAIRO_STATUS_SUCCESS; } @@ -936,34 +1031,93 @@ _pixman_operator (cairo_operator_t op) 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; } } +static cairo_status_t +_cairo_image_surface_set_clip_region (cairo_image_surface_t *surface, + cairo_region_t *region) +{ + if (region == surface->clip_region) + return CAIRO_STATUS_SUCCESS; + + if (cairo_region_equal (surface->clip_region, region)) + return CAIRO_STATUS_SUCCESS; + + cairo_region_destroy (surface->clip_region); + surface->clip_region = cairo_region_reference (region); + + if (! pixman_image_set_clip_region32 (surface->pixman_image, + region ? ®ion->rgn : NULL)) + { + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + return CAIRO_STATUS_SUCCESS; +} + static cairo_int_status_t -_cairo_image_surface_composite (cairo_operator_t op, +_cairo_image_surface_composite (cairo_operator_t op, const cairo_pattern_t *src_pattern, const cairo_pattern_t *mask_pattern, void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) + 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_region_t *clip_region) { - cairo_surface_attributes_t src_attr, mask_attr; + cairo_surface_attributes_t src_attr, mask_attr; cairo_image_surface_t *dst = abstract_dst; cairo_image_surface_t *src; cairo_image_surface_t *mask; - cairo_int_status_t status; + cairo_int_status_t status; + + status = _cairo_image_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + return status; status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern, &dst->base, - CAIRO_CONTENT_COLOR_ALPHA, src_x, src_y, mask_x, mask_y, width, height, @@ -980,8 +1134,7 @@ _cairo_image_surface_composite (cairo_operator_t op, if (unlikely (status)) goto CLEANUP_SURFACES; - if (mask) - { + if (mask) { status = _cairo_image_surface_set_attributes (mask, &mask_attr, dst_x + width / 2., dst_y + height / 2.); @@ -998,9 +1151,7 @@ _cairo_image_surface_composite (cairo_operator_t op, mask_y + mask_attr.y_offset, dst_x, dst_y, width, height); - } - else - { + } else { pixman_image_composite (_pixman_operator (op), src->pixman_image, NULL, @@ -1012,7 +1163,7 @@ _cairo_image_surface_composite (cairo_operator_t op, width, height); } - if (! _cairo_operator_bounded_by_source (op)) + if (! _cairo_operator_bounded_by_source (op)) { status = _cairo_surface_composite_fixup_unbounded (&dst->base, &src_attr, src->width, src->height, mask ? &mask_attr : NULL, @@ -1020,7 +1171,9 @@ _cairo_image_surface_composite (cairo_operator_t op, mask ? mask->height : 0, src_x, src_y, mask_x, mask_y, - dst_x, dst_y, width, height); + dst_x, dst_y, width, height, + clip_region); + } CLEANUP_SURFACES: if (mask) @@ -1045,7 +1198,7 @@ _cairo_image_surface_fill_rectangles (void *abstract_surface, pixman_rectangle16_t *pixman_rects = stack_rects; int i; - cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + cairo_int_status_t status; if (CAIRO_INJECT_FAULT ()) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -1055,6 +1208,9 @@ _cairo_image_surface_fill_rectangles (void *abstract_surface, pixman_color.blue = color->blue_short; pixman_color.alpha = color->alpha_short; + status = _cairo_image_surface_set_clip_region (surface, NULL); + assert (status == CAIRO_STATUS_SUCCESS); + if (num_rects > ARRAY_LENGTH (stack_rects)) { pixman_rects = _cairo_malloc_ab (num_rects, sizeof (pixman_rectangle16_t)); if (unlikely (pixman_rects == NULL)) @@ -1068,7 +1224,8 @@ _cairo_image_surface_fill_rectangles (void *abstract_surface, pixman_rects[i].height = rects[i].height; } - /* XXX: pixman_fill_rectangles() should be implemented */ + /* XXX: pixman_fill_region() should be implemented */ + status = CAIRO_STATUS_SUCCESS; if (! pixman_image_fill_rectangles (_pixman_operator (op), surface->pixman_image, &pixman_color, @@ -1132,7 +1289,8 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t op, unsigned int width, unsigned int height, cairo_trapezoid_t *traps, - int num_traps) + int num_traps, + cairo_region_t *clip_region) { cairo_surface_attributes_t attributes; cairo_image_surface_t *dst = abstract_dst; @@ -1153,24 +1311,27 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t op, * contained within the surface is bounded by [dst_x,dst_y,width,height]; * the Cairo core code passes bounds based on the trapezoid extents. * - * Currently the check surface->has_clip is needed for correct + * Currently the check clip_region == NULL is needed for correct * functioning, since pixman_add_trapezoids() doesn't obey the * surface clip, which is a libpixman bug , but there's no harm in * falling through to the general case when the surface is clipped * since libpixman would have to generate an intermediate mask anyways. */ if (op == CAIRO_OPERATOR_ADD && + clip_region == NULL && _cairo_pattern_is_opaque_solid (pattern) && dst->base.content == CAIRO_CONTENT_ALPHA && - ! dst->has_clip && antialias != CAIRO_ANTIALIAS_NONE) { _pixman_add_trapezoids (dst->pixman_image, 0, 0, traps, num_traps); return CAIRO_STATUS_SUCCESS; } + status = _cairo_image_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + return status; + status = _cairo_pattern_acquire_surface (pattern, &dst->base, - CAIRO_CONTENT_COLOR_ALPHA, src_x, src_y, width, height, CAIRO_PATTERN_ACQUIRE_NONE, (cairo_surface_t **) &src, @@ -1205,14 +1366,16 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t op, pixman_image_unref (mask); - if (! _cairo_operator_bounded_by_mask (op)) + if (! _cairo_operator_bounded_by_mask (op)) { status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base, &attributes, src->width, src->height, width, height, src_x, src_y, 0, 0, - dst_x, dst_y, width, height); + dst_x, dst_y, width, height, + clip_region); + } CLEANUP_SOURCE: _cairo_pattern_release_surface (pattern, &src->base, &attributes); @@ -1227,11 +1390,13 @@ typedef struct _cairo_image_surface_span_renderer { const cairo_pattern_t *pattern; cairo_antialias_t antialias; + uint8_t *mask_data; + uint32_t mask_stride; + 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; @@ -1240,66 +1405,46 @@ _cairo_image_surface_span_render_row ( int y, const cairo_half_open_span_t *spans, unsigned num_spans, - cairo_image_surface_t *mask, - const cairo_composite_rectangles_t *rects) + uint8_t *data, + uint32_t stride) { - int xmin = rects->mask.x; - int xmax = xmin + rects->width; uint8_t *row; - int prev_x = xmin; - int prev_alpha = 0; unsigned i; - /* Make sure we're within y-range. */ - y -= rects->mask.y; - if (y < 0 || y >= rects->height) + if (num_spans == 0) return; - row = (uint8_t*)(mask->data) + y*(size_t)mask->stride - xmin; + row = data + y * stride; + for (i = 0; i < num_spans - 1; i++) { + if (! spans[i].coverage) + continue; - /* 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); - } + /* We implement setting 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 (spans[i+1].x == spans[i].x + 1) { + row[spans[i].x] = spans[i].coverage; + } else { + memset (row + spans[i].x, + spans[i].coverage, + spans[i+1].x - spans[i].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); } } static cairo_status_t -_cairo_image_surface_span_renderer_render_row ( +_cairo_image_surface_span_renderer_render_rows ( void *abstract_renderer, int y, + int height, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_surface_span_renderer_t *renderer = abstract_renderer; - _cairo_image_surface_span_render_row (y, spans, num_spans, renderer->mask, &renderer->composite_rectangles); + while (height--) + _cairo_image_surface_span_render_row (y++, spans, num_spans, renderer->mask_data, renderer->mask_stride); return CAIRO_STATUS_SUCCESS; } @@ -1349,16 +1494,18 @@ _cairo_image_surface_span_renderer_finish (void *abstract_renderer) rects->dst.x, rects->dst.y, width, height); - if (! _cairo_operator_bounded_by_mask (renderer->op)) + 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, + width, height, rects->src.x, rects->src.y, 0, 0, /* mask.x, mask.y */ rects->dst.x, rects->dst.y, - rects->width, rects->height); + width, height, + dst->clip_region); + } } if (status != CAIRO_STATUS_SUCCESS) return _cairo_span_renderer_set_error (abstract_renderer, @@ -1370,15 +1517,13 @@ 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) + cairo_antialias_t antialias) { + return TRUE; (void) op; (void) pattern; (void) abstract_dst; (void) antialias; - (void) rects; - return TRUE; } static cairo_span_renderer_t * @@ -1386,22 +1531,25 @@ _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) + const cairo_composite_rectangles_t *rects, + cairo_region_t *clip_region) { cairo_image_surface_t *dst = abstract_dst; - cairo_image_surface_span_renderer_t *renderer - = calloc(1, sizeof(*renderer)); + cairo_image_surface_span_renderer_t *renderer = calloc(1, sizeof(*renderer)); cairo_status_t status; int width = rects->width; int height = rects->height; + status = _cairo_image_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + return _cairo_span_renderer_create_in_error (status); + 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->base.render_rows = _cairo_image_surface_span_renderer_render_rows; renderer->op = op; renderer->pattern = pattern; renderer->antialias = antialias; @@ -1411,7 +1559,6 @@ _cairo_image_surface_create_span_renderer (cairo_operator_t op, status = _cairo_pattern_acquire_surface ( renderer->pattern, &renderer->dst->base, - CAIRO_CONTENT_COLOR_ALPHA, rects->src.x, rects->src.y, width, height, CAIRO_PATTERN_ACQUIRE_NONE, @@ -1439,24 +1586,13 @@ _cairo_image_surface_create_span_renderer (cairo_operator_t op, _cairo_image_surface_span_renderer_destroy (renderer); return _cairo_span_renderer_create_in_error (status); } + + renderer->mask_data = renderer->mask->data - rects->mask.x - rects->mask.y * renderer->mask->stride; + renderer->mask_stride = renderer->mask->stride; return &renderer->base; } -cairo_int_status_t -_cairo_image_surface_set_clip_region (void *abstract_surface, - cairo_region_t *region) -{ - cairo_image_surface_t *surface = (cairo_image_surface_t *) abstract_surface; - - if (! pixman_image_set_clip_region32 (surface->pixman_image, region? ®ion->rgn : NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - surface->has_clip = region != NULL; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t +static cairo_bool_t _cairo_image_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { @@ -1467,7 +1603,7 @@ _cairo_image_surface_get_extents (void *abstract_surface, rectangle->width = surface->width; rectangle->height = surface->height; - return CAIRO_STATUS_SUCCESS; + return TRUE; } static void @@ -1479,18 +1615,6 @@ _cairo_image_surface_get_font_options (void *abstract_surface, cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); } -static cairo_status_t -_cairo_image_surface_reset (void *abstract_surface) -{ - cairo_image_surface_t *surface = abstract_surface; - cairo_status_t status; - - status = _cairo_image_surface_set_clip_region (surface, NULL); - assert (status == CAIRO_STATUS_SUCCESS); - - return CAIRO_STATUS_SUCCESS; -} - /** * _cairo_surface_is_image: * @surface: a #cairo_surface_t @@ -1521,13 +1645,11 @@ const cairo_surface_backend_t _cairo_image_surface_backend = { _cairo_image_surface_check_span_renderer, NULL, /* copy_page */ NULL, /* show_page */ - _cairo_image_surface_set_clip_region, - NULL, /* intersect_clip_path */ _cairo_image_surface_get_extents, NULL, /* old_show_glyphs */ _cairo_image_surface_get_font_options, NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ + NULL, /* mark dirty */ NULL, /* font_fini */ NULL, /* glyph_fini */ @@ -1538,8 +1660,6 @@ const cairo_surface_backend_t _cairo_image_surface_backend = { NULL, /* show_glyphs */ NULL, /* snapshot */ NULL, /* is_similar */ - - _cairo_image_surface_reset }; /* A convenience function for when one needs to coerce an image @@ -1567,7 +1687,8 @@ _cairo_image_surface_coerce (cairo_image_surface_t *surface, _cairo_pattern_init_for_surface (&pattern, &surface->base); status = _cairo_surface_paint (&clone->base, CAIRO_OPERATOR_SOURCE, - &pattern.base, NULL); + &pattern.base, + NULL); _cairo_pattern_fini (&pattern.base); if (unlikely (status)) { diff --git a/gfx/cairo/cairo/src/cairo-matrix.c b/gfx/cairo/cairo/src/cairo-matrix.c index b9e7290a63a4..bf20ee4fc124 100644 --- a/gfx/cairo/cairo/src/cairo-matrix.c +++ b/gfx/cairo/cairo/src/cairo-matrix.c @@ -371,37 +371,39 @@ _cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix, double min_x, max_x; double min_y, max_y; - if (_cairo_matrix_is_identity (matrix)) { - if (is_tight) - *is_tight = TRUE; - - return; - } - if (matrix->xy == 0. && matrix->yx == 0.) { /* non-rotation/skew matrix, just map the two extreme points */ - quad_x[0] = *x1; - quad_y[0] = *y1; - cairo_matrix_transform_distance (matrix, &quad_x[0], &quad_y[0]); - quad_x[1] = *x2; - quad_y[1] = *y2; - cairo_matrix_transform_distance (matrix, &quad_x[1], &quad_y[1]); - - if (quad_x[0] < quad_x[1]) { - *x1 = quad_x[0] + matrix->x0; - *x2 = quad_x[1] + matrix->x0; - } else { - *x1 = quad_x[1] + matrix->x0; - *x2 = quad_x[0] + matrix->x0; + if (matrix->xx != 1.) { + quad_x[0] = *x1 * matrix->xx; + quad_x[1] = *x2 * matrix->xx; + if (quad_x[0] < quad_x[1]) { + *x1 = quad_x[0]; + *x2 = quad_x[1]; + } else { + *x1 = quad_x[1]; + *x2 = quad_x[0]; + } + } + if (matrix->x0 != 0.) { + *x1 += matrix->x0; + *x2 += matrix->x0; } - if (quad_y[0] < quad_y[1]) { - *y1 = quad_y[0] + matrix->y0; - *y2 = quad_y[1] + matrix->y0; - } else { - *y1 = quad_y[1] + matrix->y0; - *y2 = quad_y[0] + matrix->y0; + if (matrix->yy != 1.) { + quad_y[0] = *y1 * matrix->yy; + quad_y[1] = *y2 * matrix->yy; + if (quad_y[0] < quad_y[1]) { + *y1 = quad_y[0]; + *y2 = quad_y[1]; + } else { + *y1 = quad_y[1]; + *y2 = quad_y[0]; + } + } + if (matrix->y0 != 0.) { + *y1 += matrix->y0; + *y2 += matrix->y0; } if (is_tight) @@ -624,7 +626,7 @@ _cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix, double major, minor; cairo_matrix_transform_distance (matrix, &x, &y); - major = sqrt(x*x + y*y); + major = hypot (x, y); /* * ignore mirroring */ @@ -688,16 +690,9 @@ _cairo_matrix_is_integer_translation (const cairo_matrix_t *matrix, return FALSE; } -/* By pixel exact here, we mean a matrix that is composed only of - * 90 degree rotations, flips, and integer translations and produces a 1:1 - * mapping between source and destination pixels. If we transform an image - * with a pixel-exact matrix, filtering is not useful. - */ -cairo_private cairo_bool_t -_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) +cairo_bool_t +_cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix) { - cairo_fixed_t x0_fixed, y0_fixed; - if (matrix->xy == 0.0 && matrix->yx == 0.0) { if (! (matrix->xx == 1.0 || matrix->xx == -1.0)) return FALSE; @@ -711,6 +706,22 @@ _cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) } else return FALSE; + return TRUE; +} + +/* By pixel exact here, we mean a matrix that is composed only of + * 90 degree rotations, flips, and integer translations and produces a 1:1 + * mapping between source and destination pixels. If we transform an image + * with a pixel-exact matrix, filtering is not useful. + */ +cairo_bool_t +_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) +{ + cairo_fixed_t x0_fixed, y0_fixed; + + if (! _cairo_matrix_has_unity_scale (matrix)) + return FALSE; + x0_fixed = _cairo_fixed_from_double (matrix->x0); y0_fixed = _cairo_fixed_from_double (matrix->y0); @@ -856,7 +867,7 @@ _cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix, g = 0.5 * (i - j); h = a*c + b*d; - return radius * sqrt (f + sqrt (g*g+h*h)); + return radius * sqrt (f + hypot (g, h)); /* * we don't need the minor axis length, which is diff --git a/gfx/cairo/cairo/src/cairo-meta-surface.c b/gfx/cairo/cairo/src/cairo-meta-surface.c deleted file mode 100644 index 4fb2722cdb74..000000000000 --- a/gfx/cairo/cairo/src/cairo-meta-surface.c +++ /dev/null @@ -1,1161 +0,0 @@ -/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2005 Red Hat, Inc - * Copyright © 2007 Adrian Johnson - * - * This library is free software; you can redistribute it and/or - * modify it either under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation - * (the "LGPL") or, at your option, under the terms of the Mozilla - * Public License Version 1.1 (the "MPL"). If you do not alter this - * notice, a recipient may use your version of this file under either - * the MPL or the LGPL. - * - * You should have received a copy of the LGPL along with this library - * in the file COPYING-LGPL-2.1; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * You should have received a copy of the MPL along with this library - * in the file COPYING-MPL-1.1 - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY - * OF ANY KIND, either express or implied. See the LGPL or the MPL for - * the specific language governing rights and limitations. - * - * The Original Code is the cairo graphics library. - * - * The Initial Developer of the Original Code is Red Hat, Inc. - * - * Contributor(s): - * Kristian Høgsberg - * Carl Worth - * Adrian Johnson - */ - -/* A meta surface is a surface that records all drawing operations at - * the highest level of the surface backend interface, (that is, the - * level of paint, mask, stroke, fill, and show_text_glyphs). The meta - * surface can then be "replayed" against any target surface with: - * - * - * cairo_meta_surface_replay (meta, target); - * - * - * after which the results in target will be identical to the results - * that would have been obtained if the original operations applied to - * the meta surface had instead been applied to the target surface. - * - * The recording phase of the meta surface is careful to snapshot all - * necessary objects (paths, patterns, etc.), in order to achieve - * accurate replay. The efficiency of the meta surface could be - * improved by improving the implementation of snapshot for the - * various objects. For example, it would be nice to have a - * copy-on-write implementation for _cairo_surface_snapshot. - */ - -#include "cairoint.h" -#include "cairo-analysis-surface-private.h" -#include "cairo-meta-surface-private.h" -#include "cairo-clip-private.h" - -typedef enum { - CAIRO_META_REPLAY, - CAIRO_META_CREATE_REGIONS -} cairo_meta_replay_type_t; - -static const cairo_surface_backend_t cairo_meta_surface_backend; - -/* Currently all meta surfaces do have a size which should be passed - * in as the maximum size of any target surface against which the - * meta-surface will ever be replayed. - * - * XXX: The naming of "pixels" in the size here is a misnomer. It's - * actually a size in whatever device-space units are desired (again, - * according to the intended replay target). - */ - -/** - * cairo_meta_surface_create: - * @content: the content of the meta surface - * @width_pixels: width of the surface, in pixels - * @height_pixels: height of the surface, in pixels - * - * Creates a meta-surface which can be used to record all drawing operations - * at the highest level (that is, the level of paint, mask, stroke, fill - * and show_text_glyphs). The meta surface can then be "replayed" against - * any target surface with: - * - * - * cairo_meta_surface_replay (meta, target); - * - * - * after which the results in target will be identical to the results - * that would have been obtained if the original operations applied to - * the meta surface had instead been applied to the target surface. - * - * The recording phase of the meta surface is careful to snapshot all - * necessary objects (paths, patterns, etc.), in order to achieve - * accurate replay. - * - * Since 1.10 - **/ -cairo_surface_t * -cairo_meta_surface_create (cairo_content_t content, - double width_pixels, - double height_pixels) -{ - cairo_meta_surface_t *meta; - - meta = malloc (sizeof (cairo_meta_surface_t)); - if (unlikely (meta == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_surface_init (&meta->base, &cairo_meta_surface_backend, - content); - - meta->content = content; - meta->width_pixels = width_pixels; - meta->height_pixels = height_pixels; - - /* unbounded -> 'infinite' extents */ - if (width_pixels < 0) { - meta->extents.x = CAIRO_RECT_INT_MIN; - meta->extents.width = CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN; - } else { - meta->extents.x = 0; - if (ceil (width_pixels) > CAIRO_RECT_INT_MAX) - meta->extents.width = CAIRO_RECT_INT_MAX; - else - meta->extents.width = ceil (width_pixels); - } - - if (height_pixels < 0) { - meta->extents.y = CAIRO_RECT_INT_MIN; - meta->extents.height = CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN; - } else { - meta->extents.y = 0; - if (ceil (height_pixels) > CAIRO_RECT_INT_MAX) - meta->extents.height = CAIRO_RECT_INT_MAX; - else - meta->extents.height = ceil (height_pixels); - } - - _cairo_array_init (&meta->commands, sizeof (cairo_command_t *)); - meta->commands_owner = NULL; - - meta->is_clipped = FALSE; - meta->replay_start_idx = 0; - - return &meta->base; -} -slim_hidden_def (cairo_meta_surface_create); - -static cairo_surface_t * -_cairo_meta_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - return cairo_meta_surface_create (content, width, height); -} - -static cairo_status_t -_cairo_meta_surface_finish (void *abstract_surface) -{ - cairo_meta_surface_t *meta = abstract_surface; - cairo_command_t *command; - cairo_command_t **elements; - int i, num_elements; - - if (meta->commands_owner) { - cairo_surface_destroy (meta->commands_owner); - return CAIRO_STATUS_SUCCESS; - } - - num_elements = meta->commands.num_elements; - elements = _cairo_array_index (&meta->commands, 0); - for (i = 0; i < num_elements; i++) { - command = elements[i]; - switch (command->header.type) { - - /* 5 basic drawing operations */ - - case CAIRO_COMMAND_PAINT: - _cairo_pattern_fini_snapshot (&command->paint.source.base); - free (command); - break; - - case CAIRO_COMMAND_MASK: - _cairo_pattern_fini_snapshot (&command->mask.source.base); - _cairo_pattern_fini_snapshot (&command->mask.mask.base); - free (command); - break; - - case CAIRO_COMMAND_STROKE: - _cairo_pattern_fini_snapshot (&command->stroke.source.base); - _cairo_path_fixed_fini (&command->stroke.path); - _cairo_stroke_style_fini (&command->stroke.style); - free (command); - break; - - case CAIRO_COMMAND_FILL: - _cairo_pattern_fini_snapshot (&command->fill.source.base); - _cairo_path_fixed_fini (&command->fill.path); - free (command); - break; - - case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: - _cairo_pattern_fini_snapshot (&command->show_text_glyphs.source.base); - free (command->show_text_glyphs.utf8); - free (command->show_text_glyphs.glyphs); - free (command->show_text_glyphs.clusters); - cairo_scaled_font_destroy (command->show_text_glyphs.scaled_font); - free (command); - break; - - /* Other junk. */ - case CAIRO_COMMAND_INTERSECT_CLIP_PATH: - if (command->intersect_clip_path.path_pointer) - _cairo_path_fixed_fini (&command->intersect_clip_path.path); - free (command); - break; - - default: - ASSERT_NOT_REACHED; - } - } - - _cairo_array_fini (&meta->commands); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_meta_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_status_t status; - cairo_meta_surface_t *surface = abstract_surface; - cairo_surface_t *image; - - image = _cairo_surface_has_snapshot (&surface->base, - &_cairo_image_surface_backend, - surface->content); - if (image != NULL) { - *image_out = (cairo_image_surface_t *) cairo_surface_reference (image); - *image_extra = NULL; - return CAIRO_STATUS_SUCCESS; - } - - image = _cairo_image_surface_create_with_content (surface->content, - ceil (surface->width_pixels), - ceil (surface->height_pixels)); - - status = cairo_meta_surface_replay (&surface->base, image); - if (unlikely (status)) { - cairo_surface_destroy (image); - return status; - } - - status = _cairo_surface_attach_snapshot (&surface->base, image, NULL); - if (unlikely (status)) { - cairo_surface_destroy (image); - return status; - } - - *image_out = (cairo_image_surface_t *) image; - *image_extra = NULL; - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_meta_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_surface_destroy (&image->base); -} - -static void -_draw_command_init (cairo_command_header_t *command, - cairo_command_type_t type, - cairo_meta_surface_t *meta) -{ - command->type = type; - command->region = CAIRO_META_REGION_ALL; - command->extents = meta->extents; -} - -static cairo_int_status_t -_cairo_meta_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_rectangle_int_t *extents) -{ - cairo_status_t status; - cairo_meta_surface_t *meta = abstract_surface; - cairo_command_paint_t *command; - - command = malloc (sizeof (cairo_command_paint_t)); - if (unlikely (command == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _draw_command_init (&command->header, CAIRO_COMMAND_PAINT, meta); - command->op = op; - - status = _cairo_pattern_init_snapshot (&command->source.base, source); - if (unlikely (status)) - goto CLEANUP_COMMAND; - - status = _cairo_array_append (&meta->commands, &command); - if (unlikely (status)) - goto CLEANUP_SOURCE; - - /* An optimisation that takes care to not replay what was done - * before surface is cleared. We don't erase recorded commands - * since we may have earlier snapshots of this surface. */ - if (op == CAIRO_OPERATOR_CLEAR && !meta->is_clipped) - meta->replay_start_idx = meta->commands.num_elements; - - return CAIRO_STATUS_SUCCESS; - - CLEANUP_SOURCE: - _cairo_pattern_fini_snapshot (&command->source.base); - CLEANUP_COMMAND: - free (command); - return status; -} - -static cairo_int_status_t -_cairo_meta_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_rectangle_int_t *extents) -{ - cairo_status_t status; - cairo_meta_surface_t *meta = abstract_surface; - cairo_command_mask_t *command; - - command = malloc (sizeof (cairo_command_mask_t)); - if (unlikely (command == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _draw_command_init (&command->header, CAIRO_COMMAND_MASK, meta); - command->op = op; - - status = _cairo_pattern_init_snapshot (&command->source.base, source); - if (unlikely (status)) - goto CLEANUP_COMMAND; - - status = _cairo_pattern_init_snapshot (&command->mask.base, mask); - if (unlikely (status)) - goto CLEANUP_SOURCE; - - status = _cairo_array_append (&meta->commands, &command); - if (unlikely (status)) - goto CLEANUP_MASK; - - return CAIRO_STATUS_SUCCESS; - - CLEANUP_MASK: - _cairo_pattern_fini_snapshot (&command->mask.base); - CLEANUP_SOURCE: - _cairo_pattern_fini_snapshot (&command->source.base); - CLEANUP_COMMAND: - free (command); - return status; -} - -static cairo_int_status_t -_cairo_meta_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_stroke_style_t *style, - cairo_matrix_t *ctm, - cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) -{ - cairo_status_t status; - cairo_meta_surface_t *meta = abstract_surface; - cairo_command_stroke_t *command; - - command = malloc (sizeof (cairo_command_stroke_t)); - if (unlikely (command == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _draw_command_init (&command->header, CAIRO_COMMAND_STROKE, meta); - command->op = op; - - status = _cairo_pattern_init_snapshot (&command->source.base, source); - if (unlikely (status)) - goto CLEANUP_COMMAND; - - status = _cairo_path_fixed_init_copy (&command->path, path); - if (unlikely (status)) - goto CLEANUP_SOURCE; - - status = _cairo_stroke_style_init_copy (&command->style, style); - if (unlikely (status)) - goto CLEANUP_PATH; - - command->ctm = *ctm; - command->ctm_inverse = *ctm_inverse; - command->tolerance = tolerance; - command->antialias = antialias; - - status = _cairo_array_append (&meta->commands, &command); - if (unlikely (status)) - goto CLEANUP_STYLE; - - return CAIRO_STATUS_SUCCESS; - - CLEANUP_STYLE: - _cairo_stroke_style_fini (&command->style); - CLEANUP_PATH: - _cairo_path_fixed_fini (&command->path); - CLEANUP_SOURCE: - _cairo_pattern_fini_snapshot (&command->source.base); - CLEANUP_COMMAND: - free (command); - return status; -} - -static cairo_int_status_t -_cairo_meta_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) -{ - cairo_status_t status; - cairo_meta_surface_t *meta = abstract_surface; - cairo_command_fill_t *command; - - command = malloc (sizeof (cairo_command_fill_t)); - if (unlikely (command == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _draw_command_init (&command->header, CAIRO_COMMAND_FILL, meta); - command->op = op; - - status = _cairo_pattern_init_snapshot (&command->source.base, source); - if (unlikely (status)) - goto CLEANUP_COMMAND; - - status = _cairo_path_fixed_init_copy (&command->path, path); - if (unlikely (status)) - goto CLEANUP_SOURCE; - - command->fill_rule = fill_rule; - command->tolerance = tolerance; - command->antialias = antialias; - - status = _cairo_array_append (&meta->commands, &command); - if (unlikely (status)) - goto CLEANUP_PATH; - - return CAIRO_STATUS_SUCCESS; - - CLEANUP_PATH: - _cairo_path_fixed_fini (&command->path); - CLEANUP_SOURCE: - _cairo_pattern_fini_snapshot (&command->source.base); - CLEANUP_COMMAND: - free (command); - return status; -} - -static cairo_bool_t -_cairo_meta_surface_has_show_text_glyphs (void *abstract_surface) -{ - return TRUE; -} - -static cairo_int_status_t -_cairo_meta_surface_show_text_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font, - cairo_rectangle_int_t *extents) -{ - cairo_status_t status; - cairo_meta_surface_t *meta = abstract_surface; - cairo_command_show_text_glyphs_t *command; - - command = malloc (sizeof (cairo_command_show_text_glyphs_t)); - if (unlikely (command == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _draw_command_init (&command->header, CAIRO_COMMAND_SHOW_TEXT_GLYPHS, meta); - command->op = op; - - status = _cairo_pattern_init_snapshot (&command->source.base, source); - if (unlikely (status)) - goto CLEANUP_COMMAND; - - command->utf8 = NULL; - command->utf8_len = utf8_len; - command->glyphs = NULL; - command->num_glyphs = num_glyphs; - command->clusters = NULL; - command->num_clusters = num_clusters; - - if (utf8_len) { - command->utf8 = malloc (utf8_len); - if (unlikely (command->utf8 == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_ARRAYS; - } - memcpy (command->utf8, utf8, utf8_len); - } - if (num_glyphs) { - command->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (glyphs[0])); - if (unlikely (command->glyphs == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_ARRAYS; - } - memcpy (command->glyphs, glyphs, sizeof (glyphs[0]) * num_glyphs); - } - if (num_clusters) { - command->clusters = _cairo_malloc_ab (num_clusters, sizeof (clusters[0])); - if (unlikely (command->clusters == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_ARRAYS; - } - memcpy (command->clusters, clusters, sizeof (clusters[0]) * num_clusters); - } - - command->cluster_flags = cluster_flags; - - command->scaled_font = cairo_scaled_font_reference (scaled_font); - - status = _cairo_array_append (&meta->commands, &command); - if (unlikely (status)) - goto CLEANUP_SCALED_FONT; - - return CAIRO_STATUS_SUCCESS; - - CLEANUP_SCALED_FONT: - cairo_scaled_font_destroy (command->scaled_font); - CLEANUP_ARRAYS: - free (command->utf8); - free (command->glyphs); - free (command->clusters); - - _cairo_pattern_fini_snapshot (&command->source.base); - CLEANUP_COMMAND: - free (command); - return status; -} - -/** - * _cairo_meta_surface_snapshot - * @surface: a #cairo_surface_t which must be a meta surface - * - * Make an immutable copy of @surface. It is an error to call a - * surface-modifying function on the result of this function. - * - * The caller owns the return value and should call - * cairo_surface_destroy() when finished with it. This function will not - * return %NULL, but will return a nil surface instead. - * - * Return value: The snapshot surface. - **/ -static cairo_surface_t * -_cairo_meta_surface_snapshot (void *abstract_other) -{ - cairo_meta_surface_t *other = abstract_other; - cairo_meta_surface_t *meta; - - meta = malloc (sizeof (cairo_meta_surface_t)); - if (unlikely (meta == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_surface_init (&meta->base, &cairo_meta_surface_backend, - other->base.content); - - meta->width_pixels = other->width_pixels; - meta->height_pixels = other->height_pixels; - meta->extents = other->extents; - meta->replay_start_idx = other->replay_start_idx; - meta->content = other->content; - - _cairo_array_init_snapshot (&meta->commands, &other->commands); - meta->commands_owner = cairo_surface_reference (&other->base); - - return &meta->base; -} - -static cairo_int_status_t -_cairo_meta_surface_intersect_clip_path (void *dst, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_meta_surface_t *meta = dst; - cairo_command_intersect_clip_path_t *command; - cairo_status_t status; - - command = malloc (sizeof (cairo_command_intersect_clip_path_t)); - if (unlikely (command == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - command->header.type = CAIRO_COMMAND_INTERSECT_CLIP_PATH; - command->header.region = CAIRO_META_REGION_ALL; - - if (path) { - status = _cairo_path_fixed_init_copy (&command->path, path); - if (unlikely (status)) { - free (command); - return status; - } - command->path_pointer = &command->path; - meta->is_clipped = TRUE; - } else { - command->path_pointer = NULL; - meta->is_clipped = FALSE; - } - command->fill_rule = fill_rule; - command->tolerance = tolerance; - command->antialias = antialias; - - status = _cairo_array_append (&meta->commands, &command); - if (unlikely (status)) { - if (path) - _cairo_path_fixed_fini (&command->path); - free (command); - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -/* Currently, we're using as the "size" of a meta surface the largest - * surface size against which the meta-surface is expected to be - * replayed, (as passed in to cairo_meta_surface_create()). - */ -static cairo_int_status_t -_cairo_meta_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_meta_surface_t *surface = abstract_surface; - - if (surface->width_pixels < 0 || surface->height_pixels < 0) - return CAIRO_INT_STATUS_UNSUPPORTED; - - rectangle->x = 0; - rectangle->y = 0; - rectangle->width = ceil (surface->width_pixels); - rectangle->height = ceil (surface->height_pixels); - - return CAIRO_STATUS_SUCCESS; -} - -/** - * _cairo_surface_is_meta: - * @surface: a #cairo_surface_t - * - * Checks if a surface is a #cairo_meta_surface_t - * - * Return value: %TRUE if the surface is a meta surface - **/ -cairo_bool_t -_cairo_surface_is_meta (const cairo_surface_t *surface) -{ - return surface->backend == &cairo_meta_surface_backend; -} - -static const cairo_surface_backend_t cairo_meta_surface_backend = { - CAIRO_SURFACE_TYPE_META, - _cairo_meta_surface_create_similar, - _cairo_meta_surface_finish, - _cairo_meta_surface_acquire_source_image, - _cairo_meta_surface_release_source_image, - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - NULL, /* set_clip_region */ - _cairo_meta_surface_intersect_clip_path, - _cairo_meta_surface_get_extents, - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - - /* Here are the 5 basic drawing operations, (which are in some - * sense the only things that cairo_meta_surface should need to - * implement). However, we implement the more generic show_text_glyphs - * instead of show_glyphs. One or the other is eough. */ - - _cairo_meta_surface_paint, - _cairo_meta_surface_mask, - _cairo_meta_surface_stroke, - _cairo_meta_surface_fill, - NULL, - - _cairo_meta_surface_snapshot, - - NULL, /* is_similar */ - NULL, /* reset */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - - _cairo_meta_surface_has_show_text_glyphs, - _cairo_meta_surface_show_text_glyphs -}; - -static cairo_path_fixed_t * -_cairo_command_get_path (cairo_command_t *command) -{ - switch (command->header.type) { - case CAIRO_COMMAND_PAINT: - case CAIRO_COMMAND_MASK: - case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: - return NULL; - case CAIRO_COMMAND_STROKE: - return &command->stroke.path; - case CAIRO_COMMAND_FILL: - return &command->fill.path; - case CAIRO_COMMAND_INTERSECT_CLIP_PATH: - return command->intersect_clip_path.path_pointer; - } - - ASSERT_NOT_REACHED; - return NULL; -} - -cairo_int_status_t -_cairo_meta_surface_get_path (cairo_surface_t *surface, - cairo_path_fixed_t *path) -{ - cairo_meta_surface_t *meta; - cairo_command_t *command, **elements; - int i, num_elements; - cairo_int_status_t status; - - if (surface->status) - return surface->status; - - meta = (cairo_meta_surface_t *) surface; - status = CAIRO_STATUS_SUCCESS; - - num_elements = meta->commands.num_elements; - elements = _cairo_array_index (&meta->commands, 0); - for (i = meta->replay_start_idx; i < num_elements; i++) { - command = elements[i]; - - switch (command->header.type) { - case CAIRO_COMMAND_PAINT: - case CAIRO_COMMAND_MASK: - case CAIRO_COMMAND_INTERSECT_CLIP_PATH: - status = CAIRO_INT_STATUS_UNSUPPORTED; - break; - - case CAIRO_COMMAND_STROKE: - { - cairo_traps_t traps; - - _cairo_traps_init (&traps); - - /* XXX call cairo_stroke_to_path() when that is implemented */ - status = _cairo_path_fixed_stroke_to_traps (&command->stroke.path, - &command->stroke.style, - &command->stroke.ctm, - &command->stroke.ctm_inverse, - command->stroke.tolerance, - &traps); - - if (status == CAIRO_STATUS_SUCCESS) - status = _cairo_traps_path (&traps, path); - - _cairo_traps_fini (&traps); - break; - } - case CAIRO_COMMAND_FILL: - { - status = _cairo_path_fixed_append (path, &command->fill.path, CAIRO_DIRECTION_FORWARD); - break; - } - case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: - { - status = _cairo_scaled_font_glyph_path (command->show_text_glyphs.scaled_font, - command->show_text_glyphs.glyphs, - command->show_text_glyphs.num_glyphs, - path); - break; - } - - default: - ASSERT_NOT_REACHED; - } - - if (unlikely (status)) - break; - } - - return _cairo_surface_set_error (surface, status); -} - -static cairo_status_t -_cairo_meta_surface_replay_internal (cairo_surface_t *surface, - cairo_surface_t *target, - cairo_meta_replay_type_t type, - cairo_meta_region_type_t region) -{ - cairo_meta_surface_t *meta; - cairo_command_t *command, **elements; - int i, num_elements; - cairo_int_status_t status, status2; - cairo_clip_t clip, *old_clip; - cairo_bool_t has_device_transform = _cairo_surface_has_device_transform (target); - cairo_matrix_t *device_transform = &target->device_transform; - cairo_path_fixed_t path_copy, *dev_path; - - if (surface->status) - return surface->status; - - if (target->status) - return _cairo_surface_set_error (surface, target->status); - - meta = (cairo_meta_surface_t *) surface; - status = CAIRO_STATUS_SUCCESS; - - _cairo_clip_init (&clip, target); - old_clip = _cairo_surface_get_clip (target); - - num_elements = meta->commands.num_elements; - elements = _cairo_array_index (&meta->commands, 0); - for (i = meta->replay_start_idx; i < num_elements; i++) { - command = elements[i]; - - if (type == CAIRO_META_REPLAY && region != CAIRO_META_REGION_ALL) { - if (command->header.region != region) - continue; - } - - /* For all commands except intersect_clip_path, we have to - * ensure the current clip gets set on the surface. */ - if (command->header.type != CAIRO_COMMAND_INTERSECT_CLIP_PATH) { - status = _cairo_surface_set_clip (target, &clip); - if (unlikely (status)) - break; - } - - dev_path = _cairo_command_get_path (command); - if (dev_path && has_device_transform) { - status = _cairo_path_fixed_init_copy (&path_copy, dev_path); - if (unlikely (status)) - break; - _cairo_path_fixed_transform (&path_copy, device_transform); - dev_path = &path_copy; - } - - switch (command->header.type) { - case CAIRO_COMMAND_PAINT: - status = _cairo_surface_paint (target, - command->paint.op, - &command->paint.source.base, &command->header.extents); - break; - case CAIRO_COMMAND_MASK: - status = _cairo_surface_mask (target, - command->mask.op, - &command->mask.source.base, - &command->mask.mask.base, &command->header.extents); - break; - case CAIRO_COMMAND_STROKE: - { - cairo_matrix_t dev_ctm = command->stroke.ctm; - cairo_matrix_t dev_ctm_inverse = command->stroke.ctm_inverse; - - if (has_device_transform) { - cairo_matrix_multiply (&dev_ctm, &dev_ctm, device_transform); - cairo_matrix_multiply (&dev_ctm_inverse, - &target->device_transform_inverse, - &dev_ctm_inverse); - } - - status = _cairo_surface_stroke (target, - command->stroke.op, - &command->stroke.source.base, - dev_path, - &command->stroke.style, - &dev_ctm, - &dev_ctm_inverse, - command->stroke.tolerance, - command->stroke.antialias, &command->header.extents); - break; - } - case CAIRO_COMMAND_FILL: - { - cairo_command_t *stroke_command; - - if (type != CAIRO_META_CREATE_REGIONS) - stroke_command = (i < num_elements - 1) ? elements[i + 1] : NULL; - else - stroke_command = NULL; - - if (stroke_command != NULL && - type == CAIRO_META_REPLAY && region != CAIRO_META_REGION_ALL) - { - if (stroke_command->header.region != region) - stroke_command = NULL; - } - if (stroke_command != NULL && - stroke_command->header.type == CAIRO_COMMAND_STROKE && - _cairo_path_fixed_is_equal (dev_path, _cairo_command_get_path (stroke_command))) { - cairo_matrix_t dev_ctm; - cairo_matrix_t dev_ctm_inverse; - - dev_ctm = stroke_command->stroke.ctm; - dev_ctm_inverse = stroke_command->stroke.ctm_inverse; - - if (has_device_transform) { - cairo_matrix_multiply (&dev_ctm, &dev_ctm, device_transform); - cairo_matrix_multiply (&dev_ctm_inverse, - &surface->device_transform_inverse, - &dev_ctm_inverse); - } - - status = _cairo_surface_fill_stroke (target, - command->fill.op, - &command->fill.source.base, - command->fill.fill_rule, - command->fill.tolerance, - command->fill.antialias, - dev_path, - stroke_command->stroke.op, - &stroke_command->stroke.source.base, - &stroke_command->stroke.style, - &dev_ctm, - &dev_ctm_inverse, - stroke_command->stroke.tolerance, - stroke_command->stroke.antialias, - &stroke_command->header.extents); - i++; - } else - status = _cairo_surface_fill (target, - command->fill.op, - &command->fill.source.base, - dev_path, - command->fill.fill_rule, - command->fill.tolerance, - command->fill.antialias, &command->header.extents); - break; - } - case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: - { - cairo_glyph_t *glyphs = command->show_text_glyphs.glyphs; - cairo_glyph_t *dev_glyphs; - int i, num_glyphs = command->show_text_glyphs.num_glyphs; - - /* show_text_glyphs is special because _cairo_surface_show_text_glyphs is allowed - * to modify the glyph array that's passed in. We must always - * copy the array before handing it to the backend. - */ - dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); - if (unlikely (dev_glyphs == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - break; - } - - if (has_device_transform) { - for (i = 0; i < num_glyphs; i++) { - dev_glyphs[i] = glyphs[i]; - cairo_matrix_transform_point (device_transform, - &dev_glyphs[i].x, - &dev_glyphs[i].y); - } - } else { - memcpy (dev_glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs); - } - - status = _cairo_surface_show_text_glyphs (target, - command->show_text_glyphs.op, - &command->show_text_glyphs.source.base, - command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len, - dev_glyphs, num_glyphs, - command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters, - command->show_text_glyphs.cluster_flags, - command->show_text_glyphs.scaled_font, &command->header.extents); - - free (dev_glyphs); - break; - } - case CAIRO_COMMAND_INTERSECT_CLIP_PATH: - /* XXX Meta surface clipping is broken and requires some - * cairo-gstate.c rewriting. Work around it for now. */ - if (dev_path == NULL) - _cairo_clip_reset (&clip); - else - status = _cairo_clip_clip (&clip, dev_path, - command->intersect_clip_path.fill_rule, - command->intersect_clip_path.tolerance, - command->intersect_clip_path.antialias, - target); - break; - default: - ASSERT_NOT_REACHED; - } - - if (dev_path == &path_copy) - _cairo_path_fixed_fini (&path_copy); - - if (type == CAIRO_META_CREATE_REGIONS) { - if (status == CAIRO_STATUS_SUCCESS) { - command->header.region = CAIRO_META_REGION_NATIVE; - } else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) { - command->header.region = CAIRO_META_REGION_IMAGE_FALLBACK; - status = CAIRO_STATUS_SUCCESS; - } - } - - if (unlikely (status)) - break; - } - - _cairo_clip_reset (&clip); - status2 = _cairo_surface_set_clip (target, old_clip); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - return _cairo_surface_set_error (surface, status); -} - -/** - * cairo_meta_surface_replay: - * @surface: the #cairo_meta_surface_t - * @target: a target #cairo_surface_t onto which to replay the operations - * @width_pixels: width of the surface, in pixels - * @height_pixels: height of the surface, in pixels - * - * A meta surface can be "replayed" against any target surface, - * after which the results in target will be identical to the results - * that would have been obtained if the original operations applied to - * the meta surface had instead been applied to the target surface. - * - * Since 1.10 - **/ -cairo_status_t -cairo_meta_surface_replay (cairo_surface_t *surface, - cairo_surface_t *target) -{ - return _cairo_meta_surface_replay_internal (surface, - target, - CAIRO_META_REPLAY, - CAIRO_META_REGION_ALL); -} -slim_hidden_def (cairo_meta_surface_replay); - -/* Replay meta to surface. When the return status of each operation is - * one of %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED, or - * %CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY the status of each operation - * will be stored in the meta surface. Any other status will abort the - * replay and return the status. - */ -cairo_status_t -_cairo_meta_surface_replay_and_create_regions (cairo_surface_t *surface, - cairo_surface_t *target) -{ - return _cairo_meta_surface_replay_internal (surface, - target, - CAIRO_META_CREATE_REGIONS, - CAIRO_META_REGION_ALL); -} - -cairo_status_t -_cairo_meta_surface_replay_region (cairo_surface_t *surface, - cairo_surface_t *target, - cairo_meta_region_type_t region) -{ - return _cairo_meta_surface_replay_internal (surface, - target, - CAIRO_META_REPLAY, - region); -} - -/** - * cairo_meta_surface_ink_extents: - * @surface: a #cairo_meta_surface_t - * @x0: the x-coordinate of the top-left of the ink bounding box - * @y0: the y-coordinate of the top-left of the ink bounding box - * @width: the width of the ink bounding box - * @height: the height of the ink bounding box - * - * Measures the extents of the operations stored within the meta-surface. - * This is useful to compute the required size of an image surface (or - * equivalent) into which to replay the full sequence of drawing operaitions. - * - * Since: 1.10 - **/ -void -cairo_meta_surface_ink_extents (cairo_surface_t *surface, - double *x0, - double *y0, - double *width, - double *height) -{ - cairo_surface_t *null_surface; - cairo_surface_t *analysis_surface; - cairo_status_t status; - cairo_box_t bbox; - - memset (&bbox, 0, sizeof (bbox)); - - if (! _cairo_surface_is_meta (surface)) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - goto DONE; - } - - null_surface = _cairo_null_surface_create (CAIRO_CONTENT_COLOR_ALPHA); - analysis_surface = _cairo_analysis_surface_create (null_surface, -1, -1); - cairo_surface_destroy (null_surface); - - status = analysis_surface->status; - if (unlikely (status)) - goto DONE; - - status = cairo_meta_surface_replay (surface, analysis_surface); - _cairo_analysis_surface_get_bounding_box (analysis_surface, &bbox); - cairo_surface_destroy (analysis_surface); - -DONE: - if (x0) - *x0 = _cairo_fixed_to_double (bbox.p1.x); - if (y0) - *y0 = _cairo_fixed_to_double (bbox.p1.y); - if (width) - *width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x); - if (height) - *height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y); -} diff --git a/gfx/cairo/cairo/src/cairo-misc.c b/gfx/cairo/cairo/src/cairo-misc.c index 8d9557e96c30..56c7d0b5e6a8 100644 --- a/gfx/cairo/cairo/src/cairo-misc.c +++ b/gfx/cairo/cairo/src/cairo-misc.c @@ -334,6 +334,21 @@ _cairo_operator_bounded_by_mask (cairo_operator_t op) case CAIRO_OPERATOR_XOR: case CAIRO_OPERATOR_ADD: case CAIRO_OPERATOR_SATURATE: + case CAIRO_OPERATOR_MULTIPLY: + case CAIRO_OPERATOR_SCREEN: + case CAIRO_OPERATOR_OVERLAY: + case CAIRO_OPERATOR_DARKEN: + case CAIRO_OPERATOR_LIGHTEN: + case CAIRO_OPERATOR_COLOR_DODGE: + case CAIRO_OPERATOR_COLOR_BURN: + case CAIRO_OPERATOR_HARD_LIGHT: + case CAIRO_OPERATOR_SOFT_LIGHT: + case CAIRO_OPERATOR_DIFFERENCE: + case CAIRO_OPERATOR_EXCLUSION: + case CAIRO_OPERATOR_HSL_HUE: + case CAIRO_OPERATOR_HSL_SATURATION: + case CAIRO_OPERATOR_HSL_COLOR: + case CAIRO_OPERATOR_HSL_LUMINOSITY: return TRUE; case CAIRO_OPERATOR_OUT: case CAIRO_OPERATOR_IN: @@ -372,6 +387,21 @@ _cairo_operator_bounded_by_source (cairo_operator_t op) case CAIRO_OPERATOR_XOR: case CAIRO_OPERATOR_ADD: case CAIRO_OPERATOR_SATURATE: + case CAIRO_OPERATOR_MULTIPLY: + case CAIRO_OPERATOR_SCREEN: + case CAIRO_OPERATOR_OVERLAY: + case CAIRO_OPERATOR_DARKEN: + case CAIRO_OPERATOR_LIGHTEN: + case CAIRO_OPERATOR_COLOR_DODGE: + case CAIRO_OPERATOR_COLOR_BURN: + case CAIRO_OPERATOR_HARD_LIGHT: + case CAIRO_OPERATOR_SOFT_LIGHT: + case CAIRO_OPERATOR_DIFFERENCE: + case CAIRO_OPERATOR_EXCLUSION: + case CAIRO_OPERATOR_HSL_HUE: + case CAIRO_OPERATOR_HSL_SATURATION: + case CAIRO_OPERATOR_HSL_COLOR: + case CAIRO_OPERATOR_HSL_LUMINOSITY: return TRUE; case CAIRO_OPERATOR_CLEAR: case CAIRO_OPERATOR_SOURCE: diff --git a/gfx/cairo/cairo/src/cairo-mutex-impl-private.h b/gfx/cairo/cairo/src/cairo-mutex-impl-private.h index a956b52155f9..06938b5a5f6e 100644 --- a/gfx/cairo/cairo/src/cairo-mutex-impl-private.h +++ b/gfx/cairo/cairo/src/cairo-mutex-impl-private.h @@ -168,30 +168,16 @@ # define CAIRO_MUTEX_IMPL_UNLOCK(mutex) CAIRO_MUTEX_IMPL_NOOP1(mutex) # define CAIRO_MUTEX_IMPL_NIL_INITIALIZER 0 -#elif HAVE_PTHREAD_H /*******************************************************/ +#elif defined(_WIN32) /******************************************************/ -# include - - typedef pthread_mutex_t cairo_mutex_impl_t; - -# define CAIRO_MUTEX_IMPL_PTHREAD 1 -#if HAVE_LOCKDEP -/* expose all mutexes to the validator */ -# define CAIRO_MUTEX_IMPL_INIT(mutex) pthread_mutex_init (&(mutex), NULL) +#define WIN32_LEAN_AND_MEAN +/* We require Windows 2000 features such as ETO_PDY */ +#if !defined(WINVER) || (WINVER < 0x0500) +# define WINVER 0x0500 #endif -# define CAIRO_MUTEX_IMPL_LOCK(mutex) pthread_mutex_lock (&(mutex)) -# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) pthread_mutex_unlock (&(mutex)) -#if HAVE_LOCKDEP -# define CAIRO_MUTEX_IS_LOCKED(mutex) LOCKDEP_IS_LOCKED (&(mutex)) -# define CAIRO_MUTEX_IS_UNLOCKED(mutex) LOCKDEP_IS_UNLOCKED (&(mutex)) +#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) +# define _WIN32_WINNT 0x0500 #endif -# define CAIRO_MUTEX_IMPL_FINI(mutex) pthread_mutex_destroy (&(mutex)) -#if ! HAVE_LOCKDEP -# define CAIRO_MUTEX_IMPL_FINALIZE() CAIRO_MUTEX_IMPL_NOOP -#endif -# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER PTHREAD_MUTEX_INITIALIZER - -#elif defined(HAVE_WINDOWS_H) || defined(_MSC_VER) /*************************/ # include @@ -230,6 +216,30 @@ # define CAIRO_MUTEX_IMPL_FINI(mutex) delete (mutex) # define CAIRO_MUTEX_IMPL_NIL_INITIALIZER NULL +#elif CAIRO_HAS_PTHREAD /* and finally if there are no native mutexes ********/ + +# include + + typedef pthread_mutex_t cairo_mutex_impl_t; + +# define CAIRO_MUTEX_IMPL_PTHREAD 1 +#if HAVE_LOCKDEP +/* expose all mutexes to the validator */ +# define CAIRO_MUTEX_IMPL_INIT(mutex) pthread_mutex_init (&(mutex), NULL) +#endif +# define CAIRO_MUTEX_IMPL_LOCK(mutex) pthread_mutex_lock (&(mutex)) +# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) pthread_mutex_unlock (&(mutex)) +#if HAVE_LOCKDEP +# define CAIRO_MUTEX_IS_LOCKED(mutex) LOCKDEP_IS_LOCKED (&(mutex)) +# define CAIRO_MUTEX_IS_UNLOCKED(mutex) LOCKDEP_IS_UNLOCKED (&(mutex)) +#endif +# define CAIRO_MUTEX_IMPL_FINI(mutex) pthread_mutex_destroy (&(mutex)) +#if ! HAVE_LOCKDEP +# define CAIRO_MUTEX_IMPL_FINALIZE() CAIRO_MUTEX_IMPL_NOOP +#endif +# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER PTHREAD_MUTEX_INITIALIZER + + #else /**********************************************************************/ # error "XXX: No mutex implementation found. Cairo will not work with multiple threads. Define CAIRO_NO_MUTEX to 1 to acknowledge and accept this limitation and compile cairo without thread-safety support." diff --git a/gfx/cairo/cairo/src/cairo-mutex-list-private.h b/gfx/cairo/cairo/src/cairo-mutex-list-private.h index a807d7a22f02..2f483163a7de 100644 --- a/gfx/cairo/cairo/src/cairo-mutex-list-private.h +++ b/gfx/cairo/cairo/src/cairo-mutex-list-private.h @@ -52,9 +52,16 @@ CAIRO_MUTEX_DECLARE (_cairo_ft_unscaled_font_map_mutex) CAIRO_MUTEX_DECLARE (_cairo_xlib_display_mutex) #endif +#if CAIRO_HAS_GL_SURFACE +CAIRO_MUTEX_DECLARE (_cairo_gl_context_mutex) +#endif + #if !defined (HAS_ATOMIC_OPS) || defined (ATOMIC_OP_NEEDS_MEMORY_BARRIER) CAIRO_MUTEX_DECLARE (_cairo_atomic_mutex) #endif +#if CAIRO_HAS_DRM_SURFACE +CAIRO_MUTEX_DECLARE (_cairo_drm_device_mutex) +#endif /* Undefine, to err on unintended inclusion */ #undef CAIRO_MUTEX_DECLARE diff --git a/gfx/cairo/cairo/src/cairo-no-features.h b/gfx/cairo/cairo/src/cairo-no-features.h new file mode 100644 index 000000000000..9b3d86be2637 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-no-features.h @@ -0,0 +1,12 @@ +/* Generated by configure. Do not edit */ +#ifndef CAIRO_NO_FEATURES_H +#define CAIRO_NO_FEATURES_H + +#include + +/* This is a dummy header, to trick gtk-doc only */ + +#define CAIRO_HAS_WIN32_FONT 1 +#define CAIRO_HAS_WIN32_SURFACE 1 + +#endif diff --git a/gfx/cairo/cairo/src/cairo-os2-surface.c b/gfx/cairo/cairo/src/cairo-os2-surface.c index 82bab3bea0f5..3fda0b4f66be 100644 --- a/gfx/cairo/cairo/src/cairo-os2-surface.c +++ b/gfx/cairo/cairo/src/cairo-os2-surface.c @@ -716,26 +716,18 @@ _cairo_os2_surface_release_dest_image (void *abstract_surface DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); } -static cairo_int_status_t +static cairo_bool_t _cairo_os2_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_os2_surface_t *local_os2_surface; - local_os2_surface = (cairo_os2_surface_t *) abstract_surface; - if ((!local_os2_surface) || - (local_os2_surface->base.backend != &cairo_os2_surface_backend)) - { - /* Invalid parameter (wrong surface)! */ - return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - } - rectangle->x = 0; rectangle->y = 0; rectangle->width = local_os2_surface->bitmap_info.cx; rectangle->height = local_os2_surface->bitmap_info.cy; - return CAIRO_STATUS_SUCCESS; + return TRUE; } /** @@ -1327,8 +1319,6 @@ static const cairo_surface_backend_t cairo_os2_surface_backend = { NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ - NULL, /* set_clip_region */ - NULL, /* intersect_clip_path */ _cairo_os2_surface_get_extents, NULL, /* old_show_glyphs */ NULL, /* get_font_options */ diff --git a/gfx/cairo/cairo/src/cairo-output-stream-private.h b/gfx/cairo/cairo/src/cairo-output-stream-private.h index 2b3d584ecedc..5243d21806b5 100644 --- a/gfx/cairo/cairo/src/cairo-output-stream-private.h +++ b/gfx/cairo/cairo/src/cairo-output-stream-private.h @@ -184,6 +184,10 @@ _cairo_null_stream_create (void); cairo_private cairo_output_stream_t * _cairo_base85_stream_create (cairo_output_stream_t *output); +/* cairo-base64-stream.c */ +cairo_private cairo_output_stream_t * +_cairo_base64_stream_create (cairo_output_stream_t *output); + /* cairo-deflate-stream.c */ cairo_private cairo_output_stream_t * _cairo_deflate_stream_create (cairo_output_stream_t *output); diff --git a/gfx/cairo/cairo/src/cairo-output-stream.c b/gfx/cairo/cairo/src/cairo-output-stream.c index 71154339ad4b..20f676c9a0e0 100644 --- a/gfx/cairo/cairo/src/cairo-output-stream.c +++ b/gfx/cairo/cairo/src/cairo-output-stream.c @@ -41,7 +41,6 @@ #include #include -#include #include /* Numbers printed with %f are printed with this number of significant @@ -341,7 +340,7 @@ _cairo_dtostr (char *buffer, size_t size, double d, cairo_bool_t limited_precisi if (*p == '+' || *p == '-') p++; - while (isdigit (*p)) + while (_cairo_isdigit (*p)) p++; if (strncmp (p, decimal_point, decimal_point_len) == 0) @@ -362,7 +361,7 @@ _cairo_dtostr (char *buffer, size_t size, double d, cairo_bool_t limited_precisi if (*p == '+' || *p == '-') p++; - while (isdigit (*p)) + while (_cairo_isdigit (*p)) p++; if (strncmp (p, decimal_point, decimal_point_len) == 0) { @@ -434,7 +433,7 @@ _cairo_output_stream_vprintf (cairo_output_stream_t *stream, f++; } - while (isdigit (*f)) + while (_cairo_isdigit (*f)) f++; length_modifier = 0; diff --git a/gfx/cairo/cairo/src/cairo-paginated-private.h b/gfx/cairo/cairo/src/cairo-paginated-private.h index 5cb2e48b6c41..33358b98ea33 100644 --- a/gfx/cairo/cairo/src/cairo-paginated-private.h +++ b/gfx/cairo/cairo/src/cairo-paginated-private.h @@ -98,7 +98,7 @@ struct _cairo_paginated_surface_backend { * to follow. * * What the paginated surface does is first save all drawing - * operations for a page into a meta-surface. Then when the user calls + * operations for a page into a recording-surface. Then when the user calls * cairo_show_page(), the paginated surface performs the following * sequence of operations (using the backend functions passed to * cairo_paginated_surface_create()): @@ -109,7 +109,7 @@ struct _cairo_paginated_surface_backend { * * 2. Calls set_paginated_mode() with an argument of %CAIRO_PAGINATED_MODE_ANALYZE * - * 3. Replays the meta-surface to the target surface, (with an + * 3. Replays the recording-surface to the target surface, (with an * analysis surface inserted between which watches the return value * from each operation). This analysis stage is used to decide which * operations will require fallbacks. @@ -119,7 +119,7 @@ struct _cairo_paginated_surface_backend { * * 5. Calls set_paginated_mode() with an argument of %CAIRO_PAGINATED_MODE_RENDER * - * 6. Replays a subset of the meta-surface operations to the target surface + * 6. Replays a subset of the recording-surface operations to the target surface * * 7. Calls set_paginated_mode() with an argument of %CAIRO_PAGINATED_MODE_FALLBACK * @@ -149,8 +149,6 @@ struct _cairo_paginated_surface_backend { cairo_private cairo_surface_t * _cairo_paginated_surface_create (cairo_surface_t *target, cairo_content_t content, - int width, - int height, const cairo_paginated_surface_backend_t *backend); cairo_private cairo_surface_t * diff --git a/gfx/cairo/cairo/src/cairo-paginated-surface-private.h b/gfx/cairo/cairo/src/cairo-paginated-surface-private.h index b406cac15002..a494e581d164 100644 --- a/gfx/cairo/cairo/src/cairo-paginated-surface-private.h +++ b/gfx/cairo/cairo/src/cairo-paginated-surface-private.h @@ -48,25 +48,16 @@ typedef struct _cairo_paginated_surface { cairo_content_t content; - /* XXX: These shouldn't actually exist. We inherit this ugliness - * from _cairo_meta_surface_create. The width/height parameters - * from that function also should not exist. The fix that will - * allow us to remove all of these is to fix acquire_source_image - * to pass an interest rectangle. */ - int width; - int height; - /* Paginated-surface specific functions for the target */ const cairo_paginated_surface_backend_t *backend; - /* A cairo_meta_surface to record all operations. To be replayed + /* A cairo_recording_surface to record all operations. To be replayed * against target, and also against image surface as necessary for * fallbacks. */ - cairo_surface_t *meta; + cairo_surface_t *recording_surface; int page_num; cairo_bool_t page_is_blank; - } cairo_paginated_surface_t; #endif /* CAIRO_PAGINATED_SURFACE_H */ diff --git a/gfx/cairo/cairo/src/cairo-paginated-surface.c b/gfx/cairo/cairo/src/cairo-paginated-surface.c index 033df35afbad..dff5e8313502 100644 --- a/gfx/cairo/cairo/src/cairo-paginated-surface.c +++ b/gfx/cairo/cairo/src/cairo-paginated-surface.c @@ -46,7 +46,7 @@ #include "cairo-paginated-private.h" #include "cairo-paginated-surface-private.h" -#include "cairo-meta-surface-private.h" +#include "cairo-recording-surface-private.h" #include "cairo-analysis-surface-private.h" static const cairo_surface_backend_t cairo_paginated_surface_backend; @@ -60,17 +60,36 @@ _cairo_paginated_surface_create_similar (void *abstract_surface, int width, int height) { - cairo_paginated_surface_t *surface = abstract_surface; - return cairo_surface_create_similar (surface->target, content, - width, height); + cairo_rectangle_t rect; + rect.x = rect.y = 0.; + rect.width = width; + rect.height = height; + return cairo_recording_surface_create (content, &rect); +} + +static cairo_surface_t * +_create_recording_surface_for_target (cairo_surface_t *target, + cairo_content_t content) +{ + cairo_rectangle_int_t rect; + + if (_cairo_surface_get_extents (target, &rect)) { + cairo_rectangle_t recording_extents; + + recording_extents.x = rect.x; + recording_extents.y = rect.y; + recording_extents.width = rect.width; + recording_extents.height = rect.height; + + return cairo_recording_surface_create (content, &recording_extents); + } else { + return cairo_recording_surface_create (content, NULL); + } } -/* XXX The integer width,height here should be doubles and all uses updated */ cairo_surface_t * _cairo_paginated_surface_create (cairo_surface_t *target, cairo_content_t content, - int width, - int height, const cairo_paginated_surface_backend_t *backend) { cairo_paginated_surface_t *surface; @@ -87,18 +106,15 @@ _cairo_paginated_surface_create (cairo_surface_t *target, /* Override surface->base.type with target's type so we don't leak * evidence of the paginated wrapper out to the user. */ - surface->base.type = cairo_surface_get_type (target); + surface->base.type = target->type; surface->target = cairo_surface_reference (target); surface->content = content; - surface->width = width; - surface->height = height; - surface->backend = backend; - surface->meta = cairo_meta_surface_create (content, width, height); - status = cairo_surface_status (surface->meta); + surface->recording_surface = _create_recording_surface_for_target (target, content); + status = surface->recording_surface->status; if (unlikely (status)) goto FAIL_CLEANUP_SURFACE; @@ -132,31 +148,6 @@ _cairo_paginated_surface_get_target (cairo_surface_t *surface) return paginated_surface->target; } -cairo_status_t -_cairo_paginated_surface_set_size (cairo_surface_t *surface, - int width, - int height) -{ - cairo_paginated_surface_t *paginated_surface; - cairo_status_t status; - - assert (_cairo_surface_is_paginated (surface)); - - paginated_surface = (cairo_paginated_surface_t *) surface; - - paginated_surface->width = width; - paginated_surface->height = height; - - cairo_surface_destroy (paginated_surface->meta); - paginated_surface->meta = cairo_meta_surface_create (paginated_surface->content, - width, height); - status = cairo_surface_status (paginated_surface->meta); - if (unlikely (status)) - return _cairo_surface_set_error (surface, status); - - return CAIRO_STATUS_SUCCESS; -} - static cairo_status_t _cairo_paginated_surface_finish (void *abstract_surface) { @@ -168,19 +159,22 @@ _cairo_paginated_surface_finish (void *abstract_surface) status = cairo_surface_status (abstract_surface); } - if (status == CAIRO_STATUS_SUCCESS) { + /* XXX We want to propagate any errors from destroy(), but those are not + * returned via the api. So we need to explicitly finish the target, + * and check the status afterwards. However, we can only call finish() + * on the target, if we own it. + */ + if (CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->target->ref_count) == 1) { cairo_surface_finish (surface->target); - status = cairo_surface_status (surface->target); + if (status == CAIRO_STATUS_SUCCESS) + status = cairo_surface_status (surface->target); } - - if (status == CAIRO_STATUS_SUCCESS) { - cairo_surface_finish (surface->meta); - status = cairo_surface_status (surface->meta); - } - cairo_surface_destroy (surface->target); - cairo_surface_destroy (surface->meta); + cairo_surface_finish (surface->recording_surface); + if (status == CAIRO_STATUS_SUCCESS) + status = cairo_surface_status (surface->recording_surface); + cairo_surface_destroy (surface->recording_surface); return status; } @@ -210,19 +204,20 @@ _cairo_paginated_surface_acquire_source_image (void *abstract_surface, void **image_extra) { cairo_paginated_surface_t *surface = abstract_surface; + cairo_bool_t is_bounded; cairo_surface_t *image; cairo_status_t status; cairo_rectangle_int_t extents; - status = _cairo_surface_get_extents (surface->target, &extents); - if (unlikely (status)) - return status; + is_bounded = _cairo_surface_get_extents (surface->target, &extents); + if (! is_bounded) + return CAIRO_INT_STATUS_UNSUPPORTED; image = _cairo_paginated_surface_create_image_surface (surface, extents.width, extents.height); - status = cairo_meta_surface_replay (surface->meta, image); + status = _cairo_recording_surface_replay (surface->recording_surface, image); if (unlikely (status)) { cairo_surface_destroy (image); return status; @@ -248,11 +243,11 @@ _paint_fallback_image (cairo_paginated_surface_t *surface, { double x_scale = surface->base.x_fallback_resolution / surface->target->x_resolution; double y_scale = surface->base.y_fallback_resolution / surface->target->y_resolution; - cairo_matrix_t matrix; int x, y, width, height; cairo_status_t status; cairo_surface_t *image; cairo_surface_pattern_t pattern; + cairo_clip_t clip; x = rect->x; y = rect->y; @@ -266,20 +261,26 @@ _paint_fallback_image (cairo_paginated_surface_t *surface, * so we have to do the scaling manually. */ cairo_surface_set_device_offset (image, -x*x_scale, -y*y_scale); - status = cairo_meta_surface_replay (surface->meta, image); + status = _cairo_recording_surface_replay (surface->recording_surface, image); if (unlikely (status)) goto CLEANUP_IMAGE; _cairo_pattern_init_for_surface (&pattern, image); - cairo_matrix_init (&matrix, x_scale, 0, 0, y_scale, -x*x_scale, -y*y_scale); - cairo_pattern_set_matrix (&pattern.base, &matrix); + cairo_matrix_init (&pattern.base.matrix, + x_scale, 0, 0, y_scale, -x*x_scale, -y*y_scale); /* the fallback should be rendered at native resolution, so disable * filtering (if possible) to avoid introducing potential artifacts. */ pattern.base.filter = CAIRO_FILTER_NEAREST; + status = _cairo_clip_init_rectangle (&clip, rect); + if (unlikely (status)) + goto CLEANUP_IMAGE; + status = _cairo_surface_paint (surface->target, CAIRO_OPERATOR_SOURCE, - &pattern.base, NULL); + &pattern.base, &clip); + + _cairo_clip_reset (&clip); _cairo_pattern_fini (&pattern.base); CLEANUP_IMAGE: @@ -295,18 +296,17 @@ _paint_page (cairo_paginated_surface_t *surface) cairo_status_t status; cairo_bool_t has_supported, has_page_fallback, has_finegrained_fallback; - if (surface->target->status) + if (unlikely (surface->target->status)) return surface->target->status; - analysis = _cairo_analysis_surface_create (surface->target, - surface->width, surface->height); - if (analysis->status) + analysis = _cairo_analysis_surface_create (surface->target); + if (unlikely (analysis->status)) return _cairo_surface_set_error (surface->target, analysis->status); surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_ANALYZE); - status = _cairo_meta_surface_replay_and_create_regions (surface->meta, - analysis); + status = _cairo_recording_surface_replay_and_create_regions (surface->recording_surface, + analysis); if (status || analysis->status) { if (status == CAIRO_STATUS_SUCCESS) status = analysis->status; @@ -356,25 +356,28 @@ _paint_page (cairo_paginated_surface_t *surface) surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_RENDER); - status = _cairo_meta_surface_replay_region (surface->meta, - surface->target, - CAIRO_META_REGION_NATIVE); + status = _cairo_recording_surface_replay_region (surface->recording_surface, + surface->target, + CAIRO_RECORDING_REGION_NATIVE); assert (status != CAIRO_INT_STATUS_UNSUPPORTED); if (unlikely (status)) goto FAIL; } if (has_page_fallback) { - cairo_rectangle_int_t rect; + cairo_rectangle_int_t extents; + cairo_bool_t is_bounded; surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_FALLBACK); - rect.x = 0; - rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - status = _paint_fallback_image (surface, &rect); + is_bounded = _cairo_surface_get_extents (surface->target, &extents); + if (! is_bounded) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto FAIL; + } + + status = _paint_fallback_image (surface, &extents); if (unlikely (status)) goto FAIL; } @@ -386,15 +389,6 @@ _paint_page (cairo_paginated_surface_t *surface) surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_FALLBACK); - /* Reset clip region before drawing the fall back images */ - status = _cairo_surface_intersect_clip_path (surface->target, - NULL, - CAIRO_FILL_RULE_WINDING, - CAIRO_GSTATE_TOLERANCE_DEFAULT, - CAIRO_ANTIALIAS_DEFAULT); - if (unlikely (status)) - goto FAIL; - region = _cairo_analysis_surface_get_unsupported (analysis); num_rects = cairo_region_num_rectangles (region); @@ -402,9 +396,7 @@ _paint_page (cairo_paginated_surface_t *surface) cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, i, &rect); - status = _paint_fallback_image (surface, &rect); - if (unlikely (status)) goto FAIL; } @@ -445,12 +437,12 @@ _cairo_paginated_surface_copy_page (void *abstract_surface) surface->page_num++; - /* XXX: It might make sense to add some suport here for calling + /* XXX: It might make sense to add some support here for calling * cairo_surface_copy_page on the target surface. It would be an * optimization for the output, but the interaction with image * fallbacks gets tricky. For now, we just let the target see a * show_page and we implement the copying by simply not destroying - * the meta-surface. */ + * the recording-surface. */ cairo_surface_show_page (surface->target); return cairo_surface_status (surface->target); @@ -471,20 +463,19 @@ _cairo_paginated_surface_show_page (void *abstract_surface) return status; cairo_surface_show_page (surface->target); - status = cairo_surface_status (surface->target); + status = surface->target->status; if (unlikely (status)) return status; - status = cairo_surface_status (surface->meta); + status = surface->recording_surface->status; if (unlikely (status)) return status; - cairo_surface_destroy (surface->meta); + cairo_surface_destroy (surface->recording_surface); - surface->meta = cairo_meta_surface_create (surface->content, - surface->width, - surface->height); - status = cairo_surface_status (surface->meta); + surface->recording_surface = _create_recording_surface_for_target (surface->target, + surface->content); + status = surface->recording_surface->status; if (unlikely (status)) return status; @@ -494,21 +485,7 @@ _cairo_paginated_surface_show_page (void *abstract_surface) return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_paginated_surface_intersect_clip_path (void *abstract_surface, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_paginated_surface_t *surface = abstract_surface; - - return _cairo_surface_intersect_clip_path (surface->meta, - path, fill_rule, - tolerance, antialias); -} - -static cairo_int_status_t +static cairo_bool_t _cairo_paginated_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { @@ -530,7 +507,7 @@ static cairo_int_status_t _cairo_paginated_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_paginated_surface_t *surface = abstract_surface; @@ -540,7 +517,7 @@ _cairo_paginated_surface_paint (void *abstract_surface, surface->page_is_blank = FALSE; - return _cairo_surface_paint (surface->meta, op, source, NULL); + return _cairo_surface_paint (surface->recording_surface, op, source, clip); } static cairo_int_status_t @@ -548,11 +525,17 @@ _cairo_paginated_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_paginated_surface_t *surface = abstract_surface; - return _cairo_surface_mask (surface->meta, op, source, mask, NULL); + /* Optimize away erasing of nothing. */ + if (surface->page_is_blank && op == CAIRO_OPERATOR_CLEAR) + return CAIRO_STATUS_SUCCESS; + + surface->page_is_blank = FALSE; + + return _cairo_surface_mask (surface->recording_surface, op, source, mask, clip); } static cairo_int_status_t @@ -565,7 +548,7 @@ _cairo_paginated_surface_stroke (void *abstract_surface, cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_paginated_surface_t *surface = abstract_surface; @@ -575,10 +558,11 @@ _cairo_paginated_surface_stroke (void *abstract_surface, surface->page_is_blank = FALSE; - return _cairo_surface_stroke (surface->meta, op, source, + return _cairo_surface_stroke (surface->recording_surface, op, source, path, style, ctm, ctm_inverse, - tolerance, antialias, NULL); + tolerance, antialias, + clip); } static cairo_int_status_t @@ -589,7 +573,7 @@ _cairo_paginated_surface_fill (void *abstract_surface, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_paginated_surface_t *surface = abstract_surface; @@ -599,9 +583,10 @@ _cairo_paginated_surface_fill (void *abstract_surface, surface->page_is_blank = FALSE; - return _cairo_surface_fill (surface->meta, op, source, + return _cairo_surface_fill (surface->recording_surface, op, source, path, fill_rule, - tolerance, antialias, NULL); + tolerance, antialias, + clip); } static cairo_bool_t @@ -613,21 +598,20 @@ _cairo_paginated_surface_has_show_text_glyphs (void *abstract_surface) } static cairo_int_status_t -_cairo_paginated_surface_show_text_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font, - cairo_rectangle_int_t *extents) +_cairo_paginated_surface_show_text_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip) { cairo_paginated_surface_t *surface = abstract_surface; - cairo_int_status_t status; /* Optimize away erasing of nothing. */ if (surface->page_is_blank && op == CAIRO_OPERATOR_CLEAR) @@ -635,24 +619,13 @@ _cairo_paginated_surface_show_text_glyphs (void *abstract_surface, surface->page_is_blank = FALSE; - /* Since this is a "wrapping" surface, we're calling back into - * _cairo_surface_show_text_glyphs from within a call to the same. - * Since _cairo_surface_show_text_glyphs acquires a mutex, we release - * and re-acquire the mutex around this nested call. - * - * Yes, this is ugly, but we consider it pragmatic as compared to - * adding locking code to all 18 surface-backend-specific - * show_glyphs functions, (which would get less testing and likely - * lead to bugs). - */ - status = _cairo_surface_show_text_glyphs (surface->meta, op, source, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, - cluster_flags, - scaled_font, NULL); - - return status; + return _cairo_surface_show_text_glyphs (surface->recording_surface, op, source, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + cluster_flags, + scaled_font, + clip); } static cairo_surface_t * @@ -660,7 +633,7 @@ _cairo_paginated_surface_snapshot (void *abstract_other) { cairo_paginated_surface_t *other = abstract_other; - return _cairo_surface_snapshot (other->meta); + return _cairo_surface_snapshot (other->recording_surface); } static const cairo_surface_backend_t cairo_paginated_surface_backend = { @@ -679,8 +652,6 @@ static const cairo_surface_backend_t cairo_paginated_surface_backend = { NULL, /* check_span_renderer */ _cairo_paginated_surface_copy_page, _cairo_paginated_surface_show_page, - NULL, /* set_clip_region */ - _cairo_paginated_surface_intersect_clip_path, _cairo_paginated_surface_get_extents, NULL, /* old_show_glyphs */ _cairo_paginated_surface_get_font_options, @@ -695,7 +666,6 @@ static const cairo_surface_backend_t cairo_paginated_surface_backend = { NULL, /* show_glyphs */ _cairo_paginated_surface_snapshot, NULL, /* is_similar */ - NULL, /* reset */ NULL, /* fill_stroke */ NULL, /* create_solid_pattern_surface */ NULL, /* can_repaint_solid_pattern_surface */ diff --git a/gfx/cairo/cairo/src/cairo-path-bounds.c b/gfx/cairo/cairo/src/cairo-path-bounds.c index 1fadd84dd892..d3bc449a984d 100644 --- a/gfx/cairo/cairo/src/cairo-path-bounds.c +++ b/gfx/cairo/cairo/src/cairo-path-bounds.c @@ -51,13 +51,6 @@ _cairo_path_bounder_init (cairo_path_bounder_t *bounder) bounder->has_point = FALSE; } -static void -_cairo_path_bounder_fini (cairo_path_bounder_t *bounder) -{ - bounder->has_initial_point = FALSE; - bounder->has_point = FALSE; -} - static void _cairo_path_bounder_add_point (cairo_path_bounder_t *bounder, const cairo_point_t *point) @@ -79,7 +72,6 @@ _cairo_path_bounder_add_point (cairo_path_bounder_t *bounder, bounder->extents.p1.y = point->y; bounder->extents.p2.x = point->x; bounder->extents.p2.y = point->y; - bounder->has_point = TRUE; } } @@ -173,7 +165,7 @@ _cairo_path_bounder_close_path (void *closure) * the control points of the curves, not the flattened path). */ void -_cairo_path_fixed_approximate_clip_extents (cairo_path_fixed_t *path, +_cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path, cairo_rectangle_int_t *extents) { cairo_path_bounder_t bounder; @@ -195,15 +187,13 @@ _cairo_path_fixed_approximate_clip_extents (cairo_path_fixed_t *path, extents->x = extents->y = 0; extents->width = extents->height = 0; } - - _cairo_path_bounder_fini (&bounder); } /* A slightly better approximation than above - we actually decompose the * Bezier, but we continue to ignore winding. */ void -_cairo_path_fixed_approximate_fill_extents (cairo_path_fixed_t *path, +_cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path, cairo_rectangle_int_t *extents) { cairo_path_bounder_t bounder; @@ -225,13 +215,37 @@ _cairo_path_fixed_approximate_fill_extents (cairo_path_fixed_t *path, extents->x = extents->y = 0; extents->width = extents->height = 0; } +} - _cairo_path_bounder_fini (&bounder); +void +_cairo_path_fixed_fill_extents (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_rectangle_int_t *extents) +{ + cairo_path_bounder_t bounder; + cairo_status_t status; + + _cairo_path_bounder_init (&bounder); + + status = _cairo_path_fixed_interpret_flat (path, CAIRO_DIRECTION_FORWARD, + _cairo_path_bounder_move_to, + _cairo_path_bounder_line_to, + _cairo_path_bounder_close_path, + &bounder, tolerance); + assert (status == CAIRO_STATUS_SUCCESS); + + if (bounder.has_point) { + _cairo_box_round_to_rectangle (&bounder.extents, extents); + } else { + extents->x = extents->y = 0; + extents->width = extents->height = 0; + } } /* Adjusts the fill extents (above) by the device-space pen. */ void -_cairo_path_fixed_approximate_stroke_extents (cairo_path_fixed_t *path, +_cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path, cairo_stroke_style_t *style, const cairo_matrix_t *ctm, cairo_rectangle_int_t *extents) @@ -259,17 +273,57 @@ _cairo_path_fixed_approximate_stroke_extents (cairo_path_fixed_t *path, bounder.extents.p1.y -= _cairo_fixed_from_double (dy); bounder.extents.p2.y += _cairo_fixed_from_double (dy); + _cairo_box_round_to_rectangle (&bounder.extents, extents); + } else if (bounder.has_initial_point) { + double dx, dy; + + /* accommodate capping of degenerate paths */ + + _cairo_stroke_style_max_distance_from_path (style, ctm, &dx, &dy); + + bounder.extents.p1.x = bounder.current_point.x - _cairo_fixed_from_double (dx); + bounder.extents.p2.x = bounder.current_point.x + _cairo_fixed_from_double (dx); + bounder.extents.p1.y = bounder.current_point.y - _cairo_fixed_from_double (dy); + bounder.extents.p2.y = bounder.current_point.y + _cairo_fixed_from_double (dy); + _cairo_box_round_to_rectangle (&bounder.extents, extents); } else { extents->x = extents->y = 0; extents->width = extents->height = 0; } +} - _cairo_path_bounder_fini (&bounder); +cairo_status_t +_cairo_path_fixed_stroke_extents (const cairo_path_fixed_t *path, + cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_rectangle_int_t *extents) +{ + cairo_traps_t traps; + cairo_box_t bbox; + cairo_status_t status; + + _cairo_traps_init (&traps); + + status = _cairo_path_fixed_stroke_to_traps (path, + stroke_style, + ctm, + ctm_inverse, + tolerance, + &traps); + + _cairo_traps_extents (&traps, &bbox); + _cairo_traps_fini (&traps); + + _cairo_box_round_to_rectangle (&bbox, extents); + + return status; } void -_cairo_path_fixed_bounds (cairo_path_fixed_t *path, +_cairo_path_fixed_bounds (const cairo_path_fixed_t *path, double *x1, double *y1, double *x2, double *y2) { @@ -297,6 +351,4 @@ _cairo_path_fixed_bounds (cairo_path_fixed_t *path, *x2 = 0.0; *y2 = 0.0; } - - _cairo_path_bounder_fini (&bounder); } diff --git a/gfx/cairo/cairo/src/cairo-path-fill.c b/gfx/cairo/cairo/src/cairo-path-fill.c index 9569c9e908ce..037b8078ea05 100644 --- a/gfx/cairo/cairo/src/cairo-path-fill.c +++ b/gfx/cairo/cairo/src/cairo-path-fill.c @@ -36,32 +36,25 @@ #include "cairoint.h" #include "cairo-path-fixed-private.h" +#include "cairo-region-private.h" typedef struct cairo_filler { double tolerance; - cairo_traps_t *traps; - - cairo_point_t current_point; - - cairo_polygon_t polygon; + cairo_polygon_t *polygon; } cairo_filler_t; static void -_cairo_filler_init (cairo_filler_t *filler, double tolerance, cairo_traps_t *traps) +_cairo_filler_init (cairo_filler_t *filler, + double tolerance, + cairo_polygon_t *polygon) { filler->tolerance = tolerance; - filler->traps = traps; - - filler->current_point.x = 0; - filler->current_point.y = 0; - - _cairo_polygon_init (&filler->polygon); + filler->polygon = polygon; } static void _cairo_filler_fini (cairo_filler_t *filler) { - _cairo_polygon_fini (&filler->polygon); } static cairo_status_t @@ -69,14 +62,10 @@ _cairo_filler_move_to (void *closure, const cairo_point_t *point) { cairo_filler_t *filler = closure; - cairo_polygon_t *polygon = &filler->polygon; + cairo_polygon_t *polygon = filler->polygon; - _cairo_polygon_close (polygon); - _cairo_polygon_move_to (polygon, point); - - filler->current_point = *point; - - return _cairo_polygon_status (&filler->polygon); + return _cairo_polygon_close (polygon) || + _cairo_polygon_move_to (polygon, point); } static cairo_status_t @@ -84,13 +73,7 @@ _cairo_filler_line_to (void *closure, const cairo_point_t *point) { cairo_filler_t *filler = closure; - cairo_polygon_t *polygon = &filler->polygon; - - _cairo_polygon_line_to (polygon, point); - - filler->current_point = *point; - - return _cairo_polygon_status (&filler->polygon); + return _cairo_polygon_line_to (filler->polygon, point); } static cairo_status_t @@ -103,11 +86,10 @@ _cairo_filler_curve_to (void *closure, cairo_spline_t spline; if (! _cairo_spline_init (&spline, - _cairo_filler_line_to, - filler, - &filler->current_point, b, c, d)) + _cairo_filler_line_to, filler, + &filler->polygon->current_point, b, c, d)) { - return CAIRO_STATUS_SUCCESS; + return _cairo_filler_line_to (closure, d); } return _cairo_spline_decompose (&spline, filler->tolerance); @@ -117,34 +99,18 @@ static cairo_status_t _cairo_filler_close_path (void *closure) { cairo_filler_t *filler = closure; - cairo_polygon_t *polygon = &filler->polygon; - - _cairo_polygon_close (polygon); - - return _cairo_polygon_status (polygon); + return _cairo_polygon_close (filler->polygon); } -static cairo_int_status_t -_cairo_path_fixed_fill_rectangle (cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - cairo_traps_t *traps); - cairo_status_t -_cairo_path_fixed_fill_to_traps (cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_traps_t *traps) +_cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path, + double tolerance, + cairo_polygon_t *polygon) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_filler_t filler; + cairo_status_t status; - /* Before we do anything else, we use a special-case filler for - * a device-axis aligned rectangle if possible. */ - status = _cairo_path_fixed_fill_rectangle (path, fill_rule, traps); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - _cairo_filler_init (&filler, tolerance, traps); + _cairo_filler_init (&filler, tolerance, polygon); status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, @@ -154,67 +120,154 @@ _cairo_path_fixed_fill_to_traps (cairo_path_fixed_t *path, _cairo_filler_close_path, &filler); if (unlikely (status)) - goto BAIL; + return status; - _cairo_polygon_close (&filler.polygon); - status = _cairo_polygon_status (&filler.polygon); - if (unlikely (status)) - goto BAIL; - - status = _cairo_bentley_ottmann_tessellate_polygon (filler.traps, - &filler.polygon, - fill_rule); - if (unlikely (status)) - goto BAIL; - -BAIL: + status = _cairo_polygon_close (polygon); _cairo_filler_fini (&filler); return status; } +cairo_status_t +_cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_traps_t *traps) +{ + cairo_polygon_t polygon; + cairo_status_t status; + + if (path->is_empty_fill) + return CAIRO_STATUS_SUCCESS; + + _cairo_polygon_init (&polygon); + _cairo_polygon_limit (&polygon, traps->limits, traps->num_limits); + + status = _cairo_path_fixed_fill_to_polygon (path, + tolerance, + &polygon); + if (unlikely (status || polygon.num_edges == 0)) + goto CLEANUP; + + if (path->is_rectilinear) { + status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (traps, + &polygon, + fill_rule); + } else { + status = _cairo_bentley_ottmann_tessellate_polygon (traps, + &polygon, + fill_rule); + } + + CLEANUP: + _cairo_polygon_fini (&polygon); + return status; +} + +static cairo_region_t * +_cairo_path_fixed_fill_rectilinear_tessellate_to_region (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + const cairo_rectangle_int_t *extents) +{ + cairo_box_t box; + cairo_polygon_t polygon; + cairo_traps_t traps; + cairo_status_t status; + cairo_region_t *region; + + /* first try to bypass fill-to-polygon */ + _cairo_traps_init (&traps); + status = _cairo_path_fixed_fill_rectilinear_to_traps (path, + fill_rule, + &traps); + if (_cairo_status_is_error (status)) + goto CLEANUP_TRAPS; + + if (status == CAIRO_STATUS_SUCCESS) { + status = _cairo_traps_extract_region (&traps, ®ion); + goto CLEANUP_TRAPS; + } + + /* path is not rectangular, try extracting clipped rectilinear edges */ + _cairo_polygon_init (&polygon); + if (extents != NULL) { + _cairo_box_from_rectangle (&box, extents); + _cairo_polygon_limit (&polygon, &box, 1); + } + + /* tolerance will be ignored as the path is rectilinear */ + status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon); + if (unlikely (status)) + goto CLEANUP_POLYGON; + + if (polygon.num_edges == 0) { + region = cairo_region_create (); + } else { + status = + _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps, + &polygon, + fill_rule); + if (likely (status == CAIRO_STATUS_SUCCESS)) + status = _cairo_traps_extract_region (&traps, ®ion); + } + + CLEANUP_POLYGON: + _cairo_polygon_fini (&polygon); + + CLEANUP_TRAPS: + _cairo_traps_fini (&traps); + + if (unlikely (status)) { /* XXX _cairo_region_create_in_error() */ + region = cairo_region_create (); + if (likely (region->status) == CAIRO_STATUS_SUCCESS) + region->status = status; + } + + return region; +} + /* This special-case filler supports only a path that describes a * device-axis aligned rectangle. It exists to avoid the overhead of * the general tessellator when drawing very common rectangles. * * If the path described anything but a device-axis aligned rectangle, - * this function will return %CAIRO_INT_STATUS_UNSUPPORTED. + * this function will abort. */ -static cairo_int_status_t -_cairo_path_fixed_fill_rectangle (cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - cairo_traps_t *traps) +cairo_region_t * +_cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + const cairo_rectangle_int_t *extents) { + cairo_rectangle_int_t rectangle_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; cairo_box_t box; + cairo_region_t *region = NULL; + + assert (path->maybe_fill_region); + assert (! path->is_empty_fill); if (_cairo_path_fixed_is_box (path, &box)) { - if (box.p1.x > box.p2.x) { - cairo_fixed_t t; - - t = box.p1.x; - box.p1.x = box.p2.x; - box.p2.x = t; - } - - if (box.p1.y > box.p2.y) { - cairo_fixed_t t; - - t = box.p1.y; - box.p1.y = box.p2.y; - box.p2.y = t; - } - - return _cairo_traps_tessellate_rectangle (traps, &box.p1, &box.p2); + rectangle_stack[0].x = _cairo_fixed_integer_part (box.p1.x); + rectangle_stack[0].y = _cairo_fixed_integer_part (box.p1.y); + rectangle_stack[0].width = _cairo_fixed_integer_part (box.p2.x) - + rectangle_stack[0].x; + rectangle_stack[0].height = _cairo_fixed_integer_part (box.p2.y) - + rectangle_stack[0].y; + if (! _cairo_rectangle_intersect (&rectangle_stack[0], extents)) + region = cairo_region_create (); + else + region = cairo_region_create_rectangle (&rectangle_stack[0]); } else if (fill_rule == CAIRO_FILL_RULE_WINDING) { + cairo_rectangle_int_t *rects = rectangle_stack; cairo_path_fixed_iter_t iter; int last_cw = -1; + int size = ARRAY_LENGTH (rectangle_stack); + int count = 0; /* Support a series of rectangles as can be expected to describe a * GdkRegion clip region during exposes. */ _cairo_path_fixed_iter_init (&iter, path); while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) { - cairo_status_t status; int cw = 0; if (box.p1.x > box.p2.x) { @@ -237,23 +290,104 @@ _cairo_path_fixed_fill_rectangle (cairo_path_fixed_t *path, cw = ! cw; } - if (last_cw < 0) { + if (last_cw < 0) last_cw = cw; - } else if (last_cw != cw) { - _cairo_traps_clear (traps); - return CAIRO_INT_STATUS_UNSUPPORTED; + else if (last_cw != cw) + goto TESSELLATE; + + if (count == size) { + cairo_rectangle_int_t *new_rects; + + size *= 4; + if (rects == rectangle_stack) { + new_rects = _cairo_malloc_ab (size, + sizeof (cairo_rectangle_int_t)); + if (unlikely (new_rects == NULL)) { + /* XXX _cairo_region_nil */ + break; + } + memcpy (new_rects, rects, sizeof (rectangle_stack)); + } else { + new_rects = _cairo_realloc_ab (rects, size, + sizeof (cairo_rectangle_int_t)); + if (unlikely (new_rects == NULL)) { + /* XXX _cairo_region_nil */ + break; + } + } + rects = new_rects; + } + + rects[count].x = _cairo_fixed_integer_part (box.p1.x); + rects[count].y = _cairo_fixed_integer_part (box.p1.y); + rects[count].width = _cairo_fixed_integer_part (box.p2.x) - rects[count].x; + rects[count].height = _cairo_fixed_integer_part (box.p2.y) - rects[count].y; + if (_cairo_rectangle_intersect (&rects[count], extents)) + count++; + } + + if (_cairo_path_fixed_iter_at_end (&iter)) + region = cairo_region_create_rectangles (rects, count); + +TESSELLATE: + if (rects != rectangle_stack) + free (rects); + } + + if (region == NULL) { + /* Hmm, complex polygon */ + region = _cairo_path_fixed_fill_rectilinear_tessellate_to_region (path, + fill_rule, + extents); + + + } + + return region; +} + +cairo_int_status_t +_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + cairo_traps_t *traps) +{ + cairo_box_t box; + cairo_status_t status; + + traps->is_rectilinear = TRUE; + traps->is_rectangular = TRUE; + + if (_cairo_path_fixed_is_box (path, &box)) { + return _cairo_traps_tessellate_rectangle (traps, &box.p1, &box.p2); + } else { + cairo_path_fixed_iter_t iter; + + _cairo_path_fixed_iter_init (&iter, path); + while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) { + if (box.p1.y > box.p2.y) { + cairo_fixed_t t; + + t = box.p1.y; + box.p1.y = box.p2.y; + box.p2.y = t; + + t = box.p1.x; + box.p1.x = box.p2.x; + box.p2.x = t; } status = _cairo_traps_tessellate_rectangle (traps, &box.p1, &box.p2); - if (unlikely (status)) + if (unlikely (status)) { + _cairo_traps_clear (traps); return status; + } } + if (_cairo_path_fixed_iter_at_end (&iter)) - return CAIRO_STATUS_SUCCESS; + return _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, fill_rule); _cairo_traps_clear (traps); + return CAIRO_INT_STATUS_UNSUPPORTED; } - - return CAIRO_INT_STATUS_UNSUPPORTED; } diff --git a/gfx/cairo/cairo/src/cairo-path-fixed-private.h b/gfx/cairo/cairo/src/cairo-path-fixed-private.h index 6bd09f765aee..08b7a0623255 100644 --- a/gfx/cairo/cairo/src/cairo-path-fixed-private.h +++ b/gfx/cairo/cairo/src/cairo-path-fixed-private.h @@ -81,12 +81,26 @@ struct _cairo_path_fixed { cairo_point_t current_point; unsigned int has_current_point : 1; unsigned int has_curve_to : 1; - unsigned int is_box : 1; - unsigned int is_region : 1; + unsigned int is_rectilinear : 1; + unsigned int maybe_fill_region : 1; + unsigned int is_empty_fill : 1; cairo_path_buf_fixed_t buf; }; + +cairo_private void +_cairo_path_fixed_translate (cairo_path_fixed_t *path, + cairo_fixed_t offx, + cairo_fixed_t offy); + +cairo_private cairo_status_t +_cairo_path_fixed_append (cairo_path_fixed_t *path, + const cairo_path_fixed_t *other, + cairo_direction_t dir, + cairo_fixed_t tx, + cairo_fixed_t ty); + cairo_private unsigned long _cairo_path_fixed_hash (const cairo_path_fixed_t *path); @@ -98,14 +112,15 @@ _cairo_path_fixed_equal (const cairo_path_fixed_t *a, const cairo_path_fixed_t *b); typedef struct _cairo_path_fixed_iter { - cairo_path_buf_t *buf; + const cairo_path_buf_t *first; + const cairo_path_buf_t *buf; unsigned int n_op; unsigned int n_point; } cairo_path_fixed_iter_t; cairo_private void _cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter, - cairo_path_fixed_t *path); + const cairo_path_fixed_t *path); cairo_private cairo_bool_t _cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter, @@ -115,13 +130,33 @@ cairo_private cairo_bool_t _cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter); static inline cairo_bool_t -_cairo_path_fixed_is_region (cairo_path_fixed_t *path) +_cairo_path_fixed_fill_is_empty (const cairo_path_fixed_t *path) +{ + return path->is_empty_fill; +} + +static inline cairo_bool_t +_cairo_path_fixed_is_rectilinear_fill (const cairo_path_fixed_t *path) +{ + if (! path->is_rectilinear) + return 0; + + if (! path->has_current_point) + return 1; + + /* check whether the implicit close preserves the rectilinear property */ + return path->current_point.x == path->last_move_point.x || + path->current_point.y == path->last_move_point.y; +} + +static inline cairo_bool_t +_cairo_path_fixed_maybe_fill_region (const cairo_path_fixed_t *path) { #if WATCH_PATH - fprintf (stderr, "_cairo_path_fixed_is_region () = %s\n", - path->is_region ? "true" : "false"); + fprintf (stderr, "_cairo_path_fixed_maybe_fill_region () = %s\n", + path->maybe_fill_region ? "true" : "false"); #endif - return path->is_region; + return path->maybe_fill_region; } #endif /* CAIRO_PATH_FIXED_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-path-fixed.c b/gfx/cairo/cairo/src/cairo-path-fixed.c index 0a550399cbb7..34de6a37bd77 100644 --- a/gfx/cairo/cairo/src/cairo-path-fixed.c +++ b/gfx/cairo/cairo/src/cairo-path-fixed.c @@ -39,6 +39,7 @@ #include "cairoint.h" #include "cairo-path-fixed-private.h" +#include "cairo-slope-private.h" static cairo_status_t _cairo_path_fixed_add (cairo_path_fixed_t *path, @@ -96,25 +97,32 @@ _cairo_path_fixed_init (cairo_path_fixed_t *path) path->last_move_point = path->current_point; path->has_current_point = FALSE; path->has_curve_to = FALSE; - path->is_region = TRUE; - path->is_box = TRUE; + path->is_rectilinear = TRUE; + path->maybe_fill_region = TRUE; + path->is_empty_fill = TRUE; } cairo_status_t _cairo_path_fixed_init_copy (cairo_path_fixed_t *path, - cairo_path_fixed_t *other) + const cairo_path_fixed_t *other) { cairo_path_buf_t *buf, *other_buf; unsigned int num_points, num_ops, buf_size; - _cairo_path_fixed_init (path); + VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t))); + + cairo_list_init (&path->buf.base.link); + + path->buf.base.op = path->buf.op; + path->buf.base.points = path->buf.points; path->current_point = other->current_point; - path->has_current_point = other->has_current_point; path->last_move_point = other->last_move_point; + path->has_current_point = other->has_current_point; path->has_curve_to = other->has_curve_to; - path->is_box = other->is_box; - path->is_region = other->is_region; + path->is_rectilinear = other->is_rectilinear; + path->maybe_fill_region = other->maybe_fill_region; + path->is_empty_fill = other->is_empty_fill; path->buf.base.num_ops = other->buf.base.num_ops; path->buf.base.num_points = other->buf.base.num_points; @@ -214,9 +222,10 @@ _cairo_path_fixed_equal (const cairo_path_fixed_t *a, return TRUE; /* use the flags to quickly differentiate based on contents */ - if (a->has_curve_to != b->has_curve_to || - a->is_region != b->is_region || - a->is_box != b->is_box) + if (a->is_empty_fill != b->is_empty_fill || + a->has_curve_to != b->has_curve_to || + a->maybe_fill_region != b->maybe_fill_region || + a->is_rectilinear != b->is_rectilinear) { return FALSE; } @@ -378,15 +387,16 @@ _cairo_path_fixed_move_to (cairo_path_fixed_t *path, if (unlikely (status)) return status; - if (path->has_current_point && path->is_box) { + if (path->has_current_point && path->is_rectilinear) { /* a move-to is first an implicit close */ - path->is_box = path->current_point.x == path->last_move_point.x || - path->current_point.y == path->last_move_point.y; - path->is_region &= path->is_box; + path->is_rectilinear = path->current_point.x == path->last_move_point.x || + path->current_point.y == path->last_move_point.y; + path->maybe_fill_region &= path->is_rectilinear; } - if (path->is_region) { - path->is_region = _cairo_fixed_is_integer (x) && - _cairo_fixed_is_integer (y); + if (path->maybe_fill_region) { + path->maybe_fill_region = + _cairo_fixed_is_integer (path->last_move_point.x) && + _cairo_fixed_is_integer (path->last_move_point.y); } } @@ -433,25 +443,74 @@ _cairo_path_fixed_line_to (cairo_path_fixed_t *path, * explicitly calling into _cairo_path_fixed_move_to to ensure * that the last_move_point state is updated properly. */ - if (! path->has_current_point) { - status = _cairo_path_fixed_move_to (path, point.x, point.y); - } else { - status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1); - if (path->is_box) { - path->is_box = path->current_point.x == x || - path->current_point.y == y; - path->is_region &= path->is_box; + if (! path->has_current_point) + return _cairo_path_fixed_move_to (path, point.x, point.y); + + /* If the previous op was but the initial MOVE_TO and this segment + * is degenerate, then we can simply skip this point. Note that + * a move-to followed by a degenerate line-to is a valid path for + * stroking, but at all other times is simply a degenerate segment. + */ + if (_cairo_path_last_op (path) != CAIRO_PATH_OP_MOVE_TO) { + if (x == path->current_point.x && y == path->current_point.y) + return CAIRO_STATUS_SUCCESS; + } + + /* If the previous op was also a LINE_TO with the same gradient, + * then just change its end-point rather than adding a new op. + */ + if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) { + cairo_path_buf_t *buf; + const cairo_point_t *p; + + buf = cairo_path_tail (path); + if (likely (buf->num_points >= 2)) { + p = &buf->points[buf->num_points-2]; + } else { + cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf); + p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)]; } - if (path->is_region) { - path->is_region = _cairo_fixed_is_integer (x) && - _cairo_fixed_is_integer (y); + + if (p->x == path->current_point.x && p->y == path->current_point.y) { + /* previous line element was degenerate, replace */ + buf->points[buf->num_points - 1] = point; + goto FLAGS; + } else { + cairo_slope_t prev, self; + + _cairo_slope_init (&prev, p, &path->current_point); + _cairo_slope_init (&self, &path->current_point, &point); + if (_cairo_slope_equal (&prev, &self) && + /* cannot trim anti-parallel segments whilst stroking */ + ! _cairo_slope_backwards (&prev, &self)) + { + buf->points[buf->num_points - 1] = point; + goto FLAGS; + } } } - path->current_point = point; - path->has_current_point = TRUE; + status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1); + if (unlikely (status)) + return status; - return status; + FLAGS: + if (path->is_rectilinear) { + path->is_rectilinear = path->current_point.x == x || + path->current_point.y == y; + path->maybe_fill_region &= path->is_rectilinear; + } + if (path->maybe_fill_region) { + path->maybe_fill_region = _cairo_fixed_is_integer (x) && + _cairo_fixed_is_integer (y); + } + if (path->is_empty_fill) { + path->is_empty_fill = path->current_point.x == x && + path->current_point.y == y; + } + + path->current_point = point; + return CAIRO_STATUS_SUCCESS; } cairo_status_t @@ -483,9 +542,6 @@ _cairo_path_fixed_curve_to (cairo_path_fixed_t *path, return status; } - if (x2 == path->current_point.x && y2 == path->current_point.y) - return _cairo_path_fixed_line_to (path, x2, y2); - point[0].x = x0; point[0].y = y0; point[1].x = x1; point[1].y = y1; point[2].x = x2; point[2].y = y2; @@ -495,9 +551,10 @@ _cairo_path_fixed_curve_to (cairo_path_fixed_t *path, path->current_point = point[2]; path->has_current_point = TRUE; + path->is_empty_fill = FALSE; path->has_curve_to = TRUE; - path->is_box = FALSE; - path->is_region = FALSE; + path->is_rectilinear = FALSE; + path->maybe_fill_region = FALSE; return CAIRO_STATUS_SUCCESS; } @@ -511,12 +568,6 @@ _cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path, if (unlikely (! path->has_current_point)) return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT); - if (dx2 == 0 && dy2 == 0) { - return _cairo_path_fixed_line_to (path, - path->current_point.x, - path->current_point.y); - } - return _cairo_path_fixed_curve_to (path, path->current_point.x + dx0, path->current_point.y + dy0, @@ -536,6 +587,28 @@ _cairo_path_fixed_close_path (cairo_path_fixed_t *path) if (! path->has_current_point) return CAIRO_STATUS_SUCCESS; + /* If the previous op was also a LINE_TO back to the start, discard it */ + if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) { + if (path->current_point.x == path->last_move_point.x && + path->current_point.y == path->last_move_point.y) + { + cairo_path_buf_t *buf; + cairo_point_t *p; + + buf = cairo_path_tail (path); + if (likely (buf->num_points >= 2)) { + p = &buf->points[buf->num_points-2]; + } else { + cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf); + p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)]; + } + + path->current_point = *p; + buf->num_ops--; + buf->num_points--; + } + } + status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0); if (unlikely (status)) return status; @@ -732,49 +805,77 @@ _cairo_path_fixed_interpret (const cairo_path_fixed_t *path, return CAIRO_STATUS_SUCCESS; } +typedef struct _cairo_path_fixed_append_closure { + cairo_point_t offset; + cairo_path_fixed_t *path; +} cairo_path_fixed_append_closure_t; + static cairo_status_t -_append_move_to (void *closure, +_append_move_to (void *abstract_closure, const cairo_point_t *point) { - return _cairo_path_fixed_move_to (closure, point->x, point->y); + cairo_path_fixed_append_closure_t *closure = abstract_closure; + + return _cairo_path_fixed_move_to (closure->path, + point->x + closure->offset.x, + point->y + closure->offset.y); } static cairo_status_t -_append_line_to (void *closure, +_append_line_to (void *abstract_closure, const cairo_point_t *point) { - return _cairo_path_fixed_line_to (closure, point->x, point->y); + cairo_path_fixed_append_closure_t *closure = abstract_closure; + + return _cairo_path_fixed_line_to (closure->path, + point->x + closure->offset.x, + point->y + closure->offset.y); } static cairo_status_t -_append_curve_to (void *closure, +_append_curve_to (void *abstract_closure, const cairo_point_t *p0, const cairo_point_t *p1, const cairo_point_t *p2) { - return _cairo_path_fixed_curve_to (closure, - p0->x, p0->y, - p1->x, p1->y, - p2->x, p2->y); + cairo_path_fixed_append_closure_t *closure = abstract_closure; + + return _cairo_path_fixed_curve_to (closure->path, + p0->x + closure->offset.x, + p0->y + closure->offset.y, + p1->x + closure->offset.x, + p1->y + closure->offset.y, + p2->x + closure->offset.x, + p2->y + closure->offset.y); } static cairo_status_t -_append_close_path (void *closure) +_append_close_path (void *abstract_closure) { - return _cairo_path_fixed_close_path (closure); + cairo_path_fixed_append_closure_t *closure = abstract_closure; + + return _cairo_path_fixed_close_path (closure->path); } cairo_status_t -_cairo_path_fixed_append (cairo_path_fixed_t *path, - const cairo_path_fixed_t *other, - cairo_direction_t dir) +_cairo_path_fixed_append (cairo_path_fixed_t *path, + const cairo_path_fixed_t *other, + cairo_direction_t dir, + cairo_fixed_t tx, + cairo_fixed_t ty) { + cairo_path_fixed_append_closure_t closure; + + closure.path = path; + closure.offset.x = tx; + closure.offset.y = ty; + return _cairo_path_fixed_interpret (other, dir, _append_move_to, _append_line_to, _append_curve_to, _append_close_path, - path); + &closure); } static void @@ -787,6 +888,13 @@ _cairo_path_fixed_offset_and_scale (cairo_path_fixed_t *path, cairo_path_buf_t *buf; unsigned int i; + if (path->maybe_fill_region) { + path->maybe_fill_region = _cairo_fixed_is_integer (offx) && + _cairo_fixed_is_integer (offy) && + _cairo_fixed_is_integer (scalex) && + _cairo_fixed_is_integer (scaley); + } + cairo_path_foreach_buf_start (buf, path) { for (i = 0; i < buf->num_points; i++) { if (scalex != CAIRO_FIXED_ONE) @@ -800,6 +908,36 @@ _cairo_path_fixed_offset_and_scale (cairo_path_fixed_t *path, } cairo_path_foreach_buf_end (buf, path); } +void +_cairo_path_fixed_translate (cairo_path_fixed_t *path, + cairo_fixed_t offx, + cairo_fixed_t offy) +{ + cairo_path_buf_t *buf; + unsigned int i; + + if (offx == 0 && offy == 0) + return; + + if (path->maybe_fill_region && + ! (_cairo_fixed_is_integer (offx) && _cairo_fixed_is_integer (offy))) + { + path->maybe_fill_region = FALSE; + } + + path->last_move_point.x += offx; + path->last_move_point.y += offx; + path->current_point.x += offx; + path->current_point.y += offx; + + cairo_path_foreach_buf_start (buf, path) { + for (i = 0; i < buf->num_points; i++) { + buf->points[i].x += offx; + buf->points[i].y += offy; + } + } cairo_path_foreach_buf_end (buf, path); +} + /** * _cairo_path_fixed_transform: * @path: a #cairo_path_fixed_t to be transformed @@ -811,12 +949,14 @@ _cairo_path_fixed_offset_and_scale (cairo_path_fixed_t *path, **/ void _cairo_path_fixed_transform (cairo_path_fixed_t *path, - cairo_matrix_t *matrix) + const cairo_matrix_t *matrix) { cairo_path_buf_t *buf; unsigned int i; double dx, dy; + /* XXX current_point, last_move_to */ + if (matrix->yx == 0.0 && matrix->xy == 0.0) { /* Fast path for the common case of scale+transform */ _cairo_path_fixed_offset_and_scale (path, @@ -827,6 +967,7 @@ _cairo_path_fixed_transform (cairo_path_fixed_t *path, return; } + path->maybe_fill_region = FALSE; cairo_path_foreach_buf_start (buf, path) { for (i = 0; i < buf->num_points; i++) { dx = _cairo_fixed_to_double (buf->points[i].x); @@ -841,20 +982,22 @@ _cairo_path_fixed_transform (cairo_path_fixed_t *path, } cairo_bool_t -_cairo_path_fixed_is_equal (cairo_path_fixed_t *path, - cairo_path_fixed_t *other) +_cairo_path_fixed_is_equal (const cairo_path_fixed_t *path, + const cairo_path_fixed_t *other) { - cairo_path_buf_t *path_buf, *other_buf; + const cairo_path_buf_t *path_buf, *other_buf; if (path->current_point.x != other->current_point.x || path->current_point.y != other->current_point.y || path->has_current_point != other->has_current_point || path->has_curve_to != other->has_curve_to || - path->is_box != other->is_box || - path->is_region != other->is_region || + path->is_rectilinear != other->is_rectilinear || + path->maybe_fill_region != other->maybe_fill_region || path->last_move_point.x != other->last_move_point.x || path->last_move_point.y != other->last_move_point.y) + { return FALSE; + } other_buf = cairo_path_head (other); cairo_path_foreach_buf_start (path_buf, path) { @@ -970,29 +1113,42 @@ _cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path, &flattener); } -cairo_bool_t -_cairo_path_fixed_is_empty (cairo_path_fixed_t *path) +static inline void +_canonical_box (cairo_box_t *box, + const cairo_point_t *p1, + const cairo_point_t *p2) { - if (cairo_path_head (path)->num_ops == 0) - return TRUE; + if (p1->x <= p2->x) { + box->p1.x = p1->x; + box->p2.x = p2->x; + } else { + box->p1.x = p2->x; + box->p2.x = p1->x; + } - return FALSE; + if (p1->y <= p2->y) { + box->p1.y = p1->y; + box->p2.y = p2->y; + } else { + box->p1.y = p2->y; + box->p2.y = p1->y; + } } /* * Check whether the given path contains a single rectangle. */ cairo_bool_t -_cairo_path_fixed_is_box (cairo_path_fixed_t *path, +_cairo_path_fixed_is_box (const cairo_path_fixed_t *path, cairo_box_t *box) { - cairo_path_buf_t *buf = cairo_path_head (path); + const cairo_path_buf_t *buf = cairo_path_head (path); - if (! path->is_box) + if (! path->is_rectilinear) return FALSE; /* Do we have the right number of ops? */ - if (buf->num_ops != 5 && buf->num_ops != 6) + if (buf->num_ops < 4 || buf->num_ops > 6) return FALSE; /* Check whether the ops are those that would be used for a rectangle */ @@ -1004,22 +1160,25 @@ _cairo_path_fixed_is_box (cairo_path_fixed_t *path, return FALSE; } - /* Now, there are choices. The rectangle might end with a LINE_TO - * (to the original point), but this isn't required. If it - * doesn't, then it must end with a CLOSE_PATH. */ - if (buf->op[4] == CAIRO_PATH_OP_LINE_TO) { - if (buf->points[4].x != buf->points[0].x || - buf->points[4].y != buf->points[0].y) + /* we accept an implicit close for filled paths */ + if (buf->num_ops > 4) { + /* Now, there are choices. The rectangle might end with a LINE_TO + * (to the original point), but this isn't required. If it + * doesn't, then it must end with a CLOSE_PATH. */ + if (buf->op[4] == CAIRO_PATH_OP_LINE_TO) { + if (buf->points[4].x != buf->points[0].x || + buf->points[4].y != buf->points[0].y) + return FALSE; + } else if (buf->op[4] != CAIRO_PATH_OP_CLOSE_PATH) { return FALSE; - } else if (buf->op[4] != CAIRO_PATH_OP_CLOSE_PATH) { - return FALSE; - } + } - if (buf->num_ops == 6) { - /* A trailing CLOSE_PATH or MOVE_TO is ok */ - if (buf->op[5] != CAIRO_PATH_OP_MOVE_TO && - buf->op[5] != CAIRO_PATH_OP_CLOSE_PATH) - return FALSE; + if (buf->num_ops == 6) { + /* A trailing CLOSE_PATH or MOVE_TO is ok */ + if (buf->op[5] != CAIRO_PATH_OP_MOVE_TO && + buf->op[5] != CAIRO_PATH_OP_CLOSE_PATH) + return FALSE; + } } /* Ok, we may have a box, if the points line up */ @@ -1028,8 +1187,7 @@ _cairo_path_fixed_is_box (cairo_path_fixed_t *path, buf->points[2].y == buf->points[3].y && buf->points[3].x == buf->points[0].x) { - box->p1 = buf->points[0]; - box->p2 = buf->points[2]; + _canonical_box (box, &buf->points[0], &buf->points[2]); return TRUE; } @@ -1038,8 +1196,7 @@ _cairo_path_fixed_is_box (cairo_path_fixed_t *path, buf->points[2].x == buf->points[3].x && buf->points[3].y == buf->points[0].y) { - box->p1 = buf->points[0]; - box->p2 = buf->points[2]; + _canonical_box (box, &buf->points[0], &buf->points[2]); return TRUE; } @@ -1058,12 +1215,12 @@ _cairo_path_fixed_is_box (cairo_path_fixed_t *path, * */ cairo_bool_t -_cairo_path_fixed_is_rectangle (cairo_path_fixed_t *path, +_cairo_path_fixed_is_rectangle (const cairo_path_fixed_t *path, cairo_box_t *box) { - cairo_path_buf_t *buf; + const cairo_path_buf_t *buf; - if (!_cairo_path_fixed_is_box (path, box)) + if (! _cairo_path_fixed_is_box (path, box)) return FALSE; buf = cairo_path_head (path); @@ -1075,9 +1232,9 @@ _cairo_path_fixed_is_rectangle (cairo_path_fixed_t *path, void _cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter, - cairo_path_fixed_t *path) + const cairo_path_fixed_t *path) { - iter->buf = cairo_path_head (path); + iter->first = iter->buf = cairo_path_head (path); iter->n_op = 0; iter->n_point = 0; } @@ -1087,11 +1244,16 @@ _cairo_path_fixed_iter_next_op (cairo_path_fixed_iter_t *iter) { if (++iter->n_op >= iter->buf->num_ops) { iter->buf = cairo_path_buf_next (iter->buf); + if (iter->buf == iter->first) { + iter->buf = NULL; + return FALSE; + } + iter->n_op = 0; iter->n_point = 0; } - return iter->buf != NULL; + return TRUE; } cairo_bool_t @@ -1171,8 +1333,8 @@ _cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter, points[2].x == points[3].x && points[3].y == points[0].y) { - box->p1 = points[0]; - box->p2 = points[2]; + box->p1 = points[1]; + box->p2 = points[3]; *_iter = iter; return TRUE; } diff --git a/gfx/cairo/cairo/src/cairo-path-in-fill.c b/gfx/cairo/cairo/src/cairo-path-in-fill.c index 24f43ca5e68b..2fa865054d57 100644 --- a/gfx/cairo/cairo/src/cairo-path-in-fill.c +++ b/gfx/cairo/cairo/src/cairo-path-in-fill.c @@ -38,10 +38,10 @@ typedef struct cairo_in_fill { double tolerance; + cairo_bool_t on_edge; int winding; cairo_fixed_t x, y; - cairo_bool_t on_edge; cairo_bool_t has_current_point; cairo_point_t current_point; @@ -54,12 +54,12 @@ _cairo_in_fill_init (cairo_in_fill_t *in_fill, double x, double y) { + in_fill->on_edge = FALSE; in_fill->winding = 0; in_fill->tolerance = tolerance; in_fill->x = _cairo_fixed_from_double (x); in_fill->y = _cairo_fixed_from_double (y); - in_fill->on_edge = FALSE; in_fill->has_current_point = FALSE; in_fill->current_point.x = 0; @@ -142,7 +142,7 @@ _cairo_in_fill_add_edge (cairo_in_fill_t *in_fill, return; if ((p1->x <= in_fill->x && p2->x <= in_fill->x) || - edge_compare_for_y_against_x (p1, p2, in_fill->y, in_fill->x) <= 0) + edge_compare_for_y_against_x (p1, p2, in_fill->y, in_fill->x) < 0) { in_fill->winding += dir; } @@ -243,16 +243,19 @@ _cairo_in_fill_close_path (void *closure) return CAIRO_STATUS_SUCCESS; } -void -_cairo_path_fixed_in_fill (cairo_path_fixed_t *path, +cairo_bool_t +_cairo_path_fixed_in_fill (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, double x, - double y, - cairo_bool_t *is_inside) + double y) { cairo_in_fill_t in_fill; cairo_status_t status; + cairo_bool_t is_inside; + + if (path->is_empty_fill) + return FALSE; _cairo_in_fill_init (&in_fill, tolerance, x, y); @@ -268,19 +271,21 @@ _cairo_path_fixed_in_fill (cairo_path_fixed_t *path, _cairo_in_fill_close_path (&in_fill); if (in_fill.on_edge) { - *is_inside = TRUE; + is_inside = TRUE; } else switch (fill_rule) { case CAIRO_FILL_RULE_EVEN_ODD: - *is_inside = in_fill.winding & 1; + is_inside = in_fill.winding & 1; break; case CAIRO_FILL_RULE_WINDING: - *is_inside = in_fill.winding != 0; + is_inside = in_fill.winding != 0; break; default: ASSERT_NOT_REACHED; - *is_inside = FALSE; + is_inside = FALSE; break; } _cairo_in_fill_fini (&in_fill); + + return is_inside; } diff --git a/gfx/cairo/cairo/src/cairo-path-stroke.c b/gfx/cairo/cairo/src/cairo-path-stroke.c index 79bf09b0b0f4..6e1986a9751b 100644 --- a/gfx/cairo/cairo/src/cairo-path-stroke.c +++ b/gfx/cairo/cairo/src/cairo-path-stroke.c @@ -33,11 +33,14 @@ * * Contributor(s): * Carl D. Worth + * Chris Wilson */ #define _BSD_SOURCE /* for hypot() */ #include "cairoint.h" + #include "cairo-path-fixed-private.h" +#include "cairo-slope-private.h" typedef struct _cairo_stroker_dash { cairo_bool_t dashed; @@ -48,6 +51,7 @@ typedef struct _cairo_stroker_dash { double dash_offset; const double *dashes; + double approximate_dashes[2]; unsigned int num_dashes; } cairo_stroker_dash_t; @@ -60,7 +64,18 @@ typedef struct cairo_stroker { double ctm_determinant; cairo_bool_t ctm_det_positive; - cairo_traps_t *traps; + void *closure; + cairo_status_t (*add_external_edge) (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2); + cairo_status_t (*add_triangle) (void *closure, + const cairo_point_t triangle[3]); + cairo_status_t (*add_triangle_fan) (void *closure, + const cairo_point_t *midpt, + const cairo_point_t *points, + int npoints); + cairo_status_t (*add_convex_quad) (void *closure, + const cairo_point_t quad[4]); cairo_pen_t pen; @@ -123,15 +138,25 @@ _cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step) static void _cairo_stroker_dash_init (cairo_stroker_dash_t *dash, - const cairo_stroke_style_t *style) + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + double tolerance) { dash->dashed = style->dash != NULL; if (! dash->dashed) return; - dash->dashes = style->dash; - dash->num_dashes = style->num_dashes; - dash->dash_offset = style->dash_offset; + if (_cairo_stroke_style_dash_can_approximate (style, ctm, tolerance)) { + _cairo_stroke_style_dash_approximate (style, ctm, tolerance, + &dash->dash_offset, + dash->approximate_dashes, + &dash->num_dashes); + dash->dashes = dash->approximate_dashes; + } else { + dash->dashes = style->dash; + dash->num_dashes = style->num_dashes; + dash->dash_offset = style->dash_offset; + } _cairo_stroker_dash_start (dash); } @@ -141,8 +166,7 @@ _cairo_stroker_init (cairo_stroker_t *stroker, cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_traps_t *traps) + double tolerance) { cairo_status_t status; @@ -150,7 +174,6 @@ _cairo_stroker_init (cairo_stroker_t *stroker, stroker->ctm = ctm; stroker->ctm_inverse = ctm_inverse; stroker->tolerance = tolerance; - stroker->traps = traps; stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm); stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0; @@ -161,37 +184,48 @@ _cairo_stroker_init (cairo_stroker_t *stroker, if (unlikely (status)) return status; + stroker->has_bounds = FALSE; + stroker->has_current_face = FALSE; stroker->has_first_face = FALSE; stroker->has_initial_sub_path = FALSE; - _cairo_stroker_dash_init (&stroker->dash, stroke_style); + _cairo_stroker_dash_init (&stroker->dash, stroke_style, ctm, tolerance); - stroker->has_bounds = _cairo_traps_get_limit (traps, &stroker->bounds); - if (stroker->has_bounds) { - /* Extend the bounds in each direction to account for the maximum area - * we might generate trapezoids, to capture line segments that are outside - * of the bounds but which might generate rendering that's within bounds. - */ - double dx, dy; - cairo_fixed_t fdx, fdy; - - _cairo_stroke_style_max_distance_from_path (stroker->style, - stroker->ctm, - &dx, &dy); - - fdx = _cairo_fixed_from_double (dx); - stroker->bounds.p1.x -= fdx; - stroker->bounds.p2.x += fdx; - - fdy = _cairo_fixed_from_double (dy); - stroker->bounds.p1.y -= fdy; - stroker->bounds.p2.y += fdy; - } + stroker->add_external_edge = NULL; return CAIRO_STATUS_SUCCESS; } +static void +_cairo_stroker_limit (cairo_stroker_t *stroker, + const cairo_box_t *boxes, + int num_boxes) +{ + double dx, dy; + cairo_fixed_t fdx, fdy; + + stroker->has_bounds = TRUE; + _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds); + + /* Extend the bounds in each direction to account for the maximum area + * we might generate trapezoids, to capture line segments that are outside + * of the bounds but which might generate rendering that's within bounds. + */ + + _cairo_stroke_style_max_distance_from_path (stroker->style, stroker->ctm, + &dx, &dy); + + fdx = _cairo_fixed_from_double (dx); + fdy = _cairo_fixed_from_double (dy); + + stroker->bounds.p1.x -= fdx; + stroker->bounds.p2.x += fdx; + + stroker->bounds.p1.y -= fdy; + stroker->bounds.p2.y += fdy; +} + static void _cairo_stroker_fini (cairo_stroker_t *stroker) { @@ -199,14 +233,15 @@ _cairo_stroker_fini (cairo_stroker_t *stroker) } static void -_translate_point (cairo_point_t *point, cairo_point_t *offset) +_translate_point (cairo_point_t *point, const cairo_point_t *offset) { point->x += offset->x; point->y += offset->y; } static int -_cairo_stroker_face_clockwise (cairo_stroke_face_t *in, cairo_stroke_face_t *out) +_cairo_stroker_join_is_clockwise (const cairo_stroke_face_t *in, + const cairo_stroke_face_t *out) { cairo_slope_t in_slope, out_slope; @@ -232,76 +267,209 @@ _cairo_slope_compare_sgn (double dx1, double dy1, double dx2, double dy2) return 0; } -static cairo_status_t -_cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_stroke_face_t *out) +static inline int +_range_step (int i, int step, int max) { - int clockwise = _cairo_stroker_face_clockwise (out, in); - cairo_point_t *inpt, *outpt; + i += step; + if (i < 0) + i = max - 1; + if (i >= max) + i = 0; + return i; +} + +/* + * Construct a fan around the midpoint using the vertices from pen between + * inpt and outpt. + */ +static cairo_status_t +_tessellate_fan (cairo_stroker_t *stroker, + const cairo_slope_t *in_vector, + const cairo_slope_t *out_vector, + const cairo_point_t *midpt, + const cairo_point_t *inpt, + const cairo_point_t *outpt, + cairo_bool_t clockwise) +{ + cairo_point_t stack_points[64], *points = stack_points; + int start, stop, step, i, npoints; cairo_status_t status; - if (in->cw.x == out->cw.x - && in->cw.y == out->cw.y - && in->ccw.x == out->ccw.x - && in->ccw.y == out->ccw.y) + if (clockwise) { + step = -1; + + start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen, + in_vector); + if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw, + in_vector) < 0) + start = _range_step (start, -1, stroker->pen.num_vertices); + + stop = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen, + out_vector); + if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw, + out_vector) > 0) + { + stop = _range_step (stop, 1, stroker->pen.num_vertices); + if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw, + in_vector) < 0) + { + goto BEVEL; + } + } + + npoints = start - stop; + } else { + step = 1; + + start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen, + in_vector); + if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw, + in_vector) < 0) + start = _range_step (start, 1, stroker->pen.num_vertices); + + stop = _cairo_pen_find_active_cw_vertex_index (&stroker->pen, + out_vector); + if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw, + out_vector) > 0) + { + stop = _range_step (stop, -1, stroker->pen.num_vertices); + if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw, + in_vector) < 0) + { + goto BEVEL; + } + } + + npoints = stop - start; + } + stop = _range_step (stop, step, stroker->pen.num_vertices); + + if (npoints < 0) + npoints += stroker->pen.num_vertices; + npoints += 3; + + if (npoints <= 1) + goto BEVEL; + + if (npoints > ARRAY_LENGTH (stack_points)) { + points = _cairo_malloc_ab (npoints, sizeof (cairo_point_t)); + if (unlikely (points == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + + /* Construct the fan. */ + npoints = 0; + points[npoints++] = *inpt; + for (i = start; + i != stop; + i = _range_step (i, step, stroker->pen.num_vertices)) + { + points[npoints] = *midpt; + _translate_point (&points[npoints], &stroker->pen.vertices[i].point); + npoints++; + } + points[npoints++] = *outpt; + + if (stroker->add_external_edge != NULL) { + for (i = 0; i < npoints - 1; i++) { + if (clockwise) { + status = stroker->add_external_edge (stroker->closure, + &points[i], &points[i+1]); + } else { + status = stroker->add_external_edge (stroker->closure, + &points[i+1], &points[i]); + } + if (unlikely (status)) + break; + } + } else { + status = stroker->add_triangle_fan (stroker->closure, + midpt, points, npoints); + } + + if (points != stack_points) + free (points); + + return status; + +BEVEL: + /* Ensure a leak free connection... */ + if (stroker->add_external_edge != NULL) { + if (clockwise) + return stroker->add_external_edge (stroker->closure, inpt, outpt); + else + return stroker->add_external_edge (stroker->closure, outpt, inpt); + } else { + stack_points[0] = *midpt; + stack_points[1] = *inpt; + stack_points[2] = *outpt; + return stroker->add_triangle (stroker->closure, stack_points); + } +} + +static cairo_status_t +_cairo_stroker_join (cairo_stroker_t *stroker, + const cairo_stroke_face_t *in, + const cairo_stroke_face_t *out) +{ + int clockwise = _cairo_stroker_join_is_clockwise (out, in); + const cairo_point_t *inpt, *outpt; + cairo_point_t points[4]; + cairo_status_t status; + + if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && + in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y) { return CAIRO_STATUS_SUCCESS; } if (clockwise) { + if (stroker->add_external_edge != NULL) { + status = stroker->add_external_edge (stroker->closure, + &out->cw, &in->point); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &in->point, &in->cw); + if (unlikely (status)) + return status; + } + inpt = &in->ccw; outpt = &out->ccw; } else { + if (stroker->add_external_edge != NULL) { + status = stroker->add_external_edge (stroker->closure, + &in->ccw, &in->point); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &in->point, &out->ccw); + if (unlikely (status)) + return status; + } + inpt = &in->cw; outpt = &out->cw; } switch (stroker->style->line_join) { - case CAIRO_LINE_JOIN_ROUND: { - int i; - int start, step, stop; - cairo_point_t tri[3]; - cairo_pen_t *pen = &stroker->pen; + case CAIRO_LINE_JOIN_ROUND: + /* construct a fan around the common midpoint */ + return _tessellate_fan (stroker, + &in->dev_vector, + &out->dev_vector, + &in->point, inpt, outpt, + clockwise); - tri[0] = in->point; - if (clockwise) { - start = - _cairo_pen_find_active_ccw_vertex_index (pen, &in->dev_vector); - stop = - _cairo_pen_find_active_ccw_vertex_index (pen, &out->dev_vector); - step = -1; - } else { - start = - _cairo_pen_find_active_cw_vertex_index (pen, &in->dev_vector); - stop = - _cairo_pen_find_active_cw_vertex_index (pen, &out->dev_vector); - step = +1; - } - - i = start; - tri[1] = *inpt; - while (i != stop) { - tri[2] = in->point; - _translate_point (&tri[2], &pen->vertices[i].point); - status = _cairo_traps_tessellate_triangle (stroker->traps, tri); - if (unlikely (status)) - return status; - tri[1] = tri[2]; - i += step; - if (i < 0) - i = pen->num_vertices - 1; - if (i >= pen->num_vertices) - i = 0; - } - - tri[2] = *outpt; - - return _cairo_traps_tessellate_triangle (stroker->traps, tri); - } case CAIRO_LINE_JOIN_MITER: default: { /* dot product of incoming slope vector with outgoing slope vector */ - double in_dot_out = ((-in->usr_vector.x * out->usr_vector.x)+ - (-in->usr_vector.y * out->usr_vector.y)); + double in_dot_out = -in->usr_vector.x * out->usr_vector.x + + -in->usr_vector.y * out->usr_vector.y; double ml = stroker->style->miter_limit; /* Check the miter limit -- lines meeting at an acute angle @@ -361,13 +529,10 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st * 2 <= ml² (1 - in · out) * */ - if (2 <= ml * ml * (1 - in_dot_out)) - { + if (2 <= ml * ml * (1 - in_dot_out)) { double x1, y1, x2, y2; double mx, my; double dx1, dx2, dy1, dy2; - cairo_point_t outer; - cairo_point_t quad[4]; double ix, iy; double fdx1, fdy1, fdx2, fdy2; double mdx, mdy; @@ -435,74 +600,90 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st if (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) != _cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy)) { - /* - * Draw the quadrilateral - */ - outer.x = _cairo_fixed_from_double (mx); - outer.y = _cairo_fixed_from_double (my); + if (stroker->add_external_edge != NULL) { + points[0].x = _cairo_fixed_from_double (mx); + points[0].y = _cairo_fixed_from_double (my); - quad[0] = in->point; - quad[1] = *inpt; - quad[2] = outer; - quad[3] = *outpt; + if (clockwise) { + status = stroker->add_external_edge (stroker->closure, + inpt, &points[0]); + if (unlikely (status)) + return status; - return _cairo_traps_tessellate_convex_quad (stroker->traps, quad); + status = stroker->add_external_edge (stroker->closure, + &points[0], outpt); + if (unlikely (status)) + return status; + } else { + status = stroker->add_external_edge (stroker->closure, + outpt, &points[0]); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &points[0], inpt); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; + } else { + points[0] = in->point; + points[1] = *inpt; + points[2].x = _cairo_fixed_from_double (mx); + points[2].y = _cairo_fixed_from_double (my); + points[3] = *outpt; + + return stroker->add_convex_quad (stroker->closure, points); + } } } - /* fall through ... */ } - case CAIRO_LINE_JOIN_BEVEL: { - cairo_point_t tri[3]; - tri[0] = in->point; - tri[1] = *inpt; - tri[2] = *outpt; - return _cairo_traps_tessellate_triangle (stroker->traps, tri); - } + /* fall through ... */ + + case CAIRO_LINE_JOIN_BEVEL: + if (stroker->add_external_edge != NULL) { + if (clockwise) { + return stroker->add_external_edge (stroker->closure, + inpt, outpt); + } else { + return stroker->add_external_edge (stroker->closure, + outpt, inpt); + } + } else { + points[0] = in->point; + points[1] = *inpt; + points[2] = *outpt; + + return stroker->add_triangle (stroker->closure, points); + } } } static cairo_status_t -_cairo_stroker_add_cap (cairo_stroker_t *stroker, cairo_stroke_face_t *f) +_cairo_stroker_add_cap (cairo_stroker_t *stroker, + const cairo_stroke_face_t *f) { - cairo_status_t status; - - if (stroker->style->line_cap == CAIRO_LINE_CAP_BUTT) - return CAIRO_STATUS_SUCCESS; - switch (stroker->style->line_cap) { case CAIRO_LINE_CAP_ROUND: { - int i; - int start, stop; cairo_slope_t slope; - cairo_point_t tri[3]; - cairo_pen_t *pen = &stroker->pen; - slope = f->dev_vector; - start = _cairo_pen_find_active_cw_vertex_index (pen, &slope); - slope.dx = -slope.dx; - slope.dy = -slope.dy; - stop = _cairo_pen_find_active_cw_vertex_index (pen, &slope); + slope.dx = -f->dev_vector.dx; + slope.dy = -f->dev_vector.dy; - tri[0] = f->point; - tri[1] = f->cw; - for (i=start; i != stop; i = (i+1) % pen->num_vertices) { - tri[2] = f->point; - _translate_point (&tri[2], &pen->vertices[i].point); - status = _cairo_traps_tessellate_triangle (stroker->traps, tri); - if (unlikely (status)) - return status; - tri[1] = tri[2]; - } - tri[2] = f->ccw; + return _tessellate_fan (stroker, + &f->dev_vector, + &slope, + &f->point, &f->cw, &f->ccw, + FALSE); - return _cairo_traps_tessellate_triangle (stroker->traps, tri); } + case CAIRO_LINE_CAP_SQUARE: { double dx, dy; cairo_slope_t fvector; - cairo_point_t occw, ocw; - cairo_polygon_t polygon; + cairo_point_t quad[4]; dx = f->usr_vector.x; dy = f->usr_vector.y; @@ -511,38 +692,52 @@ _cairo_stroker_add_cap (cairo_stroker_t *stroker, cairo_stroke_face_t *f) cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); fvector.dx = _cairo_fixed_from_double (dx); fvector.dy = _cairo_fixed_from_double (dy); - occw.x = f->ccw.x + fvector.dx; - occw.y = f->ccw.y + fvector.dy; - ocw.x = f->cw.x + fvector.dx; - ocw.y = f->cw.y + fvector.dy; - _cairo_polygon_init (&polygon); - _cairo_polygon_move_to (&polygon, &f->cw); - _cairo_polygon_line_to (&polygon, &ocw); - _cairo_polygon_line_to (&polygon, &occw); - _cairo_polygon_line_to (&polygon, &f->ccw); - _cairo_polygon_close (&polygon); - status = _cairo_polygon_status (&polygon); + quad[0] = f->ccw; + quad[1].x = f->ccw.x + fvector.dx; + quad[1].y = f->ccw.y + fvector.dy; + quad[2].x = f->cw.x + fvector.dx; + quad[2].y = f->cw.y + fvector.dy; + quad[3] = f->cw; - if (status == CAIRO_STATUS_SUCCESS) { - status = _cairo_bentley_ottmann_tessellate_polygon (stroker->traps, - &polygon, - CAIRO_FILL_RULE_WINDING); + if (stroker->add_external_edge != NULL) { + cairo_status_t status; + + status = stroker->add_external_edge (stroker->closure, + &quad[0], &quad[1]); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &quad[1], &quad[2]); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &quad[2], &quad[3]); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; + } else { + return stroker->add_convex_quad (stroker->closure, quad); } - - _cairo_polygon_fini (&polygon); - - return status; } + case CAIRO_LINE_CAP_BUTT: default: - return CAIRO_STATUS_SUCCESS; + if (stroker->add_external_edge != NULL) { + return stroker->add_external_edge (stroker->closure, + &f->ccw, &f->cw); + } else { + return CAIRO_STATUS_SUCCESS; + } } } static cairo_status_t _cairo_stroker_add_leading_cap (cairo_stroker_t *stroker, - cairo_stroke_face_t *face) + const cairo_stroke_face_t *face) { cairo_stroke_face_t reversed; cairo_point_t t; @@ -563,7 +758,7 @@ _cairo_stroker_add_leading_cap (cairo_stroker_t *stroker, static cairo_status_t _cairo_stroker_add_trailing_cap (cairo_stroker_t *stroker, - cairo_stroke_face_t *face) + const cairo_stroke_face_t *face) { return _cairo_stroker_add_cap (stroker, face); } @@ -614,55 +809,6 @@ _compute_normalized_device_slope (double *dx, double *dy, return TRUE; } -static void -_compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope, - double slope_dx, double slope_dy, - cairo_stroker_t *stroker, cairo_stroke_face_t *face); - -static cairo_status_t -_cairo_stroker_add_caps (cairo_stroker_t *stroker) -{ - cairo_status_t status; - /* check for a degenerative sub_path */ - if (stroker->has_initial_sub_path - && !stroker->has_first_face - && !stroker->has_current_face - && stroker->style->line_cap == CAIRO_LINE_JOIN_ROUND) - { - /* pick an arbitrary slope to use */ - double dx = 1.0, dy = 0.0; - cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 }; - cairo_stroke_face_t face; - - _compute_normalized_device_slope (&dx, &dy, stroker->ctm_inverse, NULL); - - /* arbitrarily choose first_point - * first_point and current_point should be the same */ - _compute_face (&stroker->first_point, &slope, dx, dy, stroker, &face); - - status = _cairo_stroker_add_leading_cap (stroker, &face); - if (unlikely (status)) - return status; - status = _cairo_stroker_add_trailing_cap (stroker, &face); - if (unlikely (status)) - return status; - } - - if (stroker->has_first_face) { - status = _cairo_stroker_add_leading_cap (stroker, &stroker->first_face); - if (unlikely (status)) - return status; - } - - if (stroker->has_current_face) { - status = _cairo_stroker_add_trailing_cap (stroker, &stroker->current_face); - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - static void _compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope, double slope_dx, double slope_dy, @@ -711,6 +857,55 @@ _compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope, face->dev_vector = *dev_slope; } +static cairo_status_t +_cairo_stroker_add_caps (cairo_stroker_t *stroker) +{ + cairo_status_t status; + + /* check for a degenerative sub_path */ + if (stroker->has_initial_sub_path + && ! stroker->has_first_face + && ! stroker->has_current_face + && stroker->style->line_cap == CAIRO_LINE_JOIN_ROUND) + { + /* pick an arbitrary slope to use */ + double dx = 1.0, dy = 0.0; + cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 }; + cairo_stroke_face_t face; + + _compute_normalized_device_slope (&dx, &dy, + stroker->ctm_inverse, NULL); + + /* arbitrarily choose first_point + * first_point and current_point should be the same */ + _compute_face (&stroker->first_point, &slope, dx, dy, stroker, &face); + + status = _cairo_stroker_add_leading_cap (stroker, &face); + if (unlikely (status)) + return status; + + status = _cairo_stroker_add_trailing_cap (stroker, &face); + if (unlikely (status)) + return status; + } + + if (stroker->has_first_face) { + status = _cairo_stroker_add_leading_cap (stroker, + &stroker->first_face); + if (unlikely (status)) + return status; + } + + if (stroker->has_current_face) { + status = _cairo_stroker_add_trailing_cap (stroker, + &stroker->current_face); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + static cairo_status_t _cairo_stroker_add_sub_edge (cairo_stroker_t *stroker, const cairo_point_t *p1, @@ -720,24 +915,42 @@ _cairo_stroker_add_sub_edge (cairo_stroker_t *stroker, cairo_stroke_face_t *start, cairo_stroke_face_t *end) { - cairo_point_t rectangle[4]; - _compute_face (p1, dev_slope, slope_dx, slope_dy, stroker, start); - - /* XXX: This could be optimized slightly by not calling - _compute_face again but rather translating the relevant - fields from start. */ - _compute_face (p2, dev_slope, slope_dx, slope_dy, stroker, end); + *end = *start; if (p1->x == p2->x && p1->y == p2->y) return CAIRO_STATUS_SUCCESS; - rectangle[0] = start->cw; - rectangle[1] = start->ccw; - rectangle[2] = end->ccw; - rectangle[3] = end->cw; + end->point = *p2; + end->ccw.x += p2->x - p1->x; + end->ccw.y += p2->y - p1->y; + end->cw.x += p2->x - p1->x; + end->cw.y += p2->y - p1->y; - return _cairo_traps_tessellate_convex_quad (stroker->traps, rectangle); + if (stroker->add_external_edge != NULL) { + cairo_status_t status; + + status = stroker->add_external_edge (stroker->closure, + &end->cw, &start->cw); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &start->ccw, &end->ccw); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; + } else { + cairo_point_t quad[4]; + + quad[0] = start->cw; + quad[1] = end->cw; + quad[2] = end->ccw; + quad[3] = start->ccw; + + return stroker->add_convex_quad (stroker->closure, quad); + } } static cairo_status_t @@ -747,6 +960,9 @@ _cairo_stroker_move_to (void *closure, cairo_stroker_t *stroker = closure; cairo_status_t status; + /* reset the dash pattern for new sub paths */ + _cairo_stroker_dash_start (&stroker->dash); + /* Cap the start and end of the previous sub path as needed */ status = _cairo_stroker_add_caps (stroker); if (unlikely (status)) @@ -762,41 +978,30 @@ _cairo_stroker_move_to (void *closure, return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_stroker_move_to_dashed (void *closure, - const cairo_point_t *point) -{ - cairo_stroker_t *stroker = closure; - - /* reset the dash pattern for new sub paths */ - _cairo_stroker_dash_start (&stroker->dash); - - return _cairo_stroker_move_to (closure, point); -} - static cairo_status_t _cairo_stroker_line_to (void *closure, - const cairo_point_t *p2) + const cairo_point_t *point) { - cairo_status_t status; cairo_stroker_t *stroker = closure; cairo_stroke_face_t start, end; cairo_point_t *p1 = &stroker->current_point; cairo_slope_t dev_slope; double slope_dx, slope_dy; + cairo_status_t status; stroker->has_initial_sub_path = TRUE; - if (p1->x == p2->x && p1->y == p2->y) + if (p1->x == point->x && p1->y == point->y) return CAIRO_STATUS_SUCCESS; - _cairo_slope_init (&dev_slope, p1, p2); - slope_dx = _cairo_fixed_to_double (p2->x - p1->x); - slope_dy = _cairo_fixed_to_double (p2->y - p1->y); - _compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, NULL); + _cairo_slope_init (&dev_slope, p1, point); + slope_dx = _cairo_fixed_to_double (point->x - p1->x); + slope_dy = _cairo_fixed_to_double (point->y - p1->y); + _compute_normalized_device_slope (&slope_dx, &slope_dy, + stroker->ctm_inverse, NULL); status = _cairo_stroker_add_sub_edge (stroker, - p1, p2, + p1, point, &dev_slope, slope_dx, slope_dy, &start, &end); @@ -805,10 +1010,12 @@ _cairo_stroker_line_to (void *closure, if (stroker->has_current_face) { /* Join with final face from previous segment */ - status = _cairo_stroker_join (stroker, &stroker->current_face, &start); + status = _cairo_stroker_join (stroker, + &stroker->current_face, + &start); if (unlikely (status)) return status; - } else if (!stroker->has_first_face) { + } else if (! stroker->has_first_face) { /* Save sub path's first face in case needed for closing join */ stroker->first_face = start; stroker->has_first_face = TRUE; @@ -816,7 +1023,7 @@ _cairo_stroker_line_to (void *closure, stroker->current_face = end; stroker->has_current_face = TRUE; - stroker->current_point = *p2; + stroker->current_point = *point; return CAIRO_STATUS_SUCCESS; } @@ -858,7 +1065,7 @@ _cairo_stroker_line_to_dashed (void *closure, slope_dy = _cairo_fixed_to_double (p2->y - p1->y); if (! _compute_normalized_device_slope (&slope_dx, &slope_dy, - stroker->ctm_inverse, &mag)) + stroker->ctm_inverse, &mag)) { return CAIRO_STATUS_SUCCESS; } @@ -971,123 +1178,24 @@ _cairo_stroker_curve_to (void *closure, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) -{ - cairo_stroker_t *stroker = closure; - cairo_pen_stroke_spline_t spline_pen; - cairo_stroke_face_t start, end; - cairo_point_t extra_points[4]; - cairo_point_t *a = &stroker->current_point; - double initial_slope_dx, initial_slope_dy; - double final_slope_dx, final_slope_dy; - cairo_status_t status; - - status = _cairo_pen_stroke_spline_init (&spline_pen, - &stroker->pen, - a, b, c, d); - if (status == CAIRO_INT_STATUS_DEGENERATE) - return _cairo_stroker_line_to (closure, d); - else if (unlikely (status)) - return status; - - initial_slope_dx = _cairo_fixed_to_double (spline_pen.spline.initial_slope.dx); - initial_slope_dy = _cairo_fixed_to_double (spline_pen.spline.initial_slope.dy); - final_slope_dx = _cairo_fixed_to_double (spline_pen.spline.final_slope.dx); - final_slope_dy = _cairo_fixed_to_double (spline_pen.spline.final_slope.dy); - - if (_compute_normalized_device_slope (&initial_slope_dx, &initial_slope_dy, - stroker->ctm_inverse, NULL)) - { - _compute_face (a, - &spline_pen.spline.initial_slope, - initial_slope_dx, initial_slope_dy, - stroker, &start); - } - - if (_compute_normalized_device_slope (&final_slope_dx, &final_slope_dy, - stroker->ctm_inverse, NULL)) - { - _compute_face (d, - &spline_pen.spline.final_slope, - final_slope_dx, final_slope_dy, - stroker, &end); - } - - if (stroker->has_current_face) { - status = _cairo_stroker_join (stroker, &stroker->current_face, &start); - if (unlikely (status)) - goto CLEANUP_PEN; - } else if (! stroker->has_first_face) { - stroker->first_face = start; - stroker->has_first_face = TRUE; - } - stroker->current_face = end; - stroker->has_current_face = TRUE; - - extra_points[0] = start.cw; - extra_points[0].x -= start.point.x; - extra_points[0].y -= start.point.y; - extra_points[1] = start.ccw; - extra_points[1].x -= start.point.x; - extra_points[1].y -= start.point.y; - extra_points[2] = end.cw; - extra_points[2].x -= end.point.x; - extra_points[2].y -= end.point.y; - extra_points[3] = end.ccw; - extra_points[3].x -= end.point.x; - extra_points[3].y -= end.point.y; - - status = _cairo_pen_add_points (&spline_pen.pen, extra_points, 4); - if (unlikely (status)) - goto CLEANUP_PEN; - - status = _cairo_pen_stroke_spline (&spline_pen, - stroker->tolerance, - stroker->traps); - - CLEANUP_PEN: - _cairo_pen_stroke_spline_fini (&spline_pen); - - stroker->current_point = *d; - - return status; -} - -/* We're using two different algorithms here for dashed and un-dashed - * splines. The dashed algorithm uses the existing line dashing - * code. It's linear in path length, but gets subtly wrong results for - * self-intersecting paths (an outstanding but for self-intersecting - * non-curved paths as well). The non-dashed algorithm tessellates a - * single polygon for the whole curve. It handles the - * self-intersecting problem, but it's (unsurprisingly) not O(n) and - * more significantly, it doesn't yet handle dashes. - * - * The only reason we're doing split algorithms here is to - * minimize the impact of fixing the splines-aren't-dashed bug for - * 1.0.2. Long-term the right answer is to rewrite the whole pile - * of stroking code so that the entire result is computed as a - * single polygon that is tessellated, (that is, stroking can be - * built on top of filling). That will solve the self-intersecting - * problem. It will also increase the importance of implementing - * an efficient and more robust tessellator. - */ -static cairo_status_t -_cairo_stroker_curve_to_dashed (void *closure, - const cairo_point_t *b, - const cairo_point_t *c, - const cairo_point_t *d) { cairo_stroker_t *stroker = closure; cairo_spline_t spline; - cairo_point_t *a = &stroker->current_point; cairo_line_join_t line_join_save; - cairo_status_t status; + cairo_stroke_face_t face; + double slope_dx, slope_dy; + cairo_path_fixed_line_to_func_t *line_to; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + line_to = stroker->dash.dashed ? + _cairo_stroker_line_to_dashed : + _cairo_stroker_line_to; if (! _cairo_spline_init (&spline, - _cairo_stroker_line_to_dashed, - stroker, - a, b, c, d)) + line_to, stroker, + &stroker->current_point, b, c, d)) { - return _cairo_stroker_line_to_dashed (closure, d); + return line_to (closure, d); } /* If the line width is so small that the pen is reduced to a @@ -1095,23 +1203,71 @@ _cairo_stroker_curve_to_dashed (void *closure, if (stroker->pen.num_vertices <= 1) return CAIRO_STATUS_SUCCESS; + /* Compute the initial face */ + if (! stroker->dash.dashed || stroker->dash.dash_on) { + slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx); + slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy); + if (_compute_normalized_device_slope (&slope_dx, &slope_dy, + stroker->ctm_inverse, NULL)) + { + _compute_face (&stroker->current_point, + &spline.initial_slope, + slope_dx, slope_dy, + stroker, &face); + } + if (stroker->has_current_face) { + status = _cairo_stroker_join (stroker, + &stroker->current_face, &face); + if (unlikely (status)) + return status; + } else if (! stroker->has_first_face) { + stroker->first_face = face; + stroker->has_first_face = TRUE; + } + + stroker->current_face = face; + stroker->has_current_face = TRUE; + } + /* Temporarily modify the stroker to use round joins to guarantee * smooth stroked curves. */ line_join_save = stroker->style->line_join; stroker->style->line_join = CAIRO_LINE_JOIN_ROUND; status = _cairo_spline_decompose (&spline, stroker->tolerance); + if (unlikely (status)) + return status; + + /* And join the final face */ + if (! stroker->dash.dashed || stroker->dash.dash_on) { + slope_dx = _cairo_fixed_to_double (spline.final_slope.dx); + slope_dy = _cairo_fixed_to_double (spline.final_slope.dy); + if (_compute_normalized_device_slope (&slope_dx, &slope_dy, + stroker->ctm_inverse, NULL)) + { + _compute_face (&stroker->current_point, + &spline.final_slope, + slope_dx, slope_dy, + stroker, &face); + } + + status = _cairo_stroker_join (stroker, &stroker->current_face, &face); + if (unlikely (status)) + return status; + + stroker->current_face = face; + } stroker->style->line_join = line_join_save; - return status; + return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_stroker_close_path (void *closure) { - cairo_status_t status; cairo_stroker_t *stroker = closure; + cairo_status_t status; if (stroker->dash.dashed) status = _cairo_stroker_line_to_dashed (stroker, &stroker->first_point); @@ -1122,7 +1278,9 @@ _cairo_stroker_close_path (void *closure) if (stroker->has_first_face && stroker->has_current_face) { /* Join first and final faces of sub path */ - status = _cairo_stroker_join (stroker, &stroker->current_face, &stroker->first_face); + status = _cairo_stroker_join (stroker, + &stroker->current_face, + &stroker->first_face); if (unlikely (status)) return status; } else { @@ -1139,57 +1297,45 @@ _cairo_stroker_close_path (void *closure) return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t *path, - cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - cairo_traps_t *traps); - cairo_status_t -_cairo_path_fixed_stroke_to_traps (cairo_path_fixed_t *path, - cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_traps_t *traps) +_cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t *path, + cairo_stroke_style_t *stroke_style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_status_t (*add_triangle) (void *closure, + const cairo_point_t triangle[3]), + cairo_status_t (*add_triangle_fan) (void *closure, + const cairo_point_t *midpt, + const cairo_point_t *points, + int npoints), + cairo_status_t (*add_convex_quad) (void *closure, + const cairo_point_t quad[4]), + void *closure) { - cairo_status_t status; cairo_stroker_t stroker; - - /* Before we do anything else, we attempt the rectilinear - * stroker. It's careful to generate trapezoids that align to - * device-pixel boundaries when possible. Many backends can render - * those much faster than non-aligned trapezoids, (by using clip - * regions, etc.) */ - status = _cairo_path_fixed_stroke_rectilinear (path, - stroke_style, - ctm, - traps); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; + cairo_status_t status; status = _cairo_stroker_init (&stroker, stroke_style, - ctm, ctm_inverse, tolerance, - traps); + ctm, ctm_inverse, tolerance); if (unlikely (status)) return status; - if (stroker.style->dash) - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_stroker_move_to_dashed, - _cairo_stroker_line_to_dashed, - _cairo_stroker_curve_to_dashed, - _cairo_stroker_close_path, - &stroker); - else - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_stroker_move_to, - _cairo_stroker_line_to, - _cairo_stroker_curve_to, - _cairo_stroker_close_path, - &stroker); + stroker.add_triangle = add_triangle; + stroker.add_triangle_fan = add_triangle_fan; + stroker.add_convex_quad = add_convex_quad; + stroker.closure = closure; + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_stroker_move_to, + stroker.dash.dashed ? + _cairo_stroker_line_to_dashed : + _cairo_stroker_line_to, + _cairo_stroker_curve_to, + _cairo_stroker_close_path, + &stroker); + if (unlikely (status)) goto BAIL; @@ -1202,6 +1348,100 @@ BAIL: return status; } +cairo_status_t +_cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path, + cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_polygon_t *polygon) +{ + cairo_stroker_t stroker; + cairo_status_t status; + + status = _cairo_stroker_init (&stroker, stroke_style, + ctm, ctm_inverse, tolerance); + if (unlikely (status)) + return status; + + stroker.add_external_edge = _cairo_polygon_add_external_edge, + stroker.closure = polygon; + + if (polygon->num_limits) + _cairo_stroker_limit (&stroker, polygon->limits, polygon->num_limits); + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_stroker_move_to, + stroker.dash.dashed ? + _cairo_stroker_line_to_dashed : + _cairo_stroker_line_to, + _cairo_stroker_curve_to, + _cairo_stroker_close_path, + &stroker); + + if (unlikely (status)) + goto BAIL; + + /* Cap the start and end of the final sub path as needed */ + status = _cairo_stroker_add_caps (&stroker); + +BAIL: + _cairo_stroker_fini (&stroker); + + return status; +} + +cairo_status_t +_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path, + cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_traps_t *traps) +{ + cairo_status_t status; + cairo_polygon_t polygon; + + /* Before we do anything else, we attempt the rectilinear + * stroker. It's careful to generate trapezoids that align to + * device-pixel boundaries when possible. Many backends can render + * those much faster than non-aligned trapezoids, (by using clip + * regions, etc.) */ + if (path->is_rectilinear) { + status = _cairo_path_fixed_stroke_rectilinear_to_traps (path, + stroke_style, + ctm, + traps); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + _cairo_polygon_init (&polygon); + _cairo_polygon_limit (&polygon, traps->limits, traps->num_limits); + + status = _cairo_path_fixed_stroke_to_polygon (path, + stroke_style, + ctm, + ctm_inverse, + tolerance, + &polygon); + if (unlikely (status)) + goto BAIL; + + status = _cairo_polygon_status (&polygon); + if (unlikely (status)) + goto BAIL; + + status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon, + CAIRO_FILL_RULE_WINDING); + +BAIL: + _cairo_polygon_fini (&polygon); + + return status; +} + typedef struct _segment_t { cairo_point_t p1, p2; cairo_bool_t is_horizontal; @@ -1231,10 +1471,11 @@ typedef struct _cairo_rectilinear_stroker { static void _cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker, - const cairo_box_t *box) + const cairo_box_t *boxes, + int num_boxes) { stroker->has_bounds = TRUE; - stroker->bounds = *box; + _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds); stroker->bounds.p1.x -= stroker->half_line_width; stroker->bounds.p2.x += stroker->half_line_width; @@ -1260,7 +1501,8 @@ _cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker, stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded); stroker->num_segments = 0; - _cairo_stroker_dash_init (&stroker->dash, stroke_style); + /* Assume 2*EPSILON tolerance */ + _cairo_stroker_dash_init (&stroker->dash, stroke_style, ctm, _cairo_fixed_to_double (2 * CAIRO_FIXED_EPSILON)); stroker->has_bounds = FALSE; } @@ -1583,8 +1825,7 @@ _cairo_rectilinear_stroker_line_to (void *closure, cairo_status_t status; /* We only support horizontal or vertical elements. */ - if (! (a->x == b->x || a->y == b->y)) - return CAIRO_INT_STATUS_UNSUPPORTED; + assert (a->x == b->x || a->y == b->y); /* We don't draw anything for degenerate paths. */ if (a->x == b->x && a->y == b->y) @@ -1620,8 +1861,7 @@ _cairo_rectilinear_stroker_line_to_dashed (void *closure, return CAIRO_STATUS_SUCCESS; /* We only support horizontal or vertical elements. */ - if (! (a->x == b->x || a->y == b->y)) - return CAIRO_INT_STATUS_UNSUPPORTED; + assert (a->x == b->x || a->y == b->y); fully_in_bounds = TRUE; if (stroker->has_bounds && @@ -1736,11 +1976,11 @@ _cairo_rectilinear_stroker_close_path (void *closure) return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t *path, - cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - cairo_traps_t *traps) +cairo_int_status_t +_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path, + cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + cairo_traps_t *traps) { cairo_rectilinear_stroker_t rectilinear_stroker; cairo_int_status_t status; @@ -1755,8 +1995,8 @@ _cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t *path, * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any * non-rectilinear line_to is encountered. */ - if (path->has_curve_to) - return CAIRO_INT_STATUS_UNSUPPORTED; + assert (path->is_rectilinear); + if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER) return CAIRO_INT_STATUS_UNSUPPORTED; /* If the miter limit turns right angles into bevels, then we @@ -1770,19 +2010,17 @@ _cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t *path, { return CAIRO_INT_STATUS_UNSUPPORTED; } - if (! (_cairo_matrix_is_identity (ctm) || - _cairo_matrix_is_translation (ctm))) - { + if (! _cairo_matrix_has_unity_scale (ctm)) return CAIRO_INT_STATUS_UNSUPPORTED; - } _cairo_rectilinear_stroker_init (&rectilinear_stroker, stroke_style, ctm, traps); - if (traps->has_limits) { + if (traps->num_limits) { _cairo_rectilinear_stroker_limit (&rectilinear_stroker, - &traps->limits); + traps->limits, + traps->num_limits); } status = _cairo_path_fixed_interpret (path, @@ -1802,6 +2040,10 @@ _cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t *path, else status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker); + traps->is_rectilinear = 1; + traps->is_rectangular = 1; + /* As we incrementally tessellate, we do not eliminate self-intersections */ + traps->has_intersections = traps->num_traps > 1; BAIL: _cairo_rectilinear_stroker_fini (&rectilinear_stroker); diff --git a/gfx/cairo/cairo/src/cairo-pattern.c b/gfx/cairo/cairo/src/cairo-pattern.c index 9ea959b2a87b..e988b367ed55 100644 --- a/gfx/cairo/cairo/src/cairo-pattern.c +++ b/gfx/cairo/cairo/src/cairo-pattern.c @@ -129,6 +129,8 @@ _cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type) pattern->filter = CAIRO_FILTER_DEFAULT; + pattern->has_component_alpha = FALSE; + cairo_matrix_init_identity (&pattern->matrix); } @@ -331,7 +333,7 @@ void _cairo_pattern_fini_snapshot (cairo_pattern_t *pattern) { /* XXX this is quite ugly, but currently necessary to break the circular - * references with snapshot-cow and the meta-surface. + * references with snapshot-cow and the recording-surface. * This operation remains safe only whilst _cairo_surface_snapshot() is * not public... */ @@ -486,7 +488,7 @@ _freed_pattern_get (freed_pool_t *pool) i = 0; pattern = _atomic_fetch (&pool->pool[i]); - if (pattern != NULL) { + if (likely (pattern != NULL)) { pool->top = i; return pattern; } @@ -511,7 +513,9 @@ _freed_pattern_put (freed_pool_t *pool, { int i = pool->top; - if (_atomic_store (&pool->pool[i], pattern)) { + if (likely (i < ARRAY_LENGTH (pool->pool) && + _atomic_store (&pool->pool[i], pattern))) + { pool->top = i + 1; return; } @@ -547,7 +551,7 @@ cairo_pattern_t * _cairo_pattern_create_solid (const cairo_color_t *color, cairo_content_t content) { - cairo_solid_pattern_t *pattern = NULL; + cairo_solid_pattern_t *pattern; pattern = _freed_pattern_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_SOLID]); @@ -868,7 +872,6 @@ cairo_pattern_status (cairo_pattern_t *pattern) { return pattern->status; } -slim_hidden_def (cairo_pattern_status); /** * cairo_pattern_destroy: @@ -1521,6 +1524,7 @@ _cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pat attr->matrix = matrix; attr->extend = pattern->base.extend; attr->filter = CAIRO_FILTER_NEAREST; + attr->has_component_alpha = pattern->base.has_component_alpha; *out = &image->base; @@ -1602,9 +1606,6 @@ _cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pat _cairo_debug_check_image_surface_is_defined (&image->base); status = _cairo_surface_clone_similar (dst, &image->base, - opaque ? - CAIRO_CONTENT_COLOR : - CAIRO_CONTENT_COLOR_ALPHA, 0, 0, width, height, &clone_offset_x, &clone_offset_y, @@ -1617,6 +1618,7 @@ _cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pat cairo_matrix_init_identity (&attr->matrix); attr->extend = repeat ? CAIRO_EXTEND_REPEAT : CAIRO_EXTEND_NONE; attr->filter = CAIRO_FILTER_NEAREST; + attr->has_component_alpha = pattern->base.has_component_alpha; return status; } @@ -1686,10 +1688,6 @@ _cairo_pattern_acquire_surface_for_solid (const cairo_solid_pattern_t *patt pattern, dst)) { - status = _cairo_surface_reset (solid_surface_cache.cache[i].surface); - if (unlikely (status)) - goto UNLOCK; - goto DONE; } @@ -1698,10 +1696,6 @@ _cairo_pattern_acquire_surface_for_solid (const cairo_solid_pattern_t *patt pattern, dst)) { - status = _cairo_surface_reset (solid_surface_cache.cache[i].surface); - if (unlikely (status)) - goto UNLOCK; - goto DONE; } } @@ -1717,11 +1711,6 @@ _cairo_pattern_acquire_surface_for_solid (const cairo_solid_pattern_t *patt dst)) { /* Reuse the surface instead of evicting */ - - status = _cairo_surface_reset (surface); - if (unlikely (status)) - goto EVICT; - status = _cairo_surface_repaint_solid_pattern_surface (dst, surface, pattern); if (unlikely (status)) goto EVICT; @@ -1738,14 +1727,21 @@ _cairo_pattern_acquire_surface_for_solid (const cairo_solid_pattern_t *patt if (surface == NULL) { /* Not cached, need to create new */ surface = _cairo_surface_create_solid_pattern_surface (dst, pattern); - if (surface->status) { + if (surface == NULL) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto UNLOCK; + } + if (unlikely (surface->status)) { status = surface->status; goto UNLOCK; } - if (! _cairo_surface_is_similar (surface, dst, pattern->content)) { - /* in the rare event of a substitute surface being returned (e.g. - * malloc failure) don't cache the fallback surface */ + if (unlikely (! _cairo_surface_is_similar (surface, + dst, pattern->content))) + { + /* In the rare event of a substitute surface being returned, + * don't cache the fallback. + */ *out = surface; goto NOCACHE; } @@ -1767,6 +1763,7 @@ NOCACHE: cairo_matrix_init_identity (&attribs->matrix); attribs->extend = CAIRO_EXTEND_REPEAT; attribs->filter = CAIRO_FILTER_NEAREST; + attribs->has_component_alpha = pattern->base.has_component_alpha; status = CAIRO_STATUS_SUCCESS; @@ -1853,6 +1850,9 @@ _cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern) { const cairo_pattern_union_t *pattern; + if (abstract_pattern->has_component_alpha) + return FALSE; + pattern = (cairo_pattern_union_t *) abstract_pattern; switch (pattern->base.type) { case CAIRO_PATTERN_TYPE_SOLID: @@ -1883,13 +1883,13 @@ _cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern) * backends do currently (see bug #10508) */ static cairo_filter_t -_cairo_pattern_analyze_filter (const cairo_surface_pattern_t *pattern, - double *pad_out) +_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern, + double *pad_out) { double pad; cairo_filter_t optimized_filter; - switch (pattern->base.filter) { + switch (pattern->filter) { case CAIRO_FILTER_GOOD: case CAIRO_FILTER_BEST: case CAIRO_FILTER_BILINEAR: @@ -1897,7 +1897,7 @@ _cairo_pattern_analyze_filter (const cairo_surface_pattern_t *pattern, * not need to filter (and do not want to filter, since it * will cause blurriness) */ - if (_cairo_matrix_is_pixel_exact (&pattern->base.matrix)) { + if (_cairo_matrix_is_pixel_exact (&pattern->matrix)) { pad = 0.; optimized_filter = CAIRO_FILTER_NEAREST; } else { @@ -1907,7 +1907,7 @@ _cairo_pattern_analyze_filter (const cairo_surface_pattern_t *pattern, * more would be defensive... */ pad = 0.5; - optimized_filter = pattern->base.filter; + optimized_filter = pattern->filter; } break; @@ -1916,7 +1916,7 @@ _cairo_pattern_analyze_filter (const cairo_surface_pattern_t *pattern, case CAIRO_FILTER_GAUSSIAN: default: pad = 0.; - optimized_filter = pattern->base.filter; + optimized_filter = pattern->filter; break; } @@ -1936,7 +1936,6 @@ _pixman_nearest_sample (double d) static cairo_int_status_t _cairo_pattern_acquire_surface_for_surface (const cairo_surface_pattern_t *pattern, cairo_surface_t *dst, - cairo_content_t content, int x, int y, unsigned int width, @@ -1953,6 +1952,7 @@ _cairo_pattern_acquire_surface_for_surface (const cairo_surface_pattern_t *pat double pad; cairo_bool_t is_identity; cairo_bool_t is_empty; + cairo_bool_t is_bounded; cairo_int_status_t status; surface = cairo_surface_reference (pattern->surface); @@ -1960,7 +1960,8 @@ _cairo_pattern_acquire_surface_for_surface (const cairo_surface_pattern_t *pat is_identity = FALSE; attr->matrix = pattern->base.matrix; attr->extend = pattern->base.extend; - attr->filter = _cairo_pattern_analyze_filter (pattern, &pad); + attr->filter = _cairo_pattern_analyze_filter (&pattern->base, &pad); + attr->has_component_alpha = pattern->base.has_component_alpha; attr->x_offset = attr->y_offset = tx = ty = 0; if (_cairo_matrix_is_integer_translation (&attr->matrix, &tx, &ty)) { @@ -2010,11 +2011,10 @@ _cairo_pattern_acquire_surface_for_surface (const cairo_surface_pattern_t *pat cairo_surface_t *src; int w, h; - status = _cairo_surface_get_extents (surface, &extents); - if (unlikely (status)) - goto BAIL; + is_bounded = _cairo_surface_get_extents (surface, &extents); + assert (is_bounded); - status = _cairo_surface_clone_similar (dst, surface, content, + status = _cairo_surface_clone_similar (dst, surface, extents.x, extents.y, extents.width, extents.height, &extents.x, &extents.y, &src); @@ -2045,8 +2045,13 @@ _cairo_pattern_acquire_surface_for_surface (const cairo_surface_pattern_t *pat } cairo_surface_destroy (surface); - surface = cairo_surface_create_similar (dst, dst->content, w, h); - if (surface->status) { + surface = _cairo_surface_create_similar_solid (dst, + dst->content, w, h, + CAIRO_COLOR_TRANSPARENT, + FALSE); + if (surface == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (unlikely (surface->status)) { cairo_surface_destroy (src); return surface->status; } @@ -2092,10 +2097,6 @@ _cairo_pattern_acquire_surface_for_surface (const cairo_surface_pattern_t *pat attr->extend = CAIRO_EXTEND_REPEAT; } - status = _cairo_surface_get_extents (surface, &extents); - if (unlikely (status)) - goto BAIL; - /* We first transform the rectangle to the coordinate space of the * source surface so that we only need to clone that portion of the * surface that will be read. @@ -2118,41 +2119,43 @@ _cairo_pattern_acquire_surface_for_surface (const cairo_surface_pattern_t *pat sampled_area.x += tx; sampled_area.y += ty; - if (attr->extend != CAIRO_EXTEND_REPEAT) { - /* Never acquire a larger area than the source itself */ - is_empty = _cairo_rectangle_intersect (&extents, &sampled_area); - } else { - int trim = 0; + if ( _cairo_surface_get_extents (surface, &extents)) { + if (attr->extend == CAIRO_EXTEND_NONE) { + /* Never acquire a larger area than the source itself */ + is_empty = _cairo_rectangle_intersect (&extents, &sampled_area); + } else { + int trim = 0; - if (sampled_area.x >= extents.x && - sampled_area.x + (int) sampled_area.width <= extents.x + (int) extents.width) - { - /* source is horizontally contained within extents, trim */ - extents.x = sampled_area.x; - extents.width = sampled_area.width; - trim |= 0x1; + if (sampled_area.x >= extents.x && + sampled_area.x + (int) sampled_area.width <= extents.x + (int) extents.width) + { + /* source is horizontally contained within extents, trim */ + extents.x = sampled_area.x; + extents.width = sampled_area.width; + trim |= 0x1; + } + + if (sampled_area.y >= extents.y && + sampled_area.y + (int) sampled_area.height <= extents.y + (int) extents.height) + { + /* source is vertically contained within extents, trim */ + extents.y = sampled_area.y; + extents.height = sampled_area.height; + trim |= 0x2; + } + + if (trim == 0x3) { + /* source is wholly contained within extents, drop the REPEAT */ + attr->extend = CAIRO_EXTEND_NONE; + } + + is_empty = extents.width == 0 || extents.height == 0; } - - if (sampled_area.y >= extents.y && - sampled_area.y + (int) sampled_area.height <= extents.y + (int) extents.height) - { - /* source is vertically contained within extents, trim */ - extents.y = sampled_area.y; - extents.height = sampled_area.height; - trim |= 0x2; - } - - if (trim == 0x3) { - /* source is wholly contained within extents, drop the REPEAT */ - attr->extend = CAIRO_EXTEND_NONE; - } - - is_empty = extents.width == 0 || extents.height == 0; } /* XXX can we use is_empty? */ - status = _cairo_surface_clone_similar (dst, surface, content, + status = _cairo_surface_clone_similar (dst, surface, extents.x, extents.y, extents.width, extents.height, &x, &y, out); @@ -2203,6 +2206,21 @@ _cairo_pattern_acquire_surface_for_surface (const cairo_surface_pattern_t *pat return status; } +static void +_init_solid_for_color_stop (cairo_solid_pattern_t *solid, + const cairo_color_t *color) +{ + cairo_color_t premult; + + /* Color stops aren't premultiplied, so fix that here */ + _cairo_color_init_rgba (&premult, + color->red, + color->green, + color->blue, + color->alpha); + _cairo_pattern_init_solid (solid, &premult, CAIRO_CONTENT_COLOR_ALPHA); +} + /** * _cairo_pattern_acquire_surface: * @pattern: a #cairo_pattern_t @@ -2226,7 +2244,6 @@ _cairo_pattern_acquire_surface_for_surface (const cairo_surface_pattern_t *pat cairo_int_status_t _cairo_pattern_acquire_surface (const cairo_pattern_t *pattern, cairo_surface_t *dst, - cairo_content_t content, int x, int y, unsigned int width, @@ -2237,7 +2254,7 @@ _cairo_pattern_acquire_surface (const cairo_pattern_t *pattern, { cairo_status_t status; - if (pattern->status) { + if (unlikely (pattern->status)) { *surface_out = NULL; return pattern->status; } @@ -2255,28 +2272,16 @@ _cairo_pattern_acquire_surface (const cairo_pattern_t *pattern, case CAIRO_PATTERN_TYPE_RADIAL: { cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) pattern; + /* XXX The gradient->solid conversion code should now be redundant. */ + /* fast path for gradients with less than 2 color stops */ if (src->n_stops < 2) { cairo_solid_pattern_t solid; - if (src->n_stops) - { - cairo_color_t color; - - /* multiply by alpha */ - _cairo_color_init_rgba (&color, - src->stops->color.red, - src->stops->color.green, - src->stops->color.blue, - src->stops->color.alpha); - - _cairo_pattern_init_solid (&solid, - &color, - CAIRO_CONTENT_COLOR_ALPHA); - } - else - { + if (src->n_stops) { + _init_solid_for_color_stop (&solid, &src->stops->color); + } else { _cairo_pattern_init_solid (&solid, CAIRO_COLOR_TRANSPARENT, CAIRO_CONTENT_ALPHA); @@ -2304,18 +2309,8 @@ _cairo_pattern_acquire_surface (const cairo_pattern_t *pattern, } if (i == src->n_stops) { cairo_solid_pattern_t solid; - cairo_color_t color; - /* multiply by alpha */ - _cairo_color_init_rgba (&color, - src->stops->color.red, - src->stops->color.green, - src->stops->color.blue, - src->stops->color.alpha); - - _cairo_pattern_init_solid (&solid, - &color, - CAIRO_CONTENT_COLOR_ALPHA); + _init_solid_for_color_stop (&solid, &src->stops->color); status = _cairo_pattern_acquire_surface_for_solid (&solid, dst, @@ -2337,7 +2332,6 @@ _cairo_pattern_acquire_surface (const cairo_pattern_t *pattern, cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) pattern; status = _cairo_pattern_acquire_surface_for_surface (src, dst, - content, x, y, width, height, flags, @@ -2372,7 +2366,6 @@ cairo_int_status_t _cairo_pattern_acquire_surfaces (const cairo_pattern_t *src, const cairo_pattern_t *mask, cairo_surface_t *dst, - cairo_content_t src_content, int src_x, int src_y, int mask_x, @@ -2388,19 +2381,18 @@ _cairo_pattern_acquire_surfaces (const cairo_pattern_t *src, cairo_int_status_t status; cairo_pattern_union_t src_tmp; - if (src->status) + if (unlikely (src->status)) return src->status; - if (mask && mask->status) + if (unlikely (mask != NULL && mask->status)) return mask->status; /* If src and mask are both solid, then the mask alpha can be * combined into src and mask can be ignored. */ - /* XXX: This optimization assumes that there is no color - * information in mask, so this will need to change when we - * support RENDER-style 4-channel masks. */ if (src->type == CAIRO_PATTERN_TYPE_SOLID && - mask && mask->type == CAIRO_PATTERN_TYPE_SOLID) + mask && + ! mask->has_component_alpha && + mask->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_color_t combined; cairo_solid_pattern_t *src_solid = (cairo_solid_pattern_t *) src; @@ -2410,14 +2402,13 @@ _cairo_pattern_acquire_surfaces (const cairo_pattern_t *src, _cairo_color_multiply_alpha (&combined, mask_solid->color.alpha); _cairo_pattern_init_solid (&src_tmp.solid, &combined, - (src_solid->content | mask_solid->content) & src_content); + src_solid->content | mask_solid->content); src = &src_tmp.base; mask = NULL; } status = _cairo_pattern_acquire_surface (src, dst, - src_content, src_x, src_y, width, height, flags, @@ -2431,7 +2422,6 @@ _cairo_pattern_acquire_surfaces (const cairo_pattern_t *src, } status = _cairo_pattern_acquire_surface (mask, dst, - CAIRO_CONTENT_ALPHA, mask_x, mask_y, width, height, flags, @@ -2458,79 +2448,156 @@ _cairo_pattern_acquire_surfaces (const cairo_pattern_t *src, * "infinite" extents, though it would be possible to optimize these * with a little more work. **/ -cairo_status_t +void _cairo_pattern_get_extents (const cairo_pattern_t *pattern, cairo_rectangle_int_t *extents) { - if (pattern->extend == CAIRO_EXTEND_NONE && - pattern->type == CAIRO_PATTERN_TYPE_SURFACE) - { - cairo_status_t status; - cairo_rectangle_int_t surface_extents; - const cairo_surface_pattern_t *surface_pattern = - (const cairo_surface_pattern_t *) pattern; - cairo_surface_t *surface = surface_pattern->surface; - cairo_matrix_t imatrix; - double x1, y1, x2, y2; - double pad; + cairo_matrix_t imatrix; + double x1, y1, x2, y2; + cairo_status_t status; - status = _cairo_surface_get_extents (surface, &surface_extents); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - goto UNBOUNDED; - if (unlikely (status)) - return status; + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + goto UNBOUNDED; - /* The filter can effectively enlarge the extents of the - * pattern, so extend as necessary. - */ - _cairo_pattern_analyze_filter (surface_pattern, &pad); - x1 = surface_extents.x - pad; - y1 = surface_extents.y - pad; - x2 = surface_extents.x + (int) surface_extents.width + pad; - y2 = surface_extents.y + (int) surface_extents.height + pad; + case CAIRO_PATTERN_TYPE_SURFACE: + { + cairo_rectangle_int_t surface_extents; + const cairo_surface_pattern_t *surface_pattern = + (const cairo_surface_pattern_t *) pattern; + cairo_surface_t *surface = surface_pattern->surface; + double pad; - imatrix = pattern->matrix; - status = cairo_matrix_invert (&imatrix); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); + if (! _cairo_surface_get_extents (surface, &surface_extents)) + goto UNBOUNDED; - _cairo_matrix_transform_bounding_box (&imatrix, - &x1, &y1, &x2, &y2, - NULL); + if (surface_extents.width == 0 || surface_extents.height == 0) + goto EMPTY; - x1 = floor (x1); - if (x1 < CAIRO_RECT_INT_MIN) - x1 = CAIRO_RECT_INT_MIN; - y1 = floor (y1); - if (y1 < CAIRO_RECT_INT_MIN) - y1 = CAIRO_RECT_INT_MIN; + if (pattern->extend != CAIRO_EXTEND_NONE) + goto UNBOUNDED; - x2 = ceil (x2); - if (x2 > CAIRO_RECT_INT_MAX) - x2 = CAIRO_RECT_INT_MAX; - y2 = ceil (y2); - if (y2 > CAIRO_RECT_INT_MAX) - y2 = CAIRO_RECT_INT_MAX; + /* The filter can effectively enlarge the extents of the + * pattern, so extend as necessary. + */ + _cairo_pattern_analyze_filter (&surface_pattern->base, &pad); + x1 = surface_extents.x - pad; + y1 = surface_extents.y - pad; + x2 = surface_extents.x + (int) surface_extents.width + pad; + y2 = surface_extents.y + (int) surface_extents.height + pad; + } + break; - extents->x = x1; extents->width = x2 - x1; - extents->y = y1; extents->height = y2 - y1; + case CAIRO_PATTERN_TYPE_RADIAL: + { + const cairo_radial_pattern_t *radial = + (const cairo_radial_pattern_t *) pattern; + double cx1, cy1; + double cx2, cy2; + double r, D; - return CAIRO_STATUS_SUCCESS; + if (radial->r1 == 0 && radial->r2 == 0) + goto EMPTY; + + cx1 = _cairo_fixed_to_double (radial->c1.x); + cy1 = _cairo_fixed_to_double (radial->c1.y); + r = _cairo_fixed_to_double (radial->r1); + x1 = cx1 - r; x2 = cx1 + r; + y1 = cy1 - r; y2 = cy1 + r; + + cx2 = _cairo_fixed_to_double (radial->c2.x); + cy2 = _cairo_fixed_to_double (radial->c2.y); + r = fabs (_cairo_fixed_to_double (radial->r2)); + + if (pattern->extend != CAIRO_EXTEND_NONE) + goto UNBOUNDED; + + /* We need to be careful, as if the circles are not + * self-contained, then the solution is actually unbounded. + */ + D = (cx1-cx2)*(cx1-cx2) + (cy1-cy2)*(cy1-cy2); + if (D > r*r - 1e-5) + goto UNBOUNDED; + + if (cx2 - r < x1) + x1 = cx2 - r; + if (cx2 + r > x2) + x2 = cx2 + r; + + if (cy2 - r < y1) + y1 = cy2 - r; + if (cy2 + r > y2) + y2 = cy2 + r; + } + break; + + case CAIRO_PATTERN_TYPE_LINEAR: + { + const cairo_linear_pattern_t *linear = + (const cairo_linear_pattern_t *) pattern; + + if (pattern->extend != CAIRO_EXTEND_NONE) + goto UNBOUNDED; + + if (pattern->matrix.xy != 0. || pattern->matrix.yx != 0.) + goto UNBOUNDED; + + if (linear->p1.x == linear->p2.x) { + x1 = -HUGE_VAL; + x2 = HUGE_VAL; + y1 = _cairo_fixed_to_double (MIN (linear->p1.y, linear->p2.y)); + y2 = _cairo_fixed_to_double (MAX (linear->p1.y, linear->p2.y)); + } else if (linear->p1.y == linear->p2.y) { + x1 = _cairo_fixed_to_double (MIN (linear->p1.x, linear->p2.x)); + x2 = _cairo_fixed_to_double (MAX (linear->p1.x, linear->p2.x)); + y1 = -HUGE_VAL; + y2 = HUGE_VAL; + } else { + goto UNBOUNDED; + } + } + break; + + default: + ASSERT_NOT_REACHED; } - /* XXX: We could optimize gradients with pattern->extend of NONE - * here in some cases, (eg. radial gradients and 1 axis of - * horizontal/vertical linear gradients). - */ + imatrix = pattern->matrix; + status = cairo_matrix_invert (&imatrix); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + _cairo_matrix_transform_bounding_box (&imatrix, + &x1, &y1, &x2, &y2, + NULL); + + x1 = floor (x1); + if (x1 < CAIRO_RECT_INT_MIN) + x1 = CAIRO_RECT_INT_MIN; + y1 = floor (y1); + if (y1 < CAIRO_RECT_INT_MIN) + y1 = CAIRO_RECT_INT_MIN; + + x2 = ceil (x2); + if (x2 > CAIRO_RECT_INT_MAX) + x2 = CAIRO_RECT_INT_MAX; + y2 = ceil (y2); + if (y2 > CAIRO_RECT_INT_MAX) + y2 = CAIRO_RECT_INT_MAX; + + extents->x = x1; extents->width = x2 - x1; + extents->y = y1; extents->height = y2 - y1; + return; UNBOUNDED: /* unbounded patterns -> 'infinite' extents */ - extents->x = CAIRO_RECT_INT_MIN; - extents->y = CAIRO_RECT_INT_MIN; - extents->width = CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN; - extents->height = CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN; + _cairo_unbounded_rectangle_init (extents); + return; - return CAIRO_STATUS_SUCCESS; + EMPTY: + extents->x = extents->y = 0; + extents->width = extents->height = 0; + return; } @@ -2613,9 +2680,17 @@ _cairo_pattern_hash (const cairo_pattern_t *pattern) return 0; hash = _cairo_hash_bytes (hash, &pattern->type, sizeof (pattern->type)); - hash = _cairo_hash_bytes (hash, &pattern->matrix, sizeof (pattern->matrix)); - hash = _cairo_hash_bytes (hash, &pattern->filter, sizeof (pattern->filter)); - hash = _cairo_hash_bytes (hash, &pattern->extend, sizeof (pattern->extend)); + if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) { + hash = _cairo_hash_bytes (hash, + &pattern->matrix, sizeof (pattern->matrix)); + hash = _cairo_hash_bytes (hash, + &pattern->filter, sizeof (pattern->filter)); + hash = _cairo_hash_bytes (hash, + &pattern->extend, sizeof (pattern->extend)); + hash = _cairo_hash_bytes (hash, + &pattern->has_component_alpha, + sizeof (pattern->has_component_alpha)); + } switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: @@ -2766,17 +2841,25 @@ _cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b) if (a->status || b->status) return FALSE; + if (a == b) + return TRUE; + if (a->type != b->type) return FALSE; - if (memcmp (&a->matrix, &b->matrix, sizeof (cairo_matrix_t))) + if (a->has_component_alpha != b->has_component_alpha) return FALSE; - if (a->filter != b->filter) - return FALSE; + if (a->type != CAIRO_PATTERN_TYPE_SOLID) { + if (memcmp (&a->matrix, &b->matrix, sizeof (cairo_matrix_t))) + return FALSE; - if (a->extend != b->extend) - return FALSE; + if (a->filter != b->filter) + return FALSE; + + if (a->extend != b->extend) + return FALSE; + } switch (a->type) { case CAIRO_PATTERN_TYPE_SOLID: diff --git a/gfx/cairo/cairo/src/cairo-pdf-operators.c b/gfx/cairo/cairo/src/cairo-pdf-operators.c index 22c0a88d7dd4..7b88092e2bd8 100644 --- a/gfx/cairo/cairo/src/cairo-pdf-operators.c +++ b/gfx/cairo/cairo/src/cairo-pdf-operators.c @@ -48,8 +48,6 @@ #include "cairo-output-stream-private.h" #include "cairo-scaled-font-subsets-private.h" -#include - static cairo_status_t _cairo_pdf_operators_end_text (cairo_pdf_operators_t *pdf_operators); @@ -180,7 +178,7 @@ _count_word_up_to (const unsigned char *s, int length) int word = 0; while (length--) { - if (! (isspace (*s) || *s == '<')) { + if (! (_cairo_isspace (*s) || *s == '<')) { s++; word++; } else { @@ -239,7 +237,7 @@ _word_wrap_stream_write (cairo_output_stream_t *base, length--; _cairo_output_stream_printf (stream->output, ">"); stream->column++; - } else if (isspace (*data)) { + } else if (_cairo_isspace (*data)) { newline = (*data == '\n' || *data == '\r'); if (! newline && stream->column >= stream->max_column) { _cairo_output_stream_printf (stream->output, "\n"); diff --git a/gfx/cairo/cairo/src/cairo-pdf-surface-private.h b/gfx/cairo/cairo/src/cairo-pdf-surface-private.h index 5aedcd051ee5..5c28f70ca955 100644 --- a/gfx/cairo/cairo/src/cairo-pdf-surface-private.h +++ b/gfx/cairo/cairo/src/cairo-pdf-surface-private.h @@ -45,6 +45,7 @@ #include "cairo-pdf.h" #include "cairo-surface-private.h" +#include "cairo-surface-clipper-private.h" #include "cairo-pdf-operators-private.h" #include "cairo-path-fixed-private.h" @@ -52,7 +53,10 @@ typedef struct _cairo_pdf_resource { unsigned int id; } cairo_pdf_resource_t; +#define CAIRO_NUM_OPERATORS (CAIRO_OPERATOR_HSL_LUMINOSITY + 1) + typedef struct _cairo_pdf_group_resources { + cairo_bool_t operators[CAIRO_NUM_OPERATORS]; cairo_array_t alphas; cairo_array_t smasks; cairo_array_t patterns; @@ -171,12 +175,15 @@ struct _cairo_pdf_surface { cairo_bool_t is_knockout; } group_stream; + cairo_surface_clipper_t clipper; + cairo_pdf_operators_t pdf_operators; cairo_paginated_mode_t paginated_mode; cairo_bool_t select_pattern_gstate_saved; cairo_bool_t force_fallbacks; + cairo_operator_t current_operator; cairo_bool_t current_pattern_is_solid_color; cairo_bool_t current_color_is_stroke; double current_color_red; diff --git a/gfx/cairo/cairo/src/cairo-pdf-surface.c b/gfx/cairo/cairo/src/cairo-pdf-surface.c index 47fdb1b35961..a1be10a08db4 100644 --- a/gfx/cairo/cairo/src/cairo-pdf-surface.c +++ b/gfx/cairo/cairo/src/cairo-pdf-surface.c @@ -46,10 +46,11 @@ #include "cairo-pdf-operators-private.h" #include "cairo-analysis-surface-private.h" #include "cairo-image-info-private.h" -#include "cairo-meta-surface-private.h" +#include "cairo-recording-surface-private.h" #include "cairo-output-stream-private.h" #include "cairo-paginated-private.h" #include "cairo-scaled-font-subsets-private.h" +#include "cairo-surface-clipper-private.h" #include "cairo-type3-glyph-surface-private.h" #include @@ -243,6 +244,50 @@ _cairo_pdf_surface_set_size_internal (cairo_pdf_surface_t *surface, &surface->cairo_to_pdf); } +static cairo_bool_t +_path_covers_bbox (cairo_pdf_surface_t *surface, + cairo_path_fixed_t *path) +{ + cairo_box_t box; + + return _cairo_path_fixed_is_box (path, &box) && + box.p1.x <= 0 && + box.p1.y <= 0 && + box.p2.x >= _cairo_fixed_from_double (surface->width) && + box.p2.y >= _cairo_fixed_from_double (surface->height); +} + +static cairo_status_t +_cairo_pdf_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_pdf_surface_t *surface = cairo_container_of (clipper, + cairo_pdf_surface_t, + clipper); + cairo_int_status_t status; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + if (path == NULL) { + _cairo_output_stream_printf (surface->output, "Q q\n"); + + surface->current_pattern_is_solid_color = FALSE; + _cairo_pdf_operators_reset (&surface->pdf_operators); + + return CAIRO_STATUS_SUCCESS; + } + + if (_path_covers_bbox (surface, path)) + return CAIRO_STATUS_SUCCESS; + + return _cairo_pdf_operators_clip (&surface->pdf_operators, path, fill_rule); +} + static cairo_surface_t * _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, double width, @@ -310,8 +355,12 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, surface->force_fallbacks = FALSE; surface->select_pattern_gstate_saved = FALSE; surface->current_pattern_is_solid_color = FALSE; + surface->current_operator = CAIRO_OPERATOR_OVER; surface->header_emitted = FALSE; + _cairo_surface_clipper_init (&surface->clipper, + _cairo_pdf_surface_clipper_intersect_clip_path); + _cairo_pdf_operators_init (&surface->pdf_operators, surface->output, &surface->cairo_to_pdf, @@ -324,7 +373,6 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, surface->paginated_surface = _cairo_paginated_surface_create ( &surface->base, CAIRO_CONTENT_COLOR_ALPHA, - width, height, &cairo_pdf_surface_paginated_backend); status = surface->paginated_surface->status; @@ -571,11 +619,6 @@ cairo_pdf_surface_set_size (cairo_surface_t *surface, _cairo_pdf_surface_set_size_internal (pdf_surface, width_in_points, height_in_points); - status = _cairo_paginated_surface_set_size (pdf_surface->paginated_surface, - width_in_points, - height_in_points); - if (unlikely (status)) - status = _cairo_surface_set_error (surface, status); } static void @@ -612,6 +655,11 @@ _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) static void _cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *res) { + int i; + + for (i = 0; i < CAIRO_NUM_OPERATORS; i++) + res->operators[i] = FALSE; + _cairo_array_init (&res->alphas, sizeof (double)); _cairo_array_init (&res->smasks, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&res->patterns, sizeof (cairo_pdf_resource_t)); @@ -632,6 +680,11 @@ _cairo_pdf_group_resources_fini (cairo_pdf_group_resources_t *res) static void _cairo_pdf_group_resources_clear (cairo_pdf_group_resources_t *res) { + int i; + + for (i = 0; i < CAIRO_NUM_OPERATORS; i++) + res->operators[i] = FALSE; + _cairo_array_truncate (&res->alphas, 0); _cairo_array_truncate (&res->smasks, 0); _cairo_array_truncate (&res->patterns, 0); @@ -639,6 +692,15 @@ _cairo_pdf_group_resources_clear (cairo_pdf_group_resources_t *res) _cairo_array_truncate (&res->fonts, 0); } +static void +_cairo_pdf_surface_add_operator (cairo_pdf_surface_t *surface, + cairo_operator_t op) +{ + cairo_pdf_group_resources_t *res = &surface->resources; + + res->operators[op] = TRUE; +} + static cairo_status_t _cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface, double alpha, @@ -747,6 +809,47 @@ _cairo_pdf_surface_get_font_resource (cairo_pdf_surface_t *surface, return font.subset_resource; } +static const char * +_cairo_operator_to_pdf_blend_mode (cairo_operator_t op) +{ + switch (op) { + /* The extend blend mode operators */ + case CAIRO_OPERATOR_MULTIPLY: return "Multiply"; + case CAIRO_OPERATOR_SCREEN: return "Screen"; + case CAIRO_OPERATOR_OVERLAY: return "Overlay"; + case CAIRO_OPERATOR_DARKEN: return "Darken"; + case CAIRO_OPERATOR_LIGHTEN: return "Lighten"; + case CAIRO_OPERATOR_COLOR_DODGE: return "ColorDodge"; + case CAIRO_OPERATOR_COLOR_BURN: return "ColorBurn"; + case CAIRO_OPERATOR_HARD_LIGHT: return "HardLight"; + case CAIRO_OPERATOR_SOFT_LIGHT: return "SoftLight"; + case CAIRO_OPERATOR_DIFFERENCE: return "Difference"; + case CAIRO_OPERATOR_EXCLUSION: return "Exclusion"; + case CAIRO_OPERATOR_HSL_HUE: return "Hue"; + case CAIRO_OPERATOR_HSL_SATURATION: return "Saturation"; + case CAIRO_OPERATOR_HSL_COLOR: return "Color"; + case CAIRO_OPERATOR_HSL_LUMINOSITY: return "Luminosity"; + + default: + /* The original Porter-Duff set */ + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_ATOP: + case CAIRO_OPERATOR_DEST: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_DEST_ATOP: + case CAIRO_OPERATOR_XOR: + case CAIRO_OPERATOR_ADD: + case CAIRO_OPERATOR_SATURATE: + return "Normal"; + } +} + static void _cairo_pdf_surface_emit_group_resources (cairo_pdf_surface_t *surface, cairo_pdf_group_resources_t *res) @@ -764,6 +867,14 @@ _cairo_pdf_surface_emit_group_resources (cairo_pdf_surface_t *surface, _cairo_output_stream_printf (surface->output, " /ExtGState <<\n"); + for (i = 0; i < CAIRO_NUM_OPERATORS; i++) { + if (res->operators[i]) { + _cairo_output_stream_printf (surface->output, + " /b%d << /BM /%s >>\n", + i, _cairo_operator_to_pdf_blend_mode(i)); + } + } + for (i = 0; i < num_alphas; i++) { _cairo_array_copy_element (&res->alphas, i, &alpha); _cairo_output_stream_printf (surface->output, @@ -912,9 +1023,9 @@ _get_jpx_image_info (cairo_surface_t *source, static cairo_int_status_t _get_jpeg_image_info (cairo_surface_t *source, - cairo_image_info_t *info, - const unsigned char **mime_data, - unsigned int *mime_data_length) + cairo_image_info_t *info, + const unsigned char **mime_data, + unsigned int *mime_data_length) { cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, mime_data, mime_data_length); @@ -929,52 +1040,48 @@ _get_source_surface_size (cairo_surface_t *source, int *width, int *height) { - cairo_image_surface_t *image; - void *image_extra; cairo_status_t status; + cairo_rectangle_int_t extents; cairo_image_info_t info; const unsigned char *mime_data; unsigned int mime_data_length; - if (_cairo_surface_is_meta (source)) { - cairo_rectangle_int_t extents; + if (_cairo_surface_is_recording (source)) { + cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) source; + cairo_box_t bbox; - status = _cairo_surface_get_extents (source, &extents); + status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL); if (unlikely (status)) return status; + _cairo_box_round_to_rectangle (&bbox, &extents); + *width = extents.width; *height = extents.height; - - return status; + return CAIRO_STATUS_SUCCESS; } status = _get_jpx_image_info (source, &info, &mime_data, &mime_data_length); - if (status == CAIRO_STATUS_SUCCESS) { + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { *width = info.width; *height = info.height; - } else if (_cairo_status_is_error (status)) { return status; } status = _get_jpeg_image_info (source, &info, &mime_data, &mime_data_length); - if (status == CAIRO_STATUS_SUCCESS) { + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { *width = info.width; *height = info.height; - } else if (_cairo_status_is_error (status)) { return status; } - status = _cairo_surface_acquire_source_image (source, &image, &image_extra); - if (unlikely (status)) - return status; + if (! _cairo_surface_get_extents (source, &extents)) + return CAIRO_INT_STATUS_UNSUPPORTED; - *width = image->width; - *height = image->height; + *width = extents.width; + *height = extents.height; - _cairo_surface_release_source_image (source, image, image_extra); - - return status; + return CAIRO_STATUS_SUCCESS; } static cairo_status_t @@ -992,6 +1099,7 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, cairo_bool_t interpolate; switch (filter) { + default: case CAIRO_FILTER_GOOD: case CAIRO_FILTER_BEST: case CAIRO_FILTER_BILINEAR: @@ -1054,7 +1162,7 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, static cairo_status_t _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, const cairo_pattern_t *pattern, - cairo_rectangle_int_t *extents, + const cairo_rectangle_int_t *extents, cairo_pdf_resource_t *pattern_res, cairo_pdf_resource_t *gstate_res) { @@ -1113,7 +1221,7 @@ _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, pdf_pattern.width = surface->width; pdf_pattern.height = surface->height; - if (extents) { + if (extents != NULL) { pdf_pattern.extents = *extents; } else { pdf_pattern.extents.x = 0; @@ -1169,6 +1277,7 @@ _cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface, surface->pdf_stream.length = length; surface->pdf_stream.compressed = compressed; surface->current_pattern_is_solid_color = FALSE; + surface->current_operator = CAIRO_OPERATOR_OVER; _cairo_pdf_operators_reset (&surface->pdf_operators); _cairo_output_stream_printf (surface->output, @@ -1223,13 +1332,12 @@ _cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface) surface->output = surface->pdf_stream.old_output; _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); surface->pdf_stream.old_output = NULL; - _cairo_output_stream_printf (surface->output, - "\n"); } length = _cairo_output_stream_get_position (surface->output) - surface->pdf_stream.start_offset; _cairo_output_stream_printf (surface->output, + "\n" "endstream\n" "endobj\n"); @@ -1309,6 +1417,7 @@ _cairo_pdf_surface_open_group (cairo_pdf_surface_t *surface, surface->group_stream.active = TRUE; surface->current_pattern_is_solid_color = FALSE; + surface->current_operator = CAIRO_OPERATOR_OVER; _cairo_pdf_operators_reset (&surface->pdf_operators); surface->group_stream.mem_stream = _cairo_memory_stream_create (); @@ -1469,15 +1578,6 @@ _cairo_pdf_surface_close_content_stream (cairo_pdf_surface_t *surface) return _cairo_output_stream_get_status (surface->output); } -static cairo_surface_t * -_cairo_pdf_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - return cairo_meta_surface_create (content, width, height); -} - static void _cairo_pdf_source_surface_entry_pluck (void *entry, void *closure) { @@ -1582,6 +1682,8 @@ _cairo_pdf_surface_finish (void *abstract_surface) surface->font_subsets = NULL; } + _cairo_surface_clipper_reset (&surface->clipper); + return status; } @@ -1861,8 +1963,6 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, #undef IMAGE_DICTIONARY - if (image_res == NULL) - *image_res = surface->pdf_stream.self; _cairo_output_stream_write (surface->output, rgb, rgb_size); status = _cairo_pdf_surface_close_stream (surface); @@ -1901,7 +2001,6 @@ _cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface, " /Subtype /Image\n" " /Width %d\n" " /Height %d\n" - " /ColorSpace /DeviceRGB\n" " /Filter /JPXDecode\n", info.width, info.height); @@ -1909,7 +2008,6 @@ _cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface, return status; _cairo_output_stream_write (surface->output, mime_data, mime_data_length); - _cairo_output_stream_printf (surface->output, "\n"); status = _cairo_pdf_surface_close_stream (surface); return status; @@ -1957,7 +2055,6 @@ _cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, return status; _cairo_output_stream_write (surface->output, mime_data, mime_data_length); - _cairo_output_stream_printf (surface->output, "\n"); status = _cairo_pdf_surface_close_stream (surface); return status; @@ -2019,7 +2116,7 @@ _cairo_pdf_surface_emit_padded_image_surface (cairo_pdf_surface_t *surface, return status; pad_image = &image->base; - if (cairo_pattern_get_extend (&pattern->base) == CAIRO_EXTEND_PAD) { + if (pattern->base.extend == CAIRO_EXTEND_PAD) { cairo_box_t box; cairo_rectangle_int_t rect; cairo_surface_pattern_t pad_pattern; @@ -2050,7 +2147,8 @@ _cairo_pdf_surface_emit_padded_image_surface (cairo_pdf_surface_t *surface, 0, 0, 0, 0, rect.width, - rect.height); + rect.height, + NULL); _cairo_pattern_fini (&pad_pattern.base); if (unlikely (status)) goto BAIL; @@ -2096,30 +2194,29 @@ BAIL: static cairo_status_t -_cairo_pdf_surface_emit_meta_surface (cairo_pdf_surface_t *surface, - cairo_surface_t *meta_surface, - cairo_pdf_resource_t resource) +_cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, + cairo_surface_t *recording_surface, + cairo_pdf_resource_t resource) { double old_width, old_height; cairo_paginated_mode_t old_paginated_mode; - cairo_clip_t *old_clip; - cairo_rectangle_int_t meta_extents; + cairo_rectangle_int_t recording_extents; + cairo_bool_t is_bounded; cairo_status_t status; int alpha = 0; - status = _cairo_surface_get_extents (meta_surface, &meta_extents); - if (unlikely (status)) - return status; + is_bounded = _cairo_surface_get_extents (recording_surface, &recording_extents); + assert (is_bounded); old_width = surface->width; old_height = surface->height; old_paginated_mode = surface->paginated_mode; - old_clip = _cairo_surface_get_clip (&surface->base); + _cairo_pdf_surface_set_size_internal (surface, - meta_extents.width, - meta_extents.height); + recording_extents.width, + recording_extents.height); /* Patterns are emitted after fallback images. The paginated mode - * needs to be set to _RENDER while the meta surface is replayed + * needs to be set to _RENDER while the recording surface is replayed * back to this surface. */ surface->paginated_mode = CAIRO_PAGINATED_MODE_RENDER; @@ -2128,7 +2225,7 @@ _cairo_pdf_surface_emit_meta_surface (cairo_pdf_surface_t *surface, if (unlikely (status)) return status; - if (cairo_surface_get_content (meta_surface) == CAIRO_CONTENT_COLOR) { + if (cairo_surface_get_content (recording_surface) == CAIRO_CONTENT_COLOR) { status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); if (unlikely (status)) return status; @@ -2140,16 +2237,12 @@ _cairo_pdf_surface_emit_meta_surface (cairo_pdf_surface_t *surface, surface->height); } - status = _cairo_meta_surface_replay_region (meta_surface, &surface->base, - CAIRO_META_REGION_NATIVE); + status = _cairo_recording_surface_replay_region (recording_surface, &surface->base, + CAIRO_RECORDING_REGION_NATIVE); assert (status != CAIRO_INT_STATUS_UNSUPPORTED); if (unlikely (status)) return status; - status = _cairo_surface_set_clip (&surface->base, old_clip); - if (unlikely (status)) - return status; - status = _cairo_pdf_surface_close_content_stream (surface); _cairo_pdf_surface_set_size_internal (surface, @@ -2164,8 +2257,8 @@ static cairo_status_t _cairo_pdf_surface_emit_surface (cairo_pdf_surface_t *surface, cairo_pdf_source_surface_t *src_surface) { - if (_cairo_surface_is_meta (src_surface->surface)) { - return _cairo_pdf_surface_emit_meta_surface (surface, + if (_cairo_surface_is_recording (src_surface->surface)) { + return _cairo_pdf_surface_emit_recording_surface (surface, src_surface->surface, src_surface->hash_entry->surface_res); } else { @@ -2193,8 +2286,8 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, int bbox_x, bbox_y; char draw_surface[200]; - if (cairo_pattern_get_extend (&pattern->base) == CAIRO_EXTEND_PAD && - ! _cairo_surface_is_meta (pattern->surface)) + if (pattern->base.extend == CAIRO_EXTEND_PAD && + ! _cairo_surface_is_recording (pattern->surface)) { status = _cairo_pdf_surface_emit_padded_image_surface (surface, pdf_pattern, @@ -2325,7 +2418,7 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, if (unlikely (status)) return status; - if (_cairo_surface_is_meta (pattern->surface)) { + if (_cairo_surface_is_recording (pattern->surface)) { snprintf(draw_surface, sizeof (draw_surface), "/x%d Do\n", @@ -2852,9 +2945,9 @@ _cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, dx = fabs (x2 - x1); dy = fabs (y2 - y1); if (dx > 1e-6) - x_rep = (int) ceil (surface->width/dx); + x_rep = ceil (surface->width/dx); if (dy > 1e-6) - y_rep = (int) ceil (surface->height/dy); + y_rep = ceil (surface->height/dy); repeat_end = MAX (x_rep, y_rep); repeat_begin = -repeat_end; @@ -3168,6 +3261,82 @@ _cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern return status; } +static cairo_status_t +_cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, + cairo_surface_pattern_t *source) +{ + cairo_pdf_resource_t surface_res; + int width, height; + cairo_matrix_t cairo_p2d, pdf_p2d; + cairo_status_t status; + int alpha; + + status = _cairo_pdf_surface_add_source_surface (surface, + source->surface, + source->base.filter, + &surface_res, + &width, + &height); + if (unlikely (status)) + return status; + + cairo_p2d = source->base.matrix; + status = cairo_matrix_invert (&cairo_p2d); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + pdf_p2d = surface->cairo_to_pdf; + cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &pdf_p2d); + cairo_matrix_translate (&pdf_p2d, 0.0, height); + cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); + if (! _cairo_surface_is_recording (source->surface)) + cairo_matrix_scale (&pdf_p2d, width, height); + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + if (! _cairo_matrix_is_identity (&pdf_p2d)) { + _cairo_output_stream_printf (surface->output, + "%f %f %f %f %f %f cm\n", + pdf_p2d.xx, pdf_p2d.yx, + pdf_p2d.xy, pdf_p2d.yy, + pdf_p2d.x0, pdf_p2d.y0); + } + + status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "/a%d gs /x%d Do\n", + alpha, + surface_res.id); + + return _cairo_pdf_surface_add_xobject (surface, surface_res); +} + +static cairo_status_t +_cairo_pdf_surface_select_operator (cairo_pdf_surface_t *surface, + cairo_operator_t op) +{ + cairo_status_t status; + + if (op == surface->current_operator) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "/b%d gs\n", op); + surface->current_operator = op; + _cairo_pdf_surface_add_operator (surface, op); + + return CAIRO_STATUS_SUCCESS; +} + static cairo_status_t _cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface, const cairo_pattern_t *pattern, @@ -3313,7 +3482,7 @@ _cairo_pdf_surface_show_page (void *abstract_surface) return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t +static cairo_bool_t _cairo_pdf_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { @@ -3326,35 +3495,10 @@ _cairo_pdf_surface_get_extents (void *abstract_surface, * mention the arbitrary limitation of width to a short(!). We * may need to come up with a better interface for get_size. */ - rectangle->width = (int) ceil (surface->width); - rectangle->height = (int) ceil (surface->height); + rectangle->width = ceil (surface->width); + rectangle->height = ceil (surface->height); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_pdf_surface_intersect_clip_path (void *abstract_surface, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_int_status_t status; - - if (path == NULL) { - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, "Q q\n"); - surface->current_pattern_is_solid_color = FALSE; - _cairo_pdf_operators_reset (&surface->pdf_operators); - - return CAIRO_STATUS_SUCCESS; - } - - return _cairo_pdf_operators_clip (&surface->pdf_operators, path, fill_rule); + return TRUE; } static void @@ -3579,37 +3723,62 @@ _cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface, _cairo_output_stream_printf (surface->output, "endcodespacerange\n"); - num_bfchar = font_subset->num_glyphs - 1; - - /* The CMap specification has a limit of 100 characters per beginbfchar operator */ - _cairo_output_stream_printf (surface->output, - "%d beginbfchar\n", - num_bfchar > 100 ? 100 : num_bfchar); - - for (i = 0; i < num_bfchar; i++) { - if (i != 0 && i % 100 == 0) { - _cairo_output_stream_printf (surface->output, - "endbfchar\n" - "%d beginbfchar\n", - num_bfchar - i > 100 ? 100 : num_bfchar - i); - } - if (is_composite) { - _cairo_output_stream_printf (surface->output, - "<%04x> ", - i + 1); - } else { - _cairo_output_stream_printf (surface->output, - "<%02x> ", - i + 1); - } - status = _cairo_pdf_surface_emit_unicode_for_glyph (surface, - font_subset->utf8[i + 1]); - if (unlikely (status)) - return status; + if (font_subset->is_scaled) { + /* Type 3 fonts include glyph 0 in the subset */ + num_bfchar = font_subset->num_glyphs; + /* The CMap specification has a limit of 100 characters per beginbfchar operator */ _cairo_output_stream_printf (surface->output, - "\n"); + "%d beginbfchar\n", + num_bfchar > 100 ? 100 : num_bfchar); + + for (i = 0; i < num_bfchar; i++) { + if (i != 0 && i % 100 == 0) { + _cairo_output_stream_printf (surface->output, + "endbfchar\n" + "%d beginbfchar\n", + num_bfchar - i > 100 ? 100 : num_bfchar - i); + } + _cairo_output_stream_printf (surface->output, "<%02x> ", i); + status = _cairo_pdf_surface_emit_unicode_for_glyph (surface, + font_subset->utf8[i]); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "\n"); + } + } else { + /* Other fonts reserve glyph 0 for .notdef. Omit glyph 0 from the /ToUnicode map */ + num_bfchar = font_subset->num_glyphs - 1; + + /* The CMap specification has a limit of 100 characters per beginbfchar operator */ + _cairo_output_stream_printf (surface->output, + "%d beginbfchar\n", + num_bfchar > 100 ? 100 : num_bfchar); + + for (i = 0; i < num_bfchar; i++) { + if (i != 0 && i % 100 == 0) { + _cairo_output_stream_printf (surface->output, + "endbfchar\n" + "%d beginbfchar\n", + num_bfchar - i > 100 ? 100 : num_bfchar - i); + } + if (is_composite) + _cairo_output_stream_printf (surface->output, "<%04x> ", i + 1); + else + _cairo_output_stream_printf (surface->output, "<%02x> ", i + 1); + + status = _cairo_pdf_surface_emit_unicode_for_glyph (surface, + font_subset->utf8[i + 1]); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "\n"); + } } + _cairo_output_stream_printf (surface->output, "endbfchar\n"); @@ -3824,16 +3993,16 @@ _cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, if (subset_resource.id == 0) return CAIRO_STATUS_SUCCESS; - /* We ignore the zero-trailer and set Length3 to 0. */ - length = subset->header_length + subset->data_length; + length = subset->header_length + subset->data_length + subset->trailer_length; status = _cairo_pdf_surface_open_stream (surface, NULL, TRUE, " /Length1 %lu\n" " /Length2 %lu\n" - " /Length3 0\n", + " /Length3 %lu\n", subset->header_length, - subset->data_length); + subset->data_length, + subset->trailer_length); if (unlikely (status)) return status; @@ -4762,14 +4931,15 @@ _cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface * to be appended to surface->smask_groups. Additional patterns * may also be appended to surface->patterns. * - * Writing meta surface patterns will cause additional patterns + * Writing recording surface patterns will cause additional patterns * and groups to be appended. */ pattern_index = 0; group_index = 0; surface_index = 0; while ((pattern_index < _cairo_array_num_elements (&surface->page_patterns)) || - (group_index < _cairo_array_num_elements (&surface->smask_groups))) + (group_index < _cairo_array_num_elements (&surface->smask_groups)) || + (surface_index < _cairo_array_num_elements (&surface->page_surfaces))) { for (; group_index < _cairo_array_num_elements (&surface->smask_groups); group_index++) { _cairo_array_copy_element (&surface->smask_groups, group_index, &group); @@ -4917,7 +5087,7 @@ _surface_pattern_supported (cairo_surface_pattern_t *pattern) { cairo_extend_t extend; - if (_cairo_surface_is_meta (pattern->surface)) + if (_cairo_surface_is_recording (pattern->surface)) return TRUE; if (pattern->surface->backend->acquire_source_image == NULL) @@ -4998,6 +5168,46 @@ _pattern_supported (const cairo_pattern_t *pattern) return FALSE; } +static cairo_bool_t +_pdf_operator_supported (cairo_operator_t op) +{ + switch (op) { + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_MULTIPLY: + case CAIRO_OPERATOR_SCREEN: + case CAIRO_OPERATOR_OVERLAY: + case CAIRO_OPERATOR_DARKEN: + case CAIRO_OPERATOR_LIGHTEN: + case CAIRO_OPERATOR_COLOR_DODGE: + case CAIRO_OPERATOR_COLOR_BURN: + case CAIRO_OPERATOR_HARD_LIGHT: + case CAIRO_OPERATOR_SOFT_LIGHT: + case CAIRO_OPERATOR_DIFFERENCE: + case CAIRO_OPERATOR_EXCLUSION: + case CAIRO_OPERATOR_HSL_HUE: + case CAIRO_OPERATOR_HSL_SATURATION: + case CAIRO_OPERATOR_HSL_COLOR: + case CAIRO_OPERATOR_HSL_LUMINOSITY: + return TRUE; + + default: + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_ATOP: + case CAIRO_OPERATOR_DEST: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_DEST_ATOP: + case CAIRO_OPERATOR_XOR: + case CAIRO_OPERATOR_ADD: + case CAIRO_OPERATOR_SATURATE: + return FALSE; + } +} + static cairo_int_status_t _cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface, cairo_operator_t op, @@ -5012,21 +5222,21 @@ _cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface, if (! _pattern_supported (pattern)) return CAIRO_INT_STATUS_UNSUPPORTED; - if (op == CAIRO_OPERATOR_OVER) { + if (_pdf_operator_supported (op)) { if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; - if ( _cairo_surface_is_meta (surface_pattern->surface)) { + if ( _cairo_surface_is_recording (surface_pattern->surface)) { if (pattern->extend == CAIRO_EXTEND_PAD) return CAIRO_INT_STATUS_UNSUPPORTED; else - return CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN; + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; } } + + return CAIRO_STATUS_SUCCESS; } - if (op == CAIRO_OPERATOR_OVER) - return CAIRO_STATUS_SUCCESS; /* The SOURCE operator is supported if the pattern is opaque or if * there is nothing painted underneath. */ @@ -5034,14 +5244,14 @@ _cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface, if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; - if (_cairo_surface_is_meta (surface_pattern->surface)) { + if (_cairo_surface_is_recording (surface_pattern->surface)) { if (_cairo_pattern_is_opaque (pattern)) { - return CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN; + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; } else { /* FIXME: The analysis surface does not yet have - * the capability to analyze a non opaque meta + * the capability to analyze a non opaque recording * surface and mark it supported if there is - * nothing underneath. For now meta surfaces of + * nothing underneath. For now recording surfaces of * type CONTENT_COLOR_ALPHA painted with * OPERATOR_SOURCE will result in a fallback * image. */ @@ -5095,12 +5305,13 @@ static cairo_int_status_t _cairo_pdf_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_status_t status; cairo_pdf_smask_group_t *group; cairo_pdf_resource_t pattern_res, gstate_res; + cairo_rectangle_int_t extents; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { return _cairo_pdf_surface_analyze_operation (surface, op, source); @@ -5112,11 +5323,42 @@ _cairo_pdf_surface_paint (void *abstract_surface, assert (_cairo_pdf_surface_operation_supported (surface, op, source)); + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_select_operator (surface, op); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE && + source->extend == CAIRO_EXTEND_NONE) { + + _cairo_output_stream_printf (surface->output, "q\n"); + status = _cairo_pdf_surface_paint_surface_pattern (surface, + (cairo_surface_pattern_t *) source); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, "Q\n"); + return _cairo_output_stream_get_status (surface->output); + } + + status = _cairo_surface_paint_extents (&surface->base, + op, source, clip, + &extents); + if (unlikely (status)) + return status; + pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, extents, + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, &extents, &pattern_res, &gstate_res); - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) return CAIRO_STATUS_SUCCESS; if (unlikely (status)) return status; @@ -5147,16 +5389,13 @@ _cairo_pdf_surface_paint (void *abstract_surface, if (unlikely (status)) return status; - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - _cairo_output_stream_printf (surface->output, "q /s%d gs /x%d Do Q\n", gstate_res.id, group->group_res.id); } else { - status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE); + status = _cairo_pdf_surface_select_pattern (surface, source, + pattern_res, FALSE); if (unlikely (status)) return status; @@ -5177,7 +5416,7 @@ _cairo_pdf_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_pdf_smask_group_t *group; @@ -5190,9 +5429,13 @@ _cairo_pdf_surface_mask (void *abstract_surface, if (_cairo_status_is_error (source_status)) return source_status; - mask_status = _cairo_pdf_surface_analyze_operation (surface, op, mask); - if (_cairo_status_is_error (mask_status)) - return mask_status; + if (mask->has_component_alpha) { + mask_status = CAIRO_INT_STATUS_UNSUPPORTED; + } else { + mask_status = _cairo_pdf_surface_analyze_operation (surface, op, mask); + if (_cairo_status_is_error (mask_status)) + return mask_status; + } return _cairo_analysis_surface_merge_status (source_status, mask_status); @@ -5205,6 +5448,10 @@ _cairo_pdf_surface_mask (void *abstract_surface, assert (_cairo_pdf_surface_operation_supported (surface, op, source)); assert (_cairo_pdf_surface_operation_supported (surface, op, mask)); + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + group = _cairo_pdf_surface_create_smask_group (surface); if (unlikely (group == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -5244,6 +5491,10 @@ _cairo_pdf_surface_mask (void *abstract_surface, if (unlikely (status)) return status; + status = _cairo_pdf_surface_select_operator (surface, op); + if (unlikely (status)) + return status; + _cairo_output_stream_printf (surface->output, "q /s%d gs /x%d Do Q\n", group->group_res.id, @@ -5262,27 +5513,44 @@ _cairo_pdf_surface_stroke (void *abstract_surface, cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_status_t status; cairo_pdf_smask_group_t *group; cairo_pdf_resource_t pattern_res, gstate_res; + cairo_rectangle_int_t extents; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return _cairo_pdf_surface_analyze_operation (surface, op, source); assert (_cairo_pdf_surface_operation_supported (surface, op, source)); + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _cairo_surface_stroke_extents (&surface->base, + op, source, path, + style, ctm, ctm_inverse, + tolerance, antialias, + clip, &extents); + if (unlikely (status)) + return status; + pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, extents, + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, &extents, &pattern_res, &gstate_res); - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) return CAIRO_STATUS_SUCCESS; if (unlikely (status)) return status; + status = _cairo_pdf_surface_select_operator (surface, op); + if (unlikely (status)) + return status; + if (gstate_res.id != 0) { group = _cairo_pdf_surface_create_smask_group (surface); if (unlikely (group == NULL)) @@ -5355,12 +5623,13 @@ _cairo_pdf_surface_fill (void *abstract_surface, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_status_t status; cairo_pdf_smask_group_t *group; cairo_pdf_resource_t pattern_res, gstate_res; + cairo_rectangle_int_t extents; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { return _cairo_pdf_surface_analyze_operation (surface, op, source); @@ -5372,11 +5641,49 @@ _cairo_pdf_surface_fill (void *abstract_surface, assert (_cairo_pdf_surface_operation_supported (surface, op, source)); + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_select_operator (surface, op); + if (unlikely (status)) + return status; + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE && + source->extend == CAIRO_EXTEND_NONE) { + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, "q\n"); + status = _cairo_pdf_operators_clip (&surface->pdf_operators, + path, + fill_rule); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_paint_surface_pattern (surface, + (cairo_surface_pattern_t *) source); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, "Q\n"); + return _cairo_output_stream_get_status (surface->output); + } + + status = _cairo_surface_fill_extents (&surface->base, + op, source, path, fill_rule, + tolerance, antialias, + clip, &extents); + if (unlikely (status)) + return status; + pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, extents, + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, &extents, &pattern_res, &gstate_res); - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) return CAIRO_STATUS_SUCCESS; if (unlikely (status)) return status; @@ -5456,11 +5763,12 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, cairo_antialias_t stroke_antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_status_t status; cairo_pdf_resource_t fill_pattern_res, stroke_pattern_res, gstate_res; + cairo_rectangle_int_t extents; /* During analysis we return unsupported and let the _fill and * _stroke functions that are on the fallback path do the analysis @@ -5481,10 +5789,29 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, return CAIRO_INT_STATUS_UNSUPPORTED; } + if (fill_op != stroke_op) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_select_operator (surface, fill_op); + if (unlikely (status)) + return status; + + status = _cairo_surface_fill_extents (&surface->base, + fill_op, fill_source, path, fill_rule, + fill_tolerance, fill_antialias, + clip, &extents); + if (unlikely (status)) + return status; + + fill_pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, fill_source, - extents, + &extents, &fill_pattern_res, &gstate_res); if (unlikely (status)) @@ -5492,11 +5819,19 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, assert (gstate_res.id == 0); + status = _cairo_surface_stroke_extents (&surface->base, + stroke_op, stroke_source, path, + stroke_style, stroke_ctm, stroke_ctm_inverse, + stroke_tolerance, stroke_antialias, + clip, &extents); + if (unlikely (status)) + return status; + stroke_pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, stroke_source, - extents, + &extents, &stroke_pattern_res, &gstate_res); if (unlikely (status)) @@ -5550,27 +5885,43 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_status_t status; cairo_pdf_smask_group_t *group; cairo_pdf_resource_t pattern_res, gstate_res; + cairo_rectangle_int_t extents; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return _cairo_pdf_surface_analyze_operation (surface, op, source); assert (_cairo_pdf_surface_operation_supported (surface, op, source)); + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _cairo_surface_glyphs_extents (&surface->base, op, source, + glyphs, num_glyphs, + scaled_font, + clip, &extents); + if (unlikely (status)) + return status; + pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, extents, + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, &extents, &pattern_res, &gstate_res); - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) return CAIRO_STATUS_SUCCESS; if (unlikely (status)) return status; + status = _cairo_pdf_surface_select_operator (surface, op); + if (unlikely (status)) + return status; + if (gstate_res.id != 0) { group = _cairo_pdf_surface_create_smask_group (surface); if (unlikely (group == NULL)) @@ -5681,7 +6032,7 @@ _cairo_pdf_surface_set_paginated_mode (void *abstract_surface, static const cairo_surface_backend_t cairo_pdf_surface_backend = { CAIRO_SURFACE_TYPE_PDF, - _cairo_pdf_surface_create_similar, + NULL, /* create similar: handled by wrapper */ _cairo_pdf_surface_finish, NULL, /* acquire_source_image */ NULL, /* release_source_image */ @@ -5695,8 +6046,6 @@ static const cairo_surface_backend_t cairo_pdf_surface_backend = { NULL, /* check_span_renderer */ NULL, /* _cairo_pdf_surface_copy_page */ _cairo_pdf_surface_show_page, - NULL, /* set_clip_region */ - _cairo_pdf_surface_intersect_clip_path, _cairo_pdf_surface_get_extents, NULL, /* old_show_glyphs */ _cairo_pdf_surface_get_font_options, @@ -5715,7 +6064,6 @@ static const cairo_surface_backend_t cairo_pdf_surface_backend = { NULL, /* snapshot */ NULL, /* is_compatible */ - NULL, /* reset */ _cairo_pdf_surface_fill_stroke, NULL, /* create_solid_pattern_surface */ NULL, /* can_repaint_solid_pattern_surface */ diff --git a/gfx/cairo/cairo/src/cairo-pen.c b/gfx/cairo/cairo/src/cairo-pen.c index b2fd85575740..cfb67cae467b 100644 --- a/gfx/cairo/cairo/src/cairo-pen.c +++ b/gfx/cairo/cairo/src/cairo-pen.c @@ -38,6 +38,8 @@ #include "cairoint.h" +#include "cairo-slope-private.h" + static int _cairo_pen_vertices_needed (double tolerance, double radius, @@ -393,194 +395,3 @@ _cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen, return i; } - -static int -_cairo_pen_stroke_spline_add_convolved_point (cairo_pen_stroke_spline_t *stroker, - const cairo_point_t *last_point, - const cairo_slope_t *slope, - cairo_point_t *last_hull_point, - int active, - int step) -{ - do { - cairo_point_t hull_point; - - hull_point.x = last_point->x + stroker->pen.vertices[active].point.x; - hull_point.y = last_point->y + stroker->pen.vertices[active].point.y; - _cairo_polygon_add_edge (&stroker->polygon, - last_hull_point, &hull_point, - step); - *last_hull_point = hull_point; - - /* The strict inequalities here ensure that if a spline slope - * compares identically with either of the slopes of the - * active vertex, then it remains the active vertex. This is - * very important since otherwise we can trigger an infinite - * loop in the case of a degenerate pen, (a line), where - * neither vertex considers itself active for the slope---one - * will consider it as equal and reject, and the other will - * consider it unequal and reject. This is due to the inherent - * ambiguity when comparing slopes that differ by exactly - * pi. */ - if (_cairo_slope_compare (slope, - &stroker->pen.vertices[active].slope_ccw) > 0) - { - if (++active == stroker->pen.num_vertices) - active = 0; - } - else if (_cairo_slope_compare (slope, - &stroker->pen.vertices[active].slope_cw) < 0) - { - if (--active == -1) - active = stroker->pen.num_vertices - 1; - } - else - { - return active; - } - } while (TRUE); -} - - -/* Compute outline of a given spline using the pen. - * The trapezoids needed to fill that outline will be added to traps - */ -cairo_status_t -_cairo_pen_stroke_spline (cairo_pen_stroke_spline_t *stroker, - double tolerance, - cairo_traps_t *traps) -{ - cairo_status_t status; - cairo_slope_t slope; - - /* If the line width is so small that the pen is reduced to a - single point, then we have nothing to do. */ - if (stroker->pen.num_vertices <= 1) - return CAIRO_STATUS_SUCCESS; - - /* open the polygon */ - slope = stroker->spline.initial_slope; - stroker->forward_vertex = - _cairo_pen_find_active_cw_vertex_index (&stroker->pen, &slope); - stroker->forward_hull_point.x = stroker->last_point.x + - stroker->pen.vertices[stroker->forward_vertex].point.x; - stroker->forward_hull_point.y = stroker->last_point.y + - stroker->pen.vertices[stroker->forward_vertex].point.y; - - slope.dx = -slope.dx; - slope.dy = -slope.dy; - stroker->backward_vertex = - _cairo_pen_find_active_cw_vertex_index (&stroker->pen, &slope); - stroker->backward_hull_point.x = stroker->last_point.x + - stroker->pen.vertices[stroker->backward_vertex].point.x; - stroker->backward_hull_point.y = stroker->last_point.y + - stroker->pen.vertices[stroker->backward_vertex].point.y; - - _cairo_polygon_add_edge (&stroker->polygon, - &stroker->backward_hull_point, - &stroker->forward_hull_point, - 1); - - status = _cairo_spline_decompose (&stroker->spline, tolerance); - if (unlikely (status)) - return status; - - /* close the polygon */ - slope = stroker->spline.final_slope; - _cairo_pen_stroke_spline_add_convolved_point (stroker, - &stroker->last_point, - &slope, - &stroker->forward_hull_point, - stroker->forward_vertex, - 1); - - slope.dx = -slope.dx; - slope.dy = -slope.dy; - _cairo_pen_stroke_spline_add_convolved_point (stroker, - &stroker->last_point, - &slope, - &stroker->backward_hull_point, - stroker->backward_vertex, - -1); - - _cairo_polygon_add_edge (&stroker->polygon, - &stroker->forward_hull_point, - &stroker->backward_hull_point, - 1); - - status = _cairo_polygon_status (&stroker->polygon); - if (unlikely (status)) - return status; - - status = _cairo_bentley_ottmann_tessellate_polygon (traps, - &stroker->polygon, - CAIRO_FILL_RULE_WINDING); - - return status; -} - -static cairo_status_t -_cairo_pen_stroke_spline_add_point (void *closure, - const cairo_point_t *point) -{ - cairo_pen_stroke_spline_t *stroker = closure; - cairo_slope_t slope; - - _cairo_slope_init (&slope, &stroker->last_point, point); - stroker->forward_vertex = - _cairo_pen_stroke_spline_add_convolved_point (stroker, - &stroker->last_point, - &slope, - &stroker->forward_hull_point, - stroker->forward_vertex, - 1); - - slope.dx = -slope.dx; - slope.dy = -slope.dy; - stroker->backward_vertex = - _cairo_pen_stroke_spline_add_convolved_point (stroker, - &stroker->last_point, - &slope, - &stroker->backward_hull_point, - stroker->backward_vertex, - -1); - stroker->last_point = *point; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_int_status_t -_cairo_pen_stroke_spline_init (cairo_pen_stroke_spline_t *stroker, - const cairo_pen_t *pen, - const cairo_point_t *a, - const cairo_point_t *b, - const cairo_point_t *c, - const cairo_point_t *d) -{ - cairo_int_status_t status; - - if (! _cairo_spline_init (&stroker->spline, - _cairo_pen_stroke_spline_add_point, - stroker, - a, b, c, d)) - { - return CAIRO_INT_STATUS_DEGENERATE; - } - - status = _cairo_pen_init_copy (&stroker->pen, pen); - if (unlikely (status)) - return status; - - _cairo_polygon_init (&stroker->polygon); - - stroker->last_point = *a; - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_pen_stroke_spline_fini (cairo_pen_stroke_spline_t *stroker) -{ - _cairo_polygon_fini (&stroker->polygon); - _cairo_pen_fini (&stroker->pen); -} diff --git a/gfx/cairo/cairo/src/cairo-polygon.c b/gfx/cairo/cairo/src/cairo-polygon.c index 202cb4d731fa..9cb76e557bc4 100644 --- a/gfx/cairo/cairo/src/cairo-polygon.c +++ b/gfx/cairo/cairo/src/cairo-polygon.c @@ -36,6 +36,8 @@ #include "cairoint.h" +#include "cairo-slope-private.h" + void _cairo_polygon_init (cairo_polygon_t *polygon) { @@ -49,6 +51,39 @@ _cairo_polygon_init (cairo_polygon_t *polygon) polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded); polygon->has_current_point = FALSE; + polygon->has_current_edge = FALSE; + polygon->num_limits = 0; + + polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX; + polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN; +} + +void +_cairo_polygon_limit (cairo_polygon_t *polygon, + const cairo_box_t *limits, + int num_limits) +{ + int n; + + polygon->limits = limits; + polygon->num_limits = num_limits; + + if (polygon->num_limits) { + polygon->limit = limits[0]; + for (n = 1; n < num_limits; n++) { + if (limits[n].p1.x < polygon->limit.p1.x) + polygon->limit.p1.x = limits[n].p1.x; + + if (limits[n].p1.y < polygon->limit.p1.y) + polygon->limit.p1.y = limits[n].p1.y; + + if (limits[n].p2.x > polygon->limit.p2.x) + polygon->limit.p2.x = limits[n].p2.x; + + if (limits[n].p2.y > polygon->limit.p2.y) + polygon->limit.p2.y = limits[n].p2.y; + } + } } void @@ -93,17 +128,16 @@ _cairo_polygon_grow (cairo_polygon_t *polygon) return TRUE; } -void -_cairo_polygon_add_edge (cairo_polygon_t *polygon, - const cairo_point_t *p1, - const cairo_point_t *p2, - int dir) +static void +_add_edge (cairo_polygon_t *polygon, + const cairo_point_t *p1, + const cairo_point_t *p2, + int top, int bottom, + int dir) { cairo_edge_t *edge; - /* drop horizontal edges */ - if (p1->y == p2->y) - return; + assert (top < bottom); if (polygon->num_edges == polygon->edges_size) { if (! _cairo_polygon_grow (polygon)) @@ -111,47 +145,350 @@ _cairo_polygon_add_edge (cairo_polygon_t *polygon, } edge = &polygon->edges[polygon->num_edges++]; - if (p1->y < p2->y) { - edge->edge.p1 = *p1; - edge->edge.p2 = *p2; - edge->dir = dir; - } else { - edge->edge.p1 = *p2; - edge->edge.p2 = *p1; - edge->dir = -dir; + edge->line.p1 = *p1; + edge->line.p2 = *p2; + edge->top = top; + edge->bottom = bottom; + edge->dir = dir; + + if (top < polygon->extents.p1.y) + polygon->extents.p1.y = top; + if (bottom > polygon->extents.p2.y) + polygon->extents.p2.y = bottom; + + if (p1->x < polygon->extents.p1.x || p1->x > polygon->extents.p2.x) { + cairo_fixed_t x = p1->x; + if (top != p1->y) + x = _cairo_edge_compute_intersection_x_for_y (p1, p2, top); + if (x < polygon->extents.p1.x) + polygon->extents.p1.x = x; + if (x > polygon->extents.p2.x) + polygon->extents.p2.x = x; + } + + if (p2->x < polygon->extents.p1.x || p2->x > polygon->extents.p2.x) { + cairo_fixed_t x = p2->x; + if (bottom != p2->y) + x = _cairo_edge_compute_intersection_x_for_y (p1, p2, bottom); + if (x < polygon->extents.p1.x) + polygon->extents.p1.x = x; + if (x > polygon->extents.p2.x) + polygon->extents.p2.x = x; } } -void +static void +_add_clipped_edge (cairo_polygon_t *polygon, + const cairo_point_t *p1, + const cairo_point_t *p2, + const int top, const int bottom, + const int dir) +{ + cairo_point_t p[2]; + int top_y, bot_y; + int n; + + for (n = 0; n < polygon->num_limits; n++) { + const cairo_box_t *limits = &polygon->limits[n]; + + if (top >= limits->p2.y) + continue; + if (bottom <= limits->p1.y) + continue; + + if (p1->x <= limits->p1.x && p2->x <= limits->p1.x) + { + p[0].x = limits->p1.x; + p[0].y = limits->p1.y; + top_y = top; + if (top_y < p[0].y) + top_y = p[0].y; + + p[1].x = limits->p1.x; + p[1].y = limits->p2.y; + bot_y = bottom; + if (bot_y > p[1].y) + bot_y = p[1].y; + + _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); + } + else if (p1->x >= limits->p2.x && p2->x >= limits->p2.x) + { + p[0].x = limits->p2.x; + p[0].y = limits->p1.y; + top_y = top; + if (top_y < p[0].y) + top_y = p[0].y; + + p[1].x = limits->p2.x; + p[1].y = limits->p2.y; + bot_y = bottom; + if (bot_y > p[1].y) + bot_y = p[1].y; + + _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); + } + else if (p1->x >= limits->p1.x && p2->x >= limits->p1.x && + p1->x <= limits->p2.x && p2->x <= limits->p2.x) + { + top_y = top; + if (top_y < limits->p1.y) + top_y = limits->p1.y; + + bot_y = bottom; + if (bot_y > limits->p2.y) + bot_y = limits->p2.y; + + _add_edge (polygon, p1, p2, top_y, bot_y, dir); + } + else + { + int left_y, right_y; + int p1_y, p2_y; + + left_y = _cairo_edge_compute_intersection_y_for_x (p1, p2, + limits->p1.x); + right_y = _cairo_edge_compute_intersection_y_for_x (p1, p2, + limits->p2.x); + + if (left_y == right_y) /* horizontal within bounds */ + continue; + + p1_y = top; + p2_y = bottom; + + if (left_y < right_y) { + if (p1->x < limits->p1.x && left_y > limits->p1.y) { + p[0].x = limits->p1.x; + p[0].y = limits->p1.y; + top_y = p1_y; + if (top_y < p[0].y) + top_y = p[0].y; + + p[1].x = limits->p1.x; + p[1].y = limits->p2.y; + bot_y = left_y; + if (bot_y > p[1].y) + bot_y = p[1].y; + + if (bot_y > top_y) + _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); + p1_y = bot_y; + } + + if (p2->x > limits->p2.x && right_y < limits->p2.y) { + p[0].x = limits->p2.x; + p[0].y = limits->p1.y; + top_y = right_y; + if (top_y < p[0].y) + top_y = p[0].y; + + p[1].x = limits->p2.x; + p[1].y = limits->p2.y; + bot_y = p2_y; + if (bot_y > p[1].y) + bot_y = p[1].y; + + if (bot_y > top_y) + _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); + p2_y = top_y; + } + } else { + if (p1->x > limits->p2.x && right_y > limits->p1.y) { + p[0].x = limits->p2.x; + p[0].y = limits->p1.y; + top_y = p1_y; + if (top_y < p[0].y) + top_y = p[0].y; + + p[1].x = limits->p2.x; + p[1].y = limits->p2.y; + bot_y = right_y; + if (bot_y > p[1].y) + bot_y = p[1].y; + + if (bot_y > top_y) + _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); + p1_y = bot_y; + } + + if (p2->x < limits->p1.x && left_y < limits->p2.y) { + p[0].x = limits->p1.x; + p[0].y = limits->p1.y; + top_y = left_y; + if (top_y < p[0].y) + top_y = p[0].y; + + p[1].x = limits->p1.x; + p[1].y = limits->p2.y; + bot_y = p2_y; + if (bot_y > p[1].y) + bot_y = p[1].y; + + if (bot_y > top_y) + _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); + p2_y = top_y; + } + } + + if (p1_y < limits->p1.y) + p1_y = limits->p1.y; + if (p2_y > limits->p2.y) + p2_y = limits->p2.y; + if (p2_y > p1_y) + _add_edge (polygon, p1, p2, p1_y, p2_y, dir); + } + } +} + +static void +_cairo_polygon_add_edge (cairo_polygon_t *polygon, + const cairo_point_t *p1, + const cairo_point_t *p2) +{ + int dir; + + /* drop horizontal edges */ + if (p1->y == p2->y) + return; + + if (p1->y < p2->y) { + dir = 1; + } else { + const cairo_point_t *t; + t = p1, p1 = p2, p2 = t; + dir = -1; + } + + if (polygon->num_limits) { + if (p2->y <= polygon->limit.p1.y) + return; + + if (p1->y >= polygon->limit.p2.y) + return; + + _add_clipped_edge (polygon, p1, p2, p1->y, p2->y, dir); + } else + _add_edge (polygon, p1, p2, p1->y, p2->y, dir); +} + +cairo_status_t +_cairo_polygon_add_external_edge (void *polygon, + const cairo_point_t *p1, + const cairo_point_t *p2) +{ + _cairo_polygon_add_edge (polygon, p1, p2); + return _cairo_polygon_status (polygon); +} + +cairo_status_t +_cairo_polygon_add_line (cairo_polygon_t *polygon, + const cairo_line_t *line, + int top, int bottom, + int dir) +{ + /* drop horizontal edges */ + if (line->p1.y == line->p2.y) + return CAIRO_STATUS_SUCCESS; + + if (bottom <= top) + return CAIRO_STATUS_SUCCESS; + + if (polygon->num_limits) { + if (line->p2.y <= polygon->limit.p1.y) + return CAIRO_STATUS_SUCCESS; + + if (line->p1.y >= polygon->limit.p2.y) + return CAIRO_STATUS_SUCCESS; + + _add_clipped_edge (polygon, &line->p1, &line->p2, top, bottom, dir); + } else + _add_edge (polygon, &line->p1, &line->p2, top, bottom, dir); + + return polygon->status; +} + +/* flattened path operations */ + +cairo_status_t _cairo_polygon_move_to (cairo_polygon_t *polygon, const cairo_point_t *point) { - if (! polygon->has_current_point) + if (polygon->has_current_edge) { + _cairo_polygon_add_edge (polygon, + &polygon->last_point, + &polygon->current_point); + polygon->has_current_edge = FALSE; + } + + if (! polygon->has_current_point) { polygon->first_point = *point; + polygon->has_current_point = TRUE; + } polygon->current_point = *point; - polygon->has_current_point = TRUE; + return polygon->status; } -void +cairo_status_t _cairo_polygon_line_to (cairo_polygon_t *polygon, const cairo_point_t *point) { - if (polygon->has_current_point) - _cairo_polygon_add_edge (polygon, &polygon->current_point, point, 1); + /* squash collinear edges */ + if (polygon->has_current_edge) { + if (polygon->current_point.x != point->x || + polygon->current_point.y != point->y) + { + cairo_slope_t this; - _cairo_polygon_move_to (polygon, point); + _cairo_slope_init (&this, &polygon->current_point, point); + if (_cairo_slope_equal (&polygon->current_edge, &this)) { + polygon->current_point = *point; + return CAIRO_STATUS_SUCCESS; + } + + _cairo_polygon_add_edge (polygon, + &polygon->last_point, + &polygon->current_point); + + polygon->last_point = polygon->current_point; + polygon->current_edge = this; + } + } else if (polygon->has_current_point) { + if (polygon->current_point.x != point->x || + polygon->current_point.y != point->y) + { + polygon->last_point = polygon->current_point; + _cairo_slope_init (&polygon->current_edge, + &polygon->last_point, + point); + polygon->has_current_edge = TRUE; + } + } else { + polygon->first_point = *point; + polygon->has_current_point = TRUE; + } + + polygon->current_point = *point; + return polygon->status; } -void +cairo_status_t _cairo_polygon_close (cairo_polygon_t *polygon) { - if (polygon->has_current_point) { - _cairo_polygon_add_edge (polygon, - &polygon->current_point, - &polygon->first_point, - 1); + cairo_status_t status; + if (polygon->has_current_point) { + status = _cairo_polygon_line_to (polygon, &polygon->first_point); polygon->has_current_point = FALSE; } + + if (polygon->has_current_edge) { + _cairo_polygon_add_edge (polygon, + &polygon->last_point, + &polygon->current_point); + polygon->has_current_edge = FALSE; + } + + return polygon->status; } diff --git a/gfx/cairo/cairo/src/cairo-ps-surface-private.h b/gfx/cairo/cairo/src/cairo-ps-surface-private.h index 8567aff5ccdf..3dc000802dc5 100644 --- a/gfx/cairo/cairo/src/cairo-ps-surface-private.h +++ b/gfx/cairo/cairo/src/cairo-ps-surface-private.h @@ -44,6 +44,7 @@ #include "cairo-ps.h" #include "cairo-surface-private.h" +#include "cairo-surface-clipper-private.h" #include "cairo-pdf-operators-private.h" #include @@ -65,6 +66,7 @@ typedef struct cairo_ps_surface { cairo_content_t content; double width; double height; + cairo_rectangle_int_t page_bbox; int bbox_x1, bbox_y1, bbox_x2, bbox_y2; cairo_matrix_t cairo_to_ps; @@ -88,6 +90,7 @@ typedef struct cairo_ps_surface { cairo_scaled_font_subsets_t *font_subsets; + cairo_list_t document_media; cairo_array_t dsc_header_comments; cairo_array_t dsc_setup_comments; cairo_array_t dsc_page_setup_comments; @@ -97,6 +100,8 @@ typedef struct cairo_ps_surface { cairo_ps_level_t ps_level; cairo_ps_level_t ps_level_used; + cairo_surface_clipper_t clipper; + cairo_pdf_operators_t pdf_operators; cairo_surface_t *paginated_surface; } cairo_ps_surface_t; diff --git a/gfx/cairo/cairo/src/cairo-ps-surface.c b/gfx/cairo/cairo/src/cairo-ps-surface.c index cfe730cbc01a..fd3c73d5cf85 100644 --- a/gfx/cairo/cairo/src/cairo-ps-surface.c +++ b/gfx/cairo/cairo/src/cairo-ps-surface.c @@ -60,7 +60,8 @@ #include "cairo-pdf-operators-private.h" #include "cairo-scaled-font-subsets-private.h" #include "cairo-paginated-private.h" -#include "cairo-meta-surface-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-surface-clipper-private.h" #include "cairo-output-stream-private.h" #include "cairo-type3-glyph-surface-private.h" #include "cairo-image-info-private.h" @@ -73,6 +74,13 @@ #define DEBUG_PS 0 +#if DEBUG_PS +#define DEBUG_FALLBACK(s) \ + fprintf (stderr, "%s::%d -- %s\n", __FUNCTION__, __LINE__, (s)) +#else +#define DEBUG_FALLBACK(s) +#endif + #ifndef HAVE_CTIME_R #define ctime_r(T, BUF) ctime (T) #endif @@ -98,6 +106,40 @@ static const char * _cairo_ps_level_strings[CAIRO_PS_LEVEL_LAST] = "PS Level 3" }; +typedef struct _cairo_page_standard_media { + const char *name; + int width; + int height; +} cairo_page_standard_media_t; + +static const cairo_page_standard_media_t _cairo_page_standard_media[] = +{ + { "A0", 2384, 3371 }, + { "A1", 1685, 2384 }, + { "A2", 1190, 1684 }, + { "A3", 842, 1190 }, + { "A4", 595, 842 }, + { "A5", 420, 595 }, + { "B4", 729, 1032 }, + { "B5", 516, 729 }, + { "Letter", 612, 792 }, + { "Tabloid", 792, 1224 }, + { "Ledger", 1224, 792 }, + { "Legal", 612, 1008 }, + { "Statement", 396, 612 }, + { "Executive", 540, 720 }, + { "Folio", 612, 936 }, + { "Quarto", 610, 780 }, + { "10x14", 720, 1008 }, +}; + +typedef struct _cairo_page_media { + char *name; + int width; + int height; + cairo_list_t link; +} cairo_page_media_t; + static void _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) { @@ -141,6 +183,27 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) "%%%%LanguageLevel: %d\n", level); + if (!cairo_list_is_empty (&surface->document_media)) { + cairo_page_media_t *page; + cairo_bool_t first = TRUE; + + cairo_list_foreach_entry (page, cairo_page_media_t, &surface->document_media, link) { + if (first) { + _cairo_output_stream_printf (surface->final_stream, + "%%%%DocumentMedia: "); + first = FALSE; + } else { + _cairo_output_stream_printf (surface->final_stream, + "%%%%+ "); + } + _cairo_output_stream_printf (surface->final_stream, + "%s %d %d 0 () ()\n", + page->name, + page->width, + page->height); + } + } + num_comments = _cairo_array_num_elements (&surface->dsc_header_comments); comments = _cairo_array_index (&surface->dsc_header_comments, 0); for (i = 0; i < num_comments; i++) { @@ -700,6 +763,138 @@ _cairo_ps_surface_emit_footer (cairo_ps_surface_t *surface) "%%%%EOF\n"); } +static cairo_bool_t +_path_covers_bbox (cairo_ps_surface_t *surface, + cairo_path_fixed_t *path) +{ + cairo_box_t box; + + if (_cairo_path_fixed_is_box (path, &box)) { + cairo_rectangle_int_t rect; + + _cairo_box_round_to_rectangle (&box, &rect); + + /* skip trivial whole-page clips */ + if (_cairo_rectangle_intersect (&rect, &surface->page_bbox)) { + if (rect.x == surface->page_bbox.x && + rect.width == surface->page_bbox.width && + rect.y == surface->page_bbox.y && + rect.height == surface->page_bbox.height) + { + return TRUE; + } + } + } + + return FALSE; +} + +static cairo_status_t +_cairo_ps_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_ps_surface_t *surface = cairo_container_of (clipper, + cairo_ps_surface_t, + clipper); + cairo_output_stream_t *stream = surface->stream; + cairo_status_t status; + + assert (surface->paginated_mode != CAIRO_PAGINATED_MODE_ANALYZE); + +#if DEBUG_PS + _cairo_output_stream_printf (stream, + "%% _cairo_ps_surface_intersect_clip_path\n"); +#endif + + if (path == NULL) { + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (stream, "Q q\n"); + + surface->current_pattern_is_solid_color = FALSE; + _cairo_pdf_operators_reset (&surface->pdf_operators); + + return CAIRO_STATUS_SUCCESS; + } + + if (_path_covers_bbox (surface, path)) + return CAIRO_STATUS_SUCCESS; + + return _cairo_pdf_operators_clip (&surface->pdf_operators, + path, + fill_rule); +} + +/* PLRM specifies a tolerance of 5 points when matching page sizes */ +static cairo_bool_t +_ps_page_dimension_equal (int a, int b) +{ + return (abs (a - b) < 5); +} + +static const char * +_cairo_ps_surface_get_page_media (cairo_ps_surface_t *surface) +{ + int width, height, i; + char buf[50]; + cairo_page_media_t *page; + const char *page_name; + + width = _cairo_lround (surface->width); + height = _cairo_lround (surface->height); + + /* search previously used page sizes */ + cairo_list_foreach_entry (page, cairo_page_media_t, &surface->document_media, link) { + if (_ps_page_dimension_equal (width, page->width) && + _ps_page_dimension_equal (height, page->height)) + return page->name; + } + + /* search list of standard page sizes */ + page_name = NULL; + for (i = 0; i < ARRAY_LENGTH (_cairo_page_standard_media); i++) { + if (_ps_page_dimension_equal (width, _cairo_page_standard_media[i].width) && + _ps_page_dimension_equal (height, _cairo_page_standard_media[i].height)) + { + page_name = _cairo_page_standard_media[i].name; + width = _cairo_page_standard_media[i].width; + height = _cairo_page_standard_media[i].height; + break; + } + } + + page = malloc (sizeof (cairo_page_media_t)); + if (unlikely (page == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + if (page_name) { + page->name = strdup (page_name); + } else { + snprintf (buf, sizeof (buf), "%dx%dmm", + _cairo_lround (surface->width * 25.4/72), + _cairo_lround (surface->height * 25.4/72)); + page->name = strdup (buf); + } + + if (unlikely (page->name == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + page->width = width; + page->height = height; + cairo_list_add_tail (&page->link, &surface->document_media); + + return page->name; +} + static cairo_surface_t * _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, double width, @@ -756,12 +951,16 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, surface->use_string_datasource = FALSE; surface->current_pattern_is_solid_color = FALSE; + _cairo_surface_clipper_init (&surface->clipper, + _cairo_ps_surface_clipper_intersect_clip_path); + _cairo_pdf_operators_init (&surface->pdf_operators, surface->stream, &surface->cairo_to_ps, surface->font_subsets); surface->num_pages = 0; + cairo_list_init (&surface->document_media); _cairo_array_init (&surface->dsc_header_comments, sizeof (char *)); _cairo_array_init (&surface->dsc_setup_comments, sizeof (char *)); _cairo_array_init (&surface->dsc_page_setup_comments, sizeof (char *)); @@ -771,7 +970,6 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, surface->paginated_surface = _cairo_paginated_surface_create ( &surface->base, CAIRO_CONTENT_COLOR_ALPHA, - width, height, &cairo_ps_surface_paginated_backend); status = surface->paginated_surface->status; if (status == CAIRO_STATUS_SUCCESS) { @@ -1083,11 +1281,6 @@ cairo_ps_surface_set_size (cairo_surface_t *surface, cairo_matrix_init (&ps_surface->cairo_to_ps, 1, 0, 0, -1, 0, height_in_points); _cairo_pdf_operators_set_cairo_to_pdf_matrix (&ps_surface->pdf_operators, &ps_surface->cairo_to_ps); - status = _cairo_paginated_surface_set_size (ps_surface->paginated_surface, - width_in_points, - height_in_points); - if (unlikely (status)) - status = _cairo_surface_set_error (surface, status); } /** @@ -1288,15 +1481,6 @@ cairo_ps_surface_dsc_begin_page_setup (cairo_surface_t *surface) } } -static cairo_surface_t * -_cairo_ps_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - return cairo_meta_surface_create (content, width, height); -} - static cairo_status_t _cairo_ps_surface_finish (void *abstract_surface) { @@ -1328,6 +1512,17 @@ CLEANUP: if (status == CAIRO_STATUS_SUCCESS) status = status2; + while (! cairo_list_is_empty (&surface->document_media)) { + cairo_page_media_t *page; + + page = cairo_list_first_entry (&surface->document_media, + cairo_page_media_t, + link); + cairo_list_del (&page->link); + free (page->name); + free (page); + } + num_comments = _cairo_array_num_elements (&surface->dsc_header_comments); comments = _cairo_array_index (&surface->dsc_header_comments, 0); for (i = 0; i < num_comments; i++) @@ -1346,6 +1541,8 @@ CLEANUP: free (comments[i]); _cairo_array_fini (&surface->dsc_page_setup_comments); + _cairo_surface_clipper_reset (&surface->clipper); + return status; } @@ -1369,8 +1566,11 @@ _cairo_ps_surface_end_page (cairo_ps_surface_t *surface) if (unlikely (status)) return status; - _cairo_output_stream_printf (surface->stream, - "Q\n"); + if (surface->clipper.clip.path != NULL) { + _cairo_output_stream_printf (surface->stream, "Q Q\n"); + _cairo_surface_clipper_reset (&surface->clipper); + } else + _cairo_output_stream_printf (surface->stream, "Q\n"); return CAIRO_STATUS_SUCCESS; } @@ -1448,9 +1648,7 @@ _cairo_ps_surface_analyze_surface_pattern_transparency (cairo_ps_surface_t static cairo_bool_t surface_pattern_supported (const cairo_surface_pattern_t *pattern) { - cairo_extend_t extend; - - if (_cairo_surface_is_meta (pattern->surface)) + if (_cairo_surface_is_recording (pattern->surface)) return TRUE; if (pattern->surface->backend->acquire_source_image == NULL) @@ -1465,24 +1663,7 @@ surface_pattern_supported (const cairo_surface_pattern_t *pattern) return FALSE; */ - /* Cast away the const, trusting get_extend not to muck with it. - * And I really wish I had a way to cast away just the const, and - * not potentially coerce this pointer to an incorrect type at the - * same time. :-( - */ - extend = cairo_pattern_get_extend ((cairo_pattern_t*)&pattern->base); - switch (extend) { - case CAIRO_EXTEND_NONE: - case CAIRO_EXTEND_REPEAT: - case CAIRO_EXTEND_REFLECT: - /* There's no point returning FALSE for EXTEND_PAD, as the image - * surface does not currently implement it either */ - case CAIRO_EXTEND_PAD: - return TRUE; - } - - ASSERT_NOT_REACHED; - return FALSE; + return TRUE; } static cairo_bool_t @@ -1567,20 +1748,21 @@ _cairo_ps_surface_analyze_operation (cairo_ps_surface_t *surface, } if (! pattern_supported (surface, pattern)) + { return CAIRO_INT_STATUS_UNSUPPORTED; + } - if (!(op == CAIRO_OPERATOR_SOURCE || - op == CAIRO_OPERATOR_OVER)) + if (! (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER)) return CAIRO_INT_STATUS_UNSUPPORTED; if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; - if ( _cairo_surface_is_meta (surface_pattern->surface)) { + if ( _cairo_surface_is_recording (surface_pattern->surface)) { if (pattern->extend == CAIRO_EXTEND_PAD) return CAIRO_INT_STATUS_UNSUPPORTED; else - return CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN; + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; } } @@ -1596,7 +1778,6 @@ _cairo_ps_surface_analyze_operation (cairo_ps_surface_t *surface, * render stage and we blend the transparency into the white * background to convert the pattern to opaque. */ - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; @@ -1606,8 +1787,8 @@ _cairo_ps_surface_analyze_operation (cairo_ps_surface_t *surface, if (_cairo_pattern_is_opaque (pattern)) return CAIRO_STATUS_SUCCESS; - else - return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; + + return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; } static cairo_bool_t @@ -1782,7 +1963,7 @@ _base85_array_stream_create (cairo_output_stream_t *output) } -/* PS Output - this section handles output of the parts of the meta +/* PS Output - this section handles output of the parts of the recording * surface we can render natively in PS. */ static cairo_status_t @@ -1824,7 +2005,8 @@ _cairo_ps_surface_flatten_image_transparency (cairo_ps_surface_t *surface, 0, 0, 0, 0, image->width, - image->height); + image->height, + NULL); if (unlikely (status)) goto fail; @@ -1868,10 +2050,14 @@ _cairo_ps_surface_emit_base85_string (cairo_ps_surface_t *surface, _cairo_output_stream_write (base85_stream, data, length); status = _cairo_output_stream_destroy (base85_stream); + + /* Mark end of base85 data */ + _cairo_output_stream_printf (string_array_stream, "~>"); status2 = _cairo_output_stream_destroy (string_array_stream); if (status == CAIRO_STATUS_SUCCESS) status = status2; + return status; } @@ -2109,6 +2295,7 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, data_compressed, data_compressed_size, FALSE); + _cairo_output_stream_printf (surface->stream, "\n"); } else { status = CAIRO_STATUS_SUCCESS; } @@ -2215,48 +2402,61 @@ _cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, } static cairo_status_t -_cairo_ps_surface_emit_meta_surface (cairo_ps_surface_t *surface, - cairo_surface_t *meta_surface) +_cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface, + cairo_surface_t *recording_surface) { double old_width, old_height; cairo_matrix_t old_cairo_to_ps; cairo_content_t old_content; - cairo_clip_t *old_clip; - cairo_rectangle_int_t meta_extents; + cairo_rectangle_int_t old_page_bbox; + cairo_box_t bbox; cairo_status_t status; - status = _cairo_surface_get_extents (meta_surface, &meta_extents); - if (unlikely (status)) - return status; - old_content = surface->content; old_width = surface->width; old_height = surface->height; + old_page_bbox = surface->page_bbox; old_cairo_to_ps = surface->cairo_to_ps; - old_clip = _cairo_surface_get_clip (&surface->base); - surface->width = meta_extents.width; - surface->height = meta_extents.height; + + status = + _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, + &bbox, + NULL); + if (unlikely (status)) + return status; + +#if DEBUG_PS + _cairo_output_stream_printf (surface->stream, + "%% _cairo_ps_surface_emit_recording_surface (%f, %f), (%f, %f)\n", + _cairo_fixed_to_double (bbox.p1.x), + _cairo_fixed_to_double (bbox.p1.y), + _cairo_fixed_to_double (bbox.p2.x), + _cairo_fixed_to_double (bbox.p2.y)); +#endif + + surface->width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x); + surface->height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y); + _cairo_box_round_to_rectangle (&bbox, &surface->page_bbox); + surface->current_pattern_is_solid_color = FALSE; _cairo_pdf_operators_reset (&surface->pdf_operators); cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, surface->height); _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, &surface->cairo_to_ps); - _cairo_output_stream_printf (surface->stream, - " q\n" - " 0 0 %f %f rectclip\n", - surface->width, - surface->height); + _cairo_output_stream_printf (surface->stream, " q\n"); - if (cairo_surface_get_content (meta_surface) == CAIRO_CONTENT_COLOR) { + if (cairo_surface_get_content (recording_surface) == CAIRO_CONTENT_COLOR) { surface->content = CAIRO_CONTENT_COLOR; _cairo_output_stream_printf (surface->stream, - " 0 g 0 0 %f %f rectfill\n", - surface->width, - surface->height); + " 0 g %d %d %d %d rectfill\n", + surface->page_bbox.x, + surface->page_bbox.y, + surface->page_bbox.width, + surface->page_bbox.height); } - status = _cairo_meta_surface_replay_region (meta_surface, &surface->base, - CAIRO_META_REGION_NATIVE); + status = _cairo_recording_surface_replay_region (recording_surface, &surface->base, + CAIRO_RECORDING_REGION_NATIVE); assert (status != CAIRO_INT_STATUS_UNSUPPORTED); if (unlikely (status)) return status; @@ -2265,17 +2465,14 @@ _cairo_ps_surface_emit_meta_surface (cairo_ps_surface_t *surface, if (unlikely (status)) return status; - _cairo_output_stream_printf (surface->stream, - " Q\n"); + _cairo_output_stream_printf (surface->stream, " Q\n"); surface->content = old_content; surface->width = old_width; surface->height = old_height; + surface->page_bbox = old_page_bbox; surface->current_pattern_is_solid_color = FALSE; _cairo_pdf_operators_reset (&surface->pdf_operators); surface->cairo_to_ps = old_cairo_to_ps; - status = _cairo_surface_set_clip (&surface->base, old_clip); - if (unlikely (status)) - return status; _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, &surface->cairo_to_ps); @@ -2331,27 +2528,30 @@ _cairo_ps_surface_acquire_surface (cairo_ps_surface_t *surface, cairo_rectangle_int_t *extents, int *width, int *height, - int *origin_x, - int *origin_y) + int *origin_x, + int *origin_y) { cairo_status_t status; - cairo_surface_t *pad_image; + cairo_surface_t *pad_image; int x = 0; int y = 0; surface->acquired_image = NULL; surface->image = NULL; - if (_cairo_surface_is_meta (pattern->surface)) { - cairo_surface_t *meta_surface = pattern->surface; - cairo_rectangle_int_t pattern_extents; + if (_cairo_surface_is_recording (pattern->surface)) { + cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) pattern->surface; + cairo_box_t bbox; + cairo_rectangle_int_t extents; - status = _cairo_surface_get_extents (meta_surface, &pattern_extents); + status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL); if (unlikely (status)) return status; - *width = pattern_extents.width; - *height = pattern_extents.height; + _cairo_box_round_to_rectangle (&bbox, &extents); + *width = extents.width; + *height =extents.height; + return CAIRO_STATUS_SUCCESS; } else { status = _cairo_surface_acquire_source_image (pattern->surface, &surface->acquired_image, @@ -2391,7 +2591,8 @@ _cairo_ps_surface_acquire_surface (cairo_ps_surface_t *surface, 0, 0, 0, 0, rect.width, - rect.height); + rect.height, + NULL); _cairo_pattern_fini (&pad_pattern.base); if (unlikely (status)) { if (pad_image != &surface->acquired_image->base) @@ -2406,13 +2607,11 @@ _cairo_ps_surface_acquire_surface (cairo_ps_surface_t *surface, *height = surface->image->height; *origin_x = x; *origin_y = y; + return CAIRO_STATUS_SUCCESS; } - return CAIRO_STATUS_SUCCESS; - BAIL: _cairo_ps_surface_release_surface (surface, pattern); - return status; } @@ -2425,13 +2624,12 @@ _cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface, { cairo_status_t status; - if (_cairo_surface_is_meta (pattern->surface)) { - cairo_surface_t *meta_surface = pattern->surface; + if (_cairo_surface_is_recording (pattern->surface)) { + cairo_surface_t *recording_surface = pattern->surface; - status = _cairo_ps_surface_emit_meta_surface (surface, - meta_surface); + status = _cairo_ps_surface_emit_recording_surface (surface, recording_surface); } else { - if (cairo_pattern_get_extend (&pattern->base) != CAIRO_EXTEND_PAD) { + if (pattern->base.extend != CAIRO_EXTEND_PAD) { status = _cairo_ps_surface_emit_jpeg_image (surface, pattern->surface, width, height); if (status != CAIRO_INT_STATUS_UNSUPPORTED) @@ -2452,7 +2650,7 @@ _cairo_ps_surface_release_surface (cairo_ps_surface_t *surface, if (surface->image != surface->acquired_image) cairo_surface_destroy (&surface->image->base); - if (! _cairo_surface_is_meta (pattern->surface)) { + if (! _cairo_surface_is_recording (pattern->surface)) { _cairo_surface_release_source_image (pattern->surface, surface->acquired_image, surface->image_extra); @@ -2462,6 +2660,35 @@ _cairo_ps_surface_release_surface (cairo_ps_surface_t *surface, surface->image = NULL; } +static void +_path_fixed_init_rectangle (cairo_path_fixed_t *path, + cairo_rectangle_int_t *rect) +{ + cairo_status_t status; + + _cairo_path_fixed_init (path); + + status = _cairo_path_fixed_move_to (path, + _cairo_fixed_from_int (rect->x), + _cairo_fixed_from_int (rect->y)); + assert (status == CAIRO_STATUS_SUCCESS); + status = _cairo_path_fixed_rel_line_to (path, + _cairo_fixed_from_int (rect->width), + _cairo_fixed_from_int (0)); + assert (status == CAIRO_STATUS_SUCCESS); + status = _cairo_path_fixed_rel_line_to (path, + _cairo_fixed_from_int (0), + _cairo_fixed_from_int (rect->height)); + assert (status == CAIRO_STATUS_SUCCESS); + status = _cairo_path_fixed_rel_line_to (path, + _cairo_fixed_from_int (-rect->width), + _cairo_fixed_from_int (0)); + assert (status == CAIRO_STATUS_SUCCESS); + + status = _cairo_path_fixed_close_path (path); + assert (status == CAIRO_STATUS_SUCCESS); +} + static cairo_status_t _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, cairo_surface_pattern_t *pattern, @@ -2471,9 +2698,14 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, cairo_status_t status; int width, height; cairo_matrix_t cairo_p2d, ps_p2d; + cairo_path_fixed_t path; int origin_x = 0; int origin_y = 0; + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + status = _cairo_ps_surface_acquire_surface (surface, pattern, extents, @@ -2482,6 +2714,14 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, if (unlikely (status)) return status; + _path_fixed_init_rectangle (&path, extents); + status = _cairo_pdf_operators_clip (&surface->pdf_operators, + &path, + CAIRO_FILL_RULE_WINDING); + _cairo_path_fixed_fini (&path); + if (unlikely (status)) + return status; + cairo_p2d = pattern->base.matrix; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { @@ -2540,7 +2780,6 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, int pattern_height = 0; /* squelch bogus compiler warning */ double xstep, ystep; cairo_matrix_t cairo_p2d, ps_p2d; - cairo_rectangle_int_t surface_extents; cairo_bool_t old_use_string_datasource; int origin_x = 0; int origin_y = 0; @@ -2550,12 +2789,6 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); - ps_p2d = surface->cairo_to_ps; - cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d); - cairo_matrix_translate (&ps_p2d, -origin_x, -origin_y); - cairo_matrix_translate (&ps_p2d, 0.0, pattern_height); - cairo_matrix_scale (&ps_p2d, 1.0, -1.0); - status = _cairo_ps_surface_acquire_surface (surface, pattern, extents, @@ -2669,17 +2902,13 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, _cairo_output_stream_printf (surface->stream, ">>\n"); - status = _cairo_surface_get_extents (&surface->base, &surface_extents); - if (unlikely (status)) - return status; - cairo_p2d = pattern->base.matrix; status = cairo_matrix_invert (&cairo_p2d); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_init_identity (&ps_p2d); - cairo_matrix_translate (&ps_p2d, 0.0, surface_extents.height); + cairo_matrix_translate (&ps_p2d, 0.0, surface->height); cairo_matrix_scale (&ps_p2d, 1.0, -1.0); cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d); cairo_matrix_translate (&ps_p2d, 0.0, pattern_height); @@ -2934,9 +3163,9 @@ _cairo_ps_surface_emit_linear_pattern (cairo_ps_surface_t *surface, dx = fabs (x2 - x1); dy = fabs (y2 - y1); if (dx > 1e-6) - x_rep = (int) ceil (surface->width/dx); + x_rep = ceil (surface->width/dx); if (dy > 1e-6) - y_rep = (int) ceil (surface->height/dy); + y_rep = ceil (surface->height/dy); repeat_end = MAX (x_rep, y_rep); repeat_begin = -repeat_end; @@ -3106,6 +3335,10 @@ _cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface, { cairo_status_t status; + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; @@ -3163,43 +3396,7 @@ _cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface, return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_ps_surface_intersect_clip_path (void *abstract_surface, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_ps_surface_t *surface = abstract_surface; - cairo_output_stream_t *stream = surface->stream; - cairo_status_t status; - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return CAIRO_STATUS_SUCCESS; - -#if DEBUG_PS - _cairo_output_stream_printf (stream, - "%% _cairo_ps_surface_intersect_clip_path\n"); -#endif - - if (path == NULL) { - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (stream, "Q q\n"); - surface->current_pattern_is_solid_color = FALSE; - _cairo_pdf_operators_reset (&surface->pdf_operators); - - return CAIRO_STATUS_SUCCESS; - } - - return _cairo_pdf_operators_clip (&surface->pdf_operators, - path, - fill_rule); -} - -static cairo_int_status_t +static cairo_bool_t _cairo_ps_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { @@ -3212,10 +3409,10 @@ _cairo_ps_surface_get_extents (void *abstract_surface, * mention the aribitray limitation of width to a short(!). We * may need to come up with a better interface for get_extents. */ - rectangle->width = (int) ceil (surface->width); - rectangle->height = (int) ceil (surface->height); + rectangle->width = ceil (surface->width); + rectangle->height = ceil (surface->height); - return CAIRO_STATUS_SUCCESS; + return TRUE; } static void @@ -3233,7 +3430,7 @@ static cairo_int_status_t _cairo_ps_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_rectangle_int_t *paint_extents) + cairo_clip_t *clip) { cairo_ps_surface_t *surface = abstract_surface; cairo_output_stream_t *stream = surface->stream; @@ -3250,11 +3447,16 @@ _cairo_ps_surface_paint (void *abstract_surface, "%% _cairo_ps_surface_paint\n"); #endif - status = _cairo_surface_get_extents (&surface->base, &extents); + status = _cairo_surface_paint_extents (&surface->base, + op, source, clip, + &extents); if (unlikely (status)) return status; - status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (! _cairo_rectangle_intersect (&extents, &surface->page_bbox)) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) return status; @@ -3262,28 +3464,29 @@ _cairo_ps_surface_paint (void *abstract_surface, (source->extend == CAIRO_EXTEND_NONE || source->extend == CAIRO_EXTEND_PAD)) { - _cairo_output_stream_printf (stream, "q 0 0 %d %d rectclip\n", - extents.width, - extents.height); + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + _cairo_output_stream_printf (stream, "q\n"); status = _cairo_ps_surface_paint_surface (surface, (cairo_surface_pattern_t *) source, - paint_extents, op); + &extents, op); if (unlikely (status)) return status; _cairo_output_stream_printf (stream, "Q\n"); } else { - status = _cairo_ps_surface_emit_pattern (surface, source, paint_extents, op); + status = _cairo_ps_surface_emit_pattern (surface, source, &extents, op); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) return CAIRO_STATUS_SUCCESS; if (unlikely (status)) return status; - _cairo_output_stream_printf (stream, "0 0 %d %d rectfill\n", - extents.width, - extents.height); + _cairo_output_stream_printf (stream, "%d %d %d %d rectfill\n", + extents.x, extents.y, + extents.width, extents.height); } return CAIRO_STATUS_SUCCESS; @@ -3299,10 +3502,11 @@ _cairo_ps_surface_stroke (void *abstract_surface, cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_ps_surface_t *surface = abstract_surface; cairo_int_status_t status; + cairo_rectangle_int_t extents; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return _cairo_ps_surface_analyze_operation (surface, op, source); @@ -3314,7 +3518,23 @@ _cairo_ps_surface_stroke (void *abstract_surface, "%% _cairo_ps_surface_stroke\n"); #endif - status = _cairo_ps_surface_emit_pattern (surface, source, extents, op); + status = _cairo_surface_stroke_extents (&surface->base, + op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip, &extents); + if (unlikely (status)) + return status; + + if (! _cairo_rectangle_intersect (&extents, &surface->page_bbox)) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _cairo_ps_surface_emit_pattern (surface, source, &extents, op); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) return CAIRO_STATUS_SUCCESS; @@ -3336,10 +3556,11 @@ _cairo_ps_surface_fill (void *abstract_surface, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_ps_surface_t *surface = abstract_surface; cairo_int_status_t status; + cairo_rectangle_int_t extents; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return _cairo_ps_surface_analyze_operation (surface, op, source); @@ -3351,14 +3572,29 @@ _cairo_ps_surface_fill (void *abstract_surface, "%% _cairo_ps_surface_fill\n"); #endif + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + status = _cairo_surface_fill_extents (&surface->base, + op, source, + path, fill_rule, + tolerance, antialias, + clip, &extents); + if (unlikely (status)) + return status; + + if (! _cairo_rectangle_intersect (&extents, &surface->page_bbox)) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + if (source->type == CAIRO_PATTERN_TYPE_SURFACE && (source->extend == CAIRO_EXTEND_NONE || source->extend == CAIRO_EXTEND_PAD)) { - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - _cairo_output_stream_printf (surface->stream, "q\n"); status = _cairo_pdf_operators_clip (&surface->pdf_operators, @@ -3369,14 +3605,14 @@ _cairo_ps_surface_fill (void *abstract_surface, status = _cairo_ps_surface_paint_surface (surface, (cairo_surface_pattern_t *) source, - extents, op); + &extents, op); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->stream, "Q\n"); _cairo_pdf_operators_reset (&surface->pdf_operators); } else { - status = _cairo_ps_surface_emit_pattern (surface, source, extents, op); + status = _cairo_ps_surface_emit_pattern (surface, source, &extents, op); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) return CAIRO_STATUS_SUCCESS; @@ -3398,11 +3634,12 @@ _cairo_ps_surface_show_glyphs (void *abstract_surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip, + int *remaining_glyphs) { cairo_ps_surface_t *surface = abstract_surface; cairo_status_t status; + cairo_rectangle_int_t extents; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return _cairo_ps_surface_analyze_operation (surface, op, source); @@ -3417,7 +3654,22 @@ _cairo_ps_surface_show_glyphs (void *abstract_surface, if (num_glyphs <= 0) return CAIRO_STATUS_SUCCESS; - status = _cairo_ps_surface_emit_pattern (surface, source, extents, op); + status = _cairo_surface_glyphs_extents (&surface->base, + op, source, + glyphs, num_glyphs, + scaled_font, + clip, &extents); + if (unlikely (status)) + return status; + + if (! _cairo_rectangle_intersect (&extents, &surface->page_bbox)) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _cairo_ps_surface_emit_pattern (surface, source, &extents, op); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) return CAIRO_STATUS_SUCCESS; @@ -3437,8 +3689,16 @@ _cairo_ps_surface_set_paginated_mode (void *abstract_surface, cairo_paginated_mode_t paginated_mode) { cairo_ps_surface_t *surface = abstract_surface; + cairo_status_t status; surface->paginated_mode = paginated_mode; + + if (surface->clipper.clip.path != NULL) { + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + + _cairo_output_stream_printf (surface->stream, "Q\n"); + _cairo_surface_clipper_reset (&surface->clipper); + } } static cairo_int_status_t @@ -3449,19 +3709,26 @@ _cairo_ps_surface_set_bounding_box (void *abstract_surface, int i, num_comments; char **comments; int x1, y1, x2, y2; + cairo_bool_t has_page_media; + const char *page_media; if (surface->eps) { - x1 = (int) floor (_cairo_fixed_to_double (bbox->p1.x)); - y1 = (int) floor (surface->height - _cairo_fixed_to_double (bbox->p2.y)); - x2 = (int) ceil (_cairo_fixed_to_double (bbox->p2.x)); - y2 = (int) ceil (surface->height - _cairo_fixed_to_double (bbox->p1.y)); + x1 = floor (_cairo_fixed_to_double (bbox->p1.x)); + y1 = floor (surface->height - _cairo_fixed_to_double (bbox->p2.y)); + x2 = ceil (_cairo_fixed_to_double (bbox->p2.x)); + y2 = ceil (surface->height - _cairo_fixed_to_double (bbox->p1.y)); } else { x1 = 0; y1 = 0; - x2 = (int) ceil (surface->width); - y2 = (int) ceil (surface->height); + x2 = ceil (surface->width); + y2 = ceil (surface->height); } + surface->page_bbox.x = x1; + surface->page_bbox.y = y1; + surface->page_bbox.width = x2 - x1; + surface->page_bbox.height = y2 - y1; + _cairo_output_stream_printf (surface->stream, "%%%%Page: %d %d\n", surface->num_pages, @@ -3470,23 +3737,40 @@ _cairo_ps_surface_set_bounding_box (void *abstract_surface, _cairo_output_stream_printf (surface->stream, "%%%%BeginPageSetup\n"); + has_page_media = FALSE; num_comments = _cairo_array_num_elements (&surface->dsc_page_setup_comments); comments = _cairo_array_index (&surface->dsc_page_setup_comments, 0); for (i = 0; i < num_comments; i++) { _cairo_output_stream_printf (surface->stream, "%s\n", comments[i]); + if (strncmp (comments[i], "%%PageMedia:", 11) == 0) + has_page_media = TRUE; free (comments[i]); comments[i] = NULL; } _cairo_array_truncate (&surface->dsc_page_setup_comments, 0); + if (!has_page_media && !surface->eps) { + page_media = _cairo_ps_surface_get_page_media (surface); + if (unlikely (page_media == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->stream, + "%%%%PageMedia: %s\n", + page_media); + } + _cairo_output_stream_printf (surface->stream, "%%%%PageBoundingBox: %d %d %d %d\n", x1, y1, x2, y2); _cairo_output_stream_printf (surface->stream, "%%%%EndPageSetup\n" - "q\n"); + "q %d %d %d %d rectclip q\n", + surface->page_bbox.x, + surface->page_bbox.y, + surface->page_bbox.width, + surface->page_bbox.height); if (surface->num_pages == 1) { surface->bbox_x1 = x1; @@ -3517,7 +3801,7 @@ _cairo_ps_surface_supports_fine_grained_fallbacks (void *abstract_surface) static const cairo_surface_backend_t cairo_ps_surface_backend = { CAIRO_SURFACE_TYPE_PS, - _cairo_ps_surface_create_similar, + NULL, /* create similar: handled by wrapper */ _cairo_ps_surface_finish, NULL, /* acquire_source_image */ NULL, /* release_source_image */ @@ -3531,8 +3815,6 @@ static const cairo_surface_backend_t cairo_ps_surface_backend = { NULL, /* check_span_renderer */ NULL, /* cairo_ps_surface_copy_page */ _cairo_ps_surface_show_page, - NULL, /* set_clip_region */ - _cairo_ps_surface_intersect_clip_path, _cairo_ps_surface_get_extents, NULL, /* old_show_glyphs */ _cairo_ps_surface_get_font_options, diff --git a/gfx/cairo/cairo/src/cairo-qt.h b/gfx/cairo/cairo/src/cairo-qt.h new file mode 100644 index 000000000000..d16087dd7e5a --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-qt.h @@ -0,0 +1,89 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2008 Mozilla Corporation + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Mozilla Corporation. + * + * Contributor(s): + * Vladimir Vukicevic + */ + +#ifndef CAIRO_QT_H +#define CAIRO_QT_H + +#include "cairo.h" + +#if CAIRO_HAS_QT_SURFACE + +#if defined(__cplusplus) + +class QPainter; +class QImage; + +cairo_public cairo_surface_t * +cairo_qt_surface_create (QPainter *painter); + +cairo_public cairo_surface_t * +cairo_qt_surface_create_with_qimage (cairo_format_t format, + int width, + int height); + +cairo_public cairo_surface_t * +cairo_qt_surface_create_with_qpixmap (cairo_content_t content, + int width, + int height); + +cairo_public QPainter * +cairo_qt_surface_get_qpainter (cairo_surface_t *surface); + +/* XXX needs hooking to generic surface layer, my vote is for +cairo_public cairo_surface_t * +cairo_surface_map_image (cairo_surface_t *surface); +cairo_public void +cairo_surface_unmap_image (cairo_surface_t *surface, cairo_surface_t *image); +*/ +cairo_public cairo_surface_t * +cairo_qt_surface_get_image (cairo_surface_t *surface); + +cairo_public QImage * +cairo_qt_surface_get_qimage (cairo_surface_t *surface); + +#else /* ! __cplusplus */ + +# warning cairo-qt only exports a C++ interface + +#endif /* __cplusplus */ + +#else /* CAIRO_HAS_QT_SURFACE */ + +# error Cairo was not compiled with support for the Qt backend + +#endif /* CAIRO_HAS_QT_SURFACE */ + +#endif /* CAIRO_QT_H */ diff --git a/gfx/cairo/cairo/src/cairo-quartz-font.c b/gfx/cairo/cairo/src/cairo-quartz-font.c index 6cf88397cf5c..d342e38df524 100644 --- a/gfx/cairo/cairo/src/cairo-quartz-font.c +++ b/gfx/cairo/cairo/src/cairo-quartz-font.c @@ -345,10 +345,8 @@ static cairo_quartz_font_face_t * _cairo_quartz_scaled_to_face (void *abstract_font) { cairo_quartz_scaled_font_t *sfont = (cairo_quartz_scaled_font_t*) abstract_font; - cairo_font_face_t *font_face = cairo_scaled_font_get_font_face (&sfont->base); - if (!font_face || font_face->backend->type != CAIRO_FONT_TYPE_QUARTZ) - return NULL; - + cairo_font_face_t *font_face = sfont->base.font_face; + assert (font_face->backend->type == CAIRO_FONT_TYPE_QUARTZ); return (cairo_quartz_font_face_t*) font_face; } diff --git a/gfx/cairo/cairo/src/cairo-quartz-image-surface.c b/gfx/cairo/cairo/src/cairo-quartz-image-surface.c index 8cda2968117c..5a624ebfdbad 100644 --- a/gfx/cairo/cairo/src/cairo-quartz-image-surface.c +++ b/gfx/cairo/cairo/src/cairo-quartz-image-surface.c @@ -58,9 +58,8 @@ _cairo_quartz_image_surface_create_similar (void *asurface, int height) { cairo_surface_t *result; - cairo_surface_t *isurf = cairo_image_surface_create (_cairo_format_from_content (content), - width, - height); + cairo_surface_t *isurf = + _cairo_image_surface_create_with_content (content, width, height); if (cairo_surface_status(isurf)) return isurf; @@ -108,18 +107,16 @@ _cairo_quartz_image_surface_acquire_dest_image (void *asurface, *image_extra = NULL; return CAIRO_STATUS_SUCCESS; - } -static cairo_int_status_t +static cairo_bool_t _cairo_quartz_image_surface_get_extents (void *asurface, cairo_rectangle_int_t *extents) { cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; *extents = surface->extents; - - return CAIRO_STATUS_SUCCESS; + return TRUE; } /* we assume some drawing happened to the image buffer; make sure it's @@ -168,8 +165,6 @@ static const cairo_surface_backend_t cairo_quartz_image_surface_backend = { NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ - NULL, /* set_clip_region */ - NULL, /* intersect_clip_path */ _cairo_quartz_image_surface_get_extents, NULL, /* old_show_glyphs */ NULL, /* get_font_options */ @@ -185,7 +180,6 @@ static const cairo_surface_backend_t cairo_quartz_image_surface_backend = { NULL, /* surface_show_glyphs */ NULL, /* snapshot */ NULL, /* is_similar */ - NULL, /* reset */ NULL /* fill_stroke */ }; diff --git a/gfx/cairo/cairo/src/cairo-quartz-private.h b/gfx/cairo/cairo/src/cairo-quartz-private.h index 8ba896874c9e..d7e07ac8f9e1 100644 --- a/gfx/cairo/cairo/src/cairo-quartz-private.h +++ b/gfx/cairo/cairo/src/cairo-quartz-private.h @@ -42,6 +42,7 @@ #if CAIRO_HAS_QUARTZ_SURFACE #include "cairo-quartz.h" +#include "cairo-surface-clipper-private.h" typedef struct cairo_quartz_surface { cairo_surface_t base; @@ -52,6 +53,7 @@ typedef struct cairo_quartz_surface { void *imageData; cairo_surface_t *imageSurfaceEquiv; + cairo_surface_clipper_t clipper; cairo_rectangle_int_t extents; /* These are stored while drawing operations are in place, set up diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c index 971c78028816..df063bff3964 100644 --- a/gfx/cairo/cairo/src/cairo-quartz-surface.c +++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c @@ -38,6 +38,7 @@ #include "cairoint.h" #include "cairo-quartz-private.h" +#include "cairo-surface-clipper-private.h" #include @@ -65,6 +66,11 @@ #define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0) +/* Here are some of the differences between cairo and CoreGraphics + - cairo has only a single source active at once vs. CoreGraphics having + separate sources for stroke and fill +*/ + /* This method is private, but it exists. Its params are are exposed * as args to the NS* method, but not as CG. */ @@ -199,16 +205,16 @@ _cairo_quartz_create_cgimage (cairo_format_t format, bitsPerPixel = 32; break; - /* XXX -- should use CGImageMaskCreate! */ case CAIRO_FORMAT_A8: - if (colorSpace == NULL) - colorSpace = CGColorSpaceCreateDeviceGray(); - bitinfo = kCGImageAlphaNone; bitsPerComponent = 8; bitsPerPixel = 8; break; case CAIRO_FORMAT_A1: + bitsPerComponent = 1; + bitsPerPixel = 1; + break; + default: return NULL; } @@ -225,16 +231,26 @@ _cairo_quartz_create_cgimage (cairo_format_t format, goto FINISH; } - image = CGImageCreate (width, height, - bitsPerComponent, - bitsPerPixel, - stride, - colorSpace, - bitinfo, - dataProvider, - NULL, - interpolate, - kCGRenderingIntentDefault); + if (format == CAIRO_FORMAT_A8 || format == CAIRO_FORMAT_A1) { + float decode[] = {1.0, 0.0}; + image = CGImageMaskCreate (width, height, + bitsPerComponent, + bitsPerPixel, + stride, + dataProvider, + decode, + interpolate); + } else + image = CGImageCreate (width, height, + bitsPerComponent, + bitsPerPixel, + stride, + colorSpace, + bitinfo, + dataProvider, + NULL, + interpolate, + kCGRenderingIntentDefault); FINISH: @@ -384,7 +400,7 @@ _cairo_quartz_cairo_path_to_quartz_context (cairo_path_fixed_t *path, */ static PrivateCGCompositeMode -_cairo_quartz_cairo_operator_to_quartz (cairo_operator_t op) +_cairo_quartz_cairo_operator_to_quartz_composite (cairo_operator_t op) { switch (op) { case CAIRO_OPERATOR_CLEAR: @@ -394,39 +410,150 @@ _cairo_quartz_cairo_operator_to_quartz (cairo_operator_t op) case CAIRO_OPERATOR_OVER: return kPrivateCGCompositeSourceOver; case CAIRO_OPERATOR_IN: - /* XXX This doesn't match image output */ return kPrivateCGCompositeSourceIn; case CAIRO_OPERATOR_OUT: - /* XXX This doesn't match image output */ return kPrivateCGCompositeSourceOut; case CAIRO_OPERATOR_ATOP: return kPrivateCGCompositeSourceAtop; - - case CAIRO_OPERATOR_DEST: - /* XXX this is handled specially (noop)! */ - return kPrivateCGCompositeCopy; case CAIRO_OPERATOR_DEST_OVER: return kPrivateCGCompositeDestinationOver; case CAIRO_OPERATOR_DEST_IN: - /* XXX This doesn't match image output */ return kPrivateCGCompositeDestinationIn; case CAIRO_OPERATOR_DEST_OUT: return kPrivateCGCompositeDestinationOut; case CAIRO_OPERATOR_DEST_ATOP: - /* XXX This doesn't match image output */ return kPrivateCGCompositeDestinationAtop; - case CAIRO_OPERATOR_XOR: - return kPrivateCGCompositeXOR; /* This will generate strange results */ + return kPrivateCGCompositeXOR; case CAIRO_OPERATOR_ADD: return kPrivateCGCompositePlusLighter; + + case CAIRO_OPERATOR_DEST: case CAIRO_OPERATOR_SATURATE: - /* XXX This doesn't match image output for SATURATE; there's no equivalent */ - return kPrivateCGCompositePlusDarker; /* ??? */ + case CAIRO_OPERATOR_MULTIPLY: + case CAIRO_OPERATOR_SCREEN: + case CAIRO_OPERATOR_OVERLAY: + case CAIRO_OPERATOR_DARKEN: + case CAIRO_OPERATOR_LIGHTEN: + case CAIRO_OPERATOR_COLOR_DODGE: + case CAIRO_OPERATOR_COLOR_BURN: + case CAIRO_OPERATOR_HARD_LIGHT: + case CAIRO_OPERATOR_SOFT_LIGHT: + case CAIRO_OPERATOR_DIFFERENCE: + case CAIRO_OPERATOR_EXCLUSION: + case CAIRO_OPERATOR_HSL_HUE: + case CAIRO_OPERATOR_HSL_SATURATION: + case CAIRO_OPERATOR_HSL_COLOR: + case CAIRO_OPERATOR_HSL_LUMINOSITY: + default: + assert (0); } +} + +static cairo_int_status_t +_cairo_quartz_surface_set_cairo_operator (cairo_quartz_surface_t *surface, cairo_operator_t op) +{ + ND((stderr, "%p _cairo_quartz_surface_set_cairo_operator %d\n", surface, op)); + + if (surface->base.content == CAIRO_CONTENT_ALPHA) { + /* For some weird reason, some compositing operators are + swapped when operating on masks */ + switch (op) { + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_ADD: + CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz_composite (op)); + return CAIRO_STATUS_SUCCESS; + + case CAIRO_OPERATOR_IN: + CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeDestinationAtop); + return CAIRO_STATUS_SUCCESS; + + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_MULTIPLY: + case CAIRO_OPERATOR_SCREEN: + case CAIRO_OPERATOR_OVERLAY: + case CAIRO_OPERATOR_DARKEN: + case CAIRO_OPERATOR_LIGHTEN: + case CAIRO_OPERATOR_COLOR_DODGE: + case CAIRO_OPERATOR_COLOR_BURN: + case CAIRO_OPERATOR_HARD_LIGHT: + case CAIRO_OPERATOR_SOFT_LIGHT: + case CAIRO_OPERATOR_DIFFERENCE: + case CAIRO_OPERATOR_EXCLUSION: + case CAIRO_OPERATOR_HSL_HUE: + case CAIRO_OPERATOR_HSL_SATURATION: + case CAIRO_OPERATOR_HSL_COLOR: + case CAIRO_OPERATOR_HSL_LUMINOSITY: + CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeSourceOver); + return CAIRO_STATUS_SUCCESS; + + case CAIRO_OPERATOR_DEST_ATOP: + CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeSourceIn); + return CAIRO_STATUS_SUCCESS; + + case CAIRO_OPERATOR_SATURATE: + CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositePlusLighter); + return CAIRO_STATUS_SUCCESS; - return kPrivateCGCompositeCopy; + case CAIRO_OPERATOR_ATOP: + /* + CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeDestinationOver); + return CAIRO_STATUS_SUCCESS; + */ + case CAIRO_OPERATOR_DEST: + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_XOR: + default: + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } else { + switch (op) { + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_ATOP: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_DEST_ATOP: + case CAIRO_OPERATOR_XOR: + case CAIRO_OPERATOR_ADD: + CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz_composite (op)); + return CAIRO_STATUS_SUCCESS; + + case CAIRO_OPERATOR_DEST: + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + case CAIRO_OPERATOR_SATURATE: + /* TODO: the following are mostly supported by CGContextSetBlendMode*/ + case CAIRO_OPERATOR_MULTIPLY: + case CAIRO_OPERATOR_SCREEN: + case CAIRO_OPERATOR_OVERLAY: + case CAIRO_OPERATOR_DARKEN: + case CAIRO_OPERATOR_LIGHTEN: + case CAIRO_OPERATOR_COLOR_DODGE: + case CAIRO_OPERATOR_COLOR_BURN: + case CAIRO_OPERATOR_HARD_LIGHT: + case CAIRO_OPERATOR_SOFT_LIGHT: + case CAIRO_OPERATOR_DIFFERENCE: + case CAIRO_OPERATOR_EXCLUSION: + case CAIRO_OPERATOR_HSL_HUE: + case CAIRO_OPERATOR_HSL_SATURATION: + case CAIRO_OPERATOR_HSL_COLOR: + case CAIRO_OPERATOR_HSL_LUMINOSITY: + default: + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } } static inline CGLineCap @@ -525,37 +652,36 @@ _cairo_quartz_fixup_unbounded_operation (cairo_quartz_surface_t *surface, unbounded_op_data_t *op, cairo_antialias_t antialias) { - CGColorSpaceRef gray; CGRect clipBox, clipBoxRound; CGContextRef cgc; CGImageRef maskImage; + /* TODO: handle failure */ if (!CGContextClipToMaskPtr) return; clipBox = CGContextGetClipBoundingBox (surface->cgContext); clipBoxRound = CGRectIntegral (clipBox); - gray = CGColorSpaceCreateDeviceGray (); cgc = CGBitmapContextCreate (NULL, clipBoxRound.size.width, clipBoxRound.size.height, 8, - clipBoxRound.size.width, - gray, - kCGImageAlphaNone); - CGColorSpaceRelease (gray); + (((size_t) clipBoxRound.size.width) + 15) & (~15), + NULL, + kCGImageAlphaOnly); if (!cgc) return; + CGContextSetCompositeOperation (cgc, kPrivateCGCompositeCopy); /* We want to mask out whatever we just rendered, so we fill the - * surface with white, and then we'll render with black. + * surface opaque, and then we'll render transparent. */ - CGContextSetRGBFillColor (cgc, 1.0f, 1.0f, 1.0f, 1.0f); + CGContextSetAlpha (cgc, 1.0f); CGContextFillRect (cgc, CGRectMake (0, 0, clipBoxRound.size.width, clipBoxRound.size.height)); - CGContextSetRGBFillColor (cgc, 0.0f, 0.0f, 0.0f, 1.0f); + CGContextSetCompositeOperation (cgc, kPrivateCGCompositeClear); CGContextSetShouldAntialias (cgc, (antialias != CAIRO_ANTIALIAS_NONE)); CGContextTranslateCTM (cgc, -clipBoxRound.origin.x, -clipBoxRound.origin.y); @@ -590,27 +716,26 @@ _cairo_quartz_fixup_unbounded_operation (cairo_quartz_surface_t *surface, op->u.show_glyphs.nglyphs); if (op->u.show_glyphs.isClipping) { - CGContextFillRect (cgc, CGRectMake (0.0, 0.0, clipBoxRound.size.width, clipBoxRound.size.height)); + CGContextClearRect (cgc, clipBoxRound); CGContextRestoreGState (cgc); } } else if (op->op == UNBOUNDED_MASK) { + CGAffineTransform ctm = CGContextGetCTM (cgc); CGContextSaveGState (cgc); CGContextConcatCTM (cgc, op->u.mask.maskTransform); CGContextClipToMask (cgc, CGRectMake (0.0f, 0.0f, CGImageGetWidth(op->u.mask.mask), CGImageGetHeight(op->u.mask.mask)), op->u.mask.mask); - CGContextFillRect (cgc, CGRectMake (0.0, 0.0, clipBoxRound.size.width, clipBoxRound.size.height)); + CGContextSetCTM (cgc, ctm); + CGContextClearRect (cgc, clipBoxRound); CGContextRestoreGState (cgc); } /* Also mask out the portion of the clipbox that we rounded out, if any */ if (!CGRectEqualToRect (clipBox, clipBoxRound)) { CGContextBeginPath (cgc); - CGContextAddRect (cgc, CGRectMake (0.0, 0.0, clipBoxRound.size.width, clipBoxRound.size.height)); - CGContextAddRect (cgc, CGRectMake (clipBoxRound.origin.x - clipBox.origin.x, - clipBoxRound.origin.y - clipBox.origin.y, - clipBox.size.width, - clipBox.size.height)); + CGContextAddRect (cgc, clipBoxRound); + CGContextAddRect (cgc, clipBox); CGContextEOFillPath (cgc); } @@ -1019,7 +1144,8 @@ _cairo_surface_to_cgimage (cairo_surface_t *target, } if (stype != CAIRO_SURFACE_TYPE_IMAGE) { - status = _cairo_surface_acquire_source_image (source, &isurf, &image_extra); + status = _cairo_surface_acquire_source_image (source, + &isurf, &image_extra); if (status) return status; } else { @@ -1030,12 +1156,11 @@ _cairo_surface_to_cgimage (cairo_surface_t *target, *image_out = NULL; } else { cairo_image_surface_t *isurf_snap = NULL; - isurf_snap = (cairo_image_surface_t*) _cairo_surface_snapshot ((cairo_surface_t*) isurf); - if (isurf_snap == NULL) - return CAIRO_STATUS_NO_MEMORY; - if (isurf_snap->base.type != CAIRO_SURFACE_TYPE_IMAGE) - return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; + isurf_snap = (cairo_image_surface_t*) + _cairo_surface_snapshot (&isurf->base); + if (isurf_snap->base.status) + return isurf_snap->base.status; image = _cairo_quartz_create_cgimage (isurf_snap->format, isurf_snap->width, @@ -1048,9 +1173,11 @@ _cairo_surface_to_cgimage (cairo_surface_t *target, isurf_snap); *image_out = image; + if (image == NULL) + status = CAIRO_INT_STATUS_UNSUPPORTED; } - if ((cairo_surface_t*) isurf != source) + if (&isurf->base != source) _cairo_surface_release_source_image (source, isurf, image_extra); return status; @@ -1124,6 +1251,7 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t SurfacePatternDrawInfo *info; float rw, rh; cairo_status_t status; + cairo_bool_t is_bounded; cairo_matrix_t m; @@ -1134,14 +1262,12 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t spattern = (cairo_surface_pattern_t *) apattern; pat_surf = spattern->surface; - status = _cairo_surface_get_extents (pat_surf, &extents); - if (status) - return status; + is_bounded = _cairo_surface_get_extents (pat_surf, &extents); + assert (is_bounded); status = _cairo_surface_to_cgimage ((cairo_surface_t*) dest, pat_surf, &image); - if (status != CAIRO_STATUS_SUCCESS) - return CAIRO_INT_STATUS_UNSUPPORTED; - + if (status) + return status; if (image == NULL) return CAIRO_INT_STATUS_NOTHING_TO_DO; @@ -1223,9 +1349,7 @@ _cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface, double x0, y0, w, h; cairo_surface_t *fallback; - cairo_t *fallback_cr; CGImageRef img; - cairo_pattern_t *source_copy; cairo_status_t status; @@ -1243,24 +1367,42 @@ _cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface, fallback = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, (int) w, (int) h); cairo_surface_set_device_offset (fallback, -x0, -y0); - /* Paint the source onto our temporary */ - fallback_cr = cairo_create (fallback); - cairo_set_operator (fallback_cr, CAIRO_OPERATOR_SOURCE); +#if 0 + { + cairo_t *fallback_cr; + cairo_pattern_t *source_copy; - /* Use a copy of the pattern because it is const and could be allocated - * on the stack */ - status = _cairo_pattern_create_copy (&source_copy, source); - cairo_set_source (fallback_cr, source_copy); - cairo_pattern_destroy (source_copy); + /* Paint the source onto our temporary */ + fallback_cr = cairo_create (fallback); + cairo_set_operator (fallback_cr, CAIRO_OPERATOR_SOURCE); - cairo_paint (fallback_cr); - cairo_destroy (fallback_cr); + /* Use a copy of the pattern because it is const and could be allocated + * on the stack */ + status = _cairo_pattern_create_copy (&source_copy, source); + cairo_set_source (fallback_cr, source_copy); + cairo_pattern_destroy (source_copy); - status = _cairo_surface_to_cgimage ((cairo_surface_t*) surface, fallback, &img); - if (status == CAIRO_STATUS_SUCCESS && img == NULL) - return DO_NOTHING; + cairo_paint (fallback_cr); + cairo_destroy (fallback_cr); + } +#else + { + cairo_pattern_union_t pattern; + + _cairo_pattern_init_static_copy (&pattern.base, source); + _cairo_pattern_transform (&pattern.base, + &fallback->device_transform_inverse); + status = _cairo_surface_paint (fallback, + CAIRO_OPERATOR_SOURCE, + &pattern.base, NULL); + } +#endif + + status = _cairo_surface_to_cgimage (&surface->base, fallback, &img); if (status) return DO_UNSUPPORTED; + if (img == NULL) + return DO_NOTHING; surface->sourceImageRect = CGRectMake (0.0, 0.0, w, h); surface->sourceImage = img; @@ -1466,21 +1608,23 @@ _cairo_quartz_setup_source (cairo_quartz_surface_t *surface, CGAffineTransform xform; CGRect srcRect; cairo_fixed_t fw, fh; + cairo_bool_t is_bounded; status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img); - if (status == CAIRO_STATUS_SUCCESS && img == NULL) - return DO_NOTHING; if (status) return DO_UNSUPPORTED; + if (img == NULL) + return DO_NOTHING; + + CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1); surface->sourceImage = img; cairo_matrix_invert(&m); _cairo_quartz_cairo_matrix_to_quartz (&m, &surface->sourceTransform); - status = _cairo_surface_get_extents (pat_surf, &extents); - if (status) - return DO_UNSUPPORTED; + is_bounded = _cairo_surface_get_extents (pat_surf, &extents); + assert (is_bounded); if (source->extend == CAIRO_EXTEND_NONE) { surface->sourceImageRect = CGRectMake (0, 0, extents.width, extents.height); @@ -1590,6 +1734,30 @@ _cairo_quartz_teardown_source (cairo_quartz_surface_t *surface, } } + +static void +_cairo_quartz_draw_image (cairo_quartz_surface_t *surface, cairo_operator_t op, cairo_quartz_action_t action) +{ + assert (surface && surface->sourceImage && (action == DO_IMAGE || action == DO_TILED_IMAGE)); + + CGContextConcatCTM (surface->cgContext, surface->sourceTransform); + CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height); + CGContextScaleCTM (surface->cgContext, 1, -1); + + if (action == DO_IMAGE) { + CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage); + if (!_cairo_operator_bounded_by_source(op)) { + CGContextBeginPath (surface->cgContext); + CGContextAddRect (surface->cgContext, surface->sourceImageRect); + CGContextAddRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext)); + CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 0); + CGContextEOFillPath (surface->cgContext); + } + } else + CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage); +} + + /* * get source/dest image implementation */ @@ -1700,6 +1868,7 @@ _cairo_quartz_surface_finish (void *abstract_surface) /* Restore our saved gstate that we use to reset clipping */ CGContextRestoreGState (surface->cgContext); + _cairo_surface_clipper_reset (&surface->clipper); CGContextRelease (surface->cgContext); @@ -1830,7 +1999,6 @@ _cairo_quartz_surface_create_similar (void *abstract_surface, static cairo_status_t _cairo_quartz_surface_clone_similar (void *abstract_surface, cairo_surface_t *src, - cairo_content_t content, int src_x, int src_y, int width, @@ -1915,22 +2083,21 @@ FINISH: return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t +static cairo_bool_t _cairo_quartz_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; *extents = surface->extents; - - return CAIRO_STATUS_SUCCESS; + return TRUE; } static cairo_int_status_t _cairo_quartz_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; @@ -1941,10 +2108,13 @@ _cairo_quartz_surface_paint (void *abstract_surface, if (IS_EMPTY(surface)) return CAIRO_STATUS_SUCCESS; - if (op == CAIRO_OPERATOR_DEST) - return CAIRO_STATUS_SUCCESS; + rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (rv)) + return rv; - CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op)); + rv = _cairo_quartz_surface_set_cairo_operator (surface, op); + if (unlikely (rv)) + return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv; action = _cairo_quartz_setup_source (surface, source, NULL); @@ -1960,15 +2130,7 @@ _cairo_quartz_surface_paint (void *abstract_surface, CGContextRestoreGState (surface->cgContext); } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) { CGContextSaveGState (surface->cgContext); - - CGContextConcatCTM (surface->cgContext, surface->sourceTransform); - CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height); - CGContextScaleCTM (surface->cgContext, 1, -1); - - if (action == DO_IMAGE) - CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage); - else - CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage); + _cairo_quartz_draw_image (surface, op, action); CGContextRestoreGState (surface->cgContext); } else if (action != DO_NOTHING) { rv = CAIRO_INT_STATUS_UNSUPPORTED; @@ -2007,13 +2169,12 @@ _cairo_quartz_surface_fill (void *abstract_surface, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; cairo_quartz_action_t action; quartz_stroke_t stroke; - cairo_box_t box; CGPathRef path_for_unbounded = NULL; ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type)); @@ -2021,23 +2182,17 @@ _cairo_quartz_surface_fill (void *abstract_surface, if (IS_EMPTY(surface)) return CAIRO_STATUS_SUCCESS; - if (op == CAIRO_OPERATOR_DEST) - return CAIRO_STATUS_SUCCESS; + rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (rv)) + return rv; - /* Check whether the path would be a no-op */ - /* XXX handle unbounded ops */ - if (_cairo_path_fixed_is_empty(path) || - (_cairo_path_fixed_is_box(path, &box) && - box.p1.x == box.p2.x && - box.p1.y == box.p2.y)) - { - return CAIRO_STATUS_SUCCESS; - } + rv = _cairo_quartz_surface_set_cairo_operator (surface, op); + if (unlikely (rv)) + return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv; CGContextSaveGState (surface->cgContext); CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE)); - CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op)); if (_cairo_quartz_source_needs_extents (source)) { @@ -2084,14 +2239,7 @@ _cairo_quartz_surface_fill (void *abstract_surface, else CGContextEOClip (surface->cgContext); - CGContextConcatCTM (surface->cgContext, surface->sourceTransform); - CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height); - CGContextScaleCTM (surface->cgContext, 1, -1); - - if (action == DO_IMAGE) - CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage); - else - CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage); + _cairo_quartz_draw_image (surface, op, action); } else if (action != DO_NOTHING) { rv = CAIRO_INT_STATUS_UNSUPPORTED; } @@ -2125,7 +2273,7 @@ _cairo_quartz_surface_stroke (void *abstract_surface, cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; @@ -2139,10 +2287,13 @@ _cairo_quartz_surface_stroke (void *abstract_surface, if (IS_EMPTY(surface)) return CAIRO_STATUS_SUCCESS; - if (op == CAIRO_OPERATOR_DEST) - return CAIRO_STATUS_SUCCESS; + rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (rv)) + return rv; - CGContextSaveGState (surface->cgContext); + rv = _cairo_quartz_surface_set_cairo_operator (surface, op); + if (unlikely (rv)) + return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv; // Turning antialiasing off used to cause misrendering with // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels). @@ -2155,32 +2306,41 @@ _cairo_quartz_surface_stroke (void *abstract_surface, origCTM = CGContextGetCTM (surface->cgContext); - _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform); - CGContextConcatCTM (surface->cgContext, strokeTransform); - if (style->dash && style->num_dashes) { #define STATIC_DASH 32 CGFloat sdash[STATIC_DASH]; CGFloat *fdash = sdash; + double offset = style->dash_offset; unsigned int max_dashes = style->num_dashes; unsigned int k; - if (style->num_dashes%2) - max_dashes *= 2; - if (max_dashes > STATIC_DASH) - fdash = _cairo_malloc_ab (max_dashes, sizeof (CGFloat)); - if (fdash == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (_cairo_stroke_style_dash_can_approximate (style, ctm, tolerance)) { + double approximate_dashes[2]; + _cairo_stroke_style_dash_approximate (style, ctm, tolerance, + &offset, + approximate_dashes, + &max_dashes); + sdash[0] = approximate_dashes[0]; + sdash[1] = approximate_dashes[1]; + } else { + if (style->num_dashes%2) + max_dashes *= 2; + if (max_dashes > STATIC_DASH) + fdash = _cairo_malloc_ab (max_dashes, sizeof (CGFloat)); + if (fdash == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); - for (k = 0; k < max_dashes; k++) - fdash[k] = (CGFloat) style->dash[k % style->num_dashes]; - - CGContextSetLineDash (surface->cgContext, style->dash_offset, fdash, max_dashes); + for (k = 0; k < max_dashes; k++) + fdash[k] = (CGFloat) style->dash[k % style->num_dashes]; + } + CGContextSetLineDash (surface->cgContext, offset, fdash, max_dashes); if (fdash != sdash) free (fdash); - } + } else + CGContextSetLineDash (surface->cgContext, 0, NULL, 0); + + CGContextSaveGState (surface->cgContext); - CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op)); if (_cairo_quartz_source_needs_extents (source)) { @@ -2191,6 +2351,9 @@ _cairo_quartz_surface_stroke (void *abstract_surface, action = _cairo_quartz_setup_source (surface, source, NULL); } + _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform); + CGContextConcatCTM (surface->cgContext, strokeTransform); + CGContextBeginPath (surface->cgContext); stroke.cgContext = surface->cgContext; @@ -2209,15 +2372,7 @@ _cairo_quartz_surface_stroke (void *abstract_surface, CGContextClip (surface->cgContext); CGContextSetCTM (surface->cgContext, origCTM); - - CGContextConcatCTM (surface->cgContext, surface->sourceTransform); - CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height); - CGContextScaleCTM (surface->cgContext, 1, -1); - - if (action == DO_IMAGE) - CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage); - else - CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage); + _cairo_quartz_draw_image (surface, op, action); } else if (action == DO_SHADING) { CGContextReplacePathWithStrokedPath (surface->cgContext); CGContextClip (surface->cgContext); @@ -2236,32 +2391,21 @@ _cairo_quartz_surface_stroke (void *abstract_surface, CGContextRestoreGState (surface->cgContext); if (path_for_unbounded) { - unbounded_op_data_t ub; + CGContextSaveGState (surface->cgContext); + CGContextConcatCTM (surface->cgContext, strokeTransform); CGContextBeginPath (surface->cgContext); - - /* recreate the stroke state, but without the CTM, as it's been already baked - * into the path. - */ - CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE)); - CGContextSetLineWidth (surface->cgContext, style->line_width); - CGContextSetLineCap (surface->cgContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap)); - CGContextSetLineJoin (surface->cgContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join)); - CGContextSetMiterLimit (surface->cgContext, style->miter_limit); - CGContextAddPath (surface->cgContext, path_for_unbounded); CGPathRelease (path_for_unbounded); CGContextReplacePathWithStrokedPath (surface->cgContext); - path_for_unbounded = CGContextCopyPathPtr (surface->cgContext); - ub.op = UNBOUNDED_STROKE_FILL; - ub.u.stroke_fill.cgPath = path_for_unbounded; - ub.u.stroke_fill.fill_rule = CAIRO_FILL_RULE_WINDING; + CGContextAddRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext)); - _cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias); + CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.); + CGContextEOFillPath (surface->cgContext); - CGPathRelease (path_for_unbounded); + CGContextRestoreGState (surface->cgContext); } ND((stderr, "-- stroke\n")); @@ -2276,8 +2420,8 @@ _cairo_quartz_surface_show_glyphs (void *abstract_surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip, + int *remaining_glyphs) { CGAffineTransform textTransform, ctm; #define STATIC_BUF_SIZE 64 @@ -2302,19 +2446,24 @@ _cairo_quartz_surface_show_glyphs (void *abstract_surface, if (num_glyphs <= 0) return CAIRO_STATUS_SUCCESS; - if (op == CAIRO_OPERATOR_DEST) - return CAIRO_STATUS_SUCCESS; - if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ) return CAIRO_INT_STATUS_UNSUPPORTED; + rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (rv)) + return rv; + + rv = _cairo_quartz_surface_set_cairo_operator (surface, op); + if (unlikely (rv)) + return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv; + CGContextSaveGState (surface->cgContext); if (_cairo_quartz_source_needs_extents (source)) { cairo_rectangle_int_t glyph_extents; _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs, - &glyph_extents); + &glyph_extents, NULL); action = _cairo_quartz_setup_source (surface, source, &glyph_extents); } else { action = _cairo_quartz_setup_source (surface, source, NULL); @@ -2331,8 +2480,6 @@ _cairo_quartz_surface_show_glyphs (void *abstract_surface, goto BAIL; } - CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op)); - /* this doesn't addref */ cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font); CGContextSetFont (surface->cgContext, cgfref); @@ -2436,14 +2583,7 @@ _cairo_quartz_surface_show_glyphs (void *abstract_surface, CGContextSetCTM (surface->cgContext, ctm); if (action == DO_IMAGE || action == DO_TILED_IMAGE) { - CGContextConcatCTM (surface->cgContext, surface->sourceTransform); - CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height); - CGContextScaleCTM (surface->cgContext, 1, -1); - - if (action == DO_IMAGE) - CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage); - else - CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage); + _cairo_quartz_draw_image (surface, op, action); } else if (action == DO_SHADING) { CGContextConcatCTM (surface->cgContext, surface->sourceTransform); CGContextDrawShading (surface->cgContext, surface->sourceShading); @@ -2493,30 +2633,24 @@ _cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_surface_pattern_t *mask, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { - cairo_rectangle_int_t mask_extents; CGRect rect; CGImageRef img; cairo_surface_t *pat_surf = mask->surface; cairo_status_t status = CAIRO_STATUS_SUCCESS; CGAffineTransform ctm, mask_matrix; - status = _cairo_surface_get_extents (pat_surf, &mask_extents); - if (status) - return status; - - // everything would be masked out, so do nothing - if (mask_extents.width == 0 || mask_extents.height == 0) - return CAIRO_STATUS_SUCCESS; - status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img); - if (status == CAIRO_STATUS_SUCCESS && img == NULL) - return CAIRO_STATUS_SUCCESS; if (status) return status; + if (img == NULL) { + if (!_cairo_operator_bounded_by_mask (op)) + CGContextClearRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext)); + return CAIRO_STATUS_SUCCESS; + } - rect = CGRectMake (0.0f, 0.0f, mask_extents.width, mask_extents.height); + rect = CGRectMake (0.0f, 0.0f, CGImageGetWidth (img) , CGImageGetHeight (img)); CGContextSaveGState (surface->cgContext); @@ -2525,15 +2659,16 @@ _cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface, ctm = CGContextGetCTM (surface->cgContext); _cairo_quartz_cairo_matrix_to_quartz (&mask->base.matrix, &mask_matrix); - CGContextConcatCTM (surface->cgContext, CGAffineTransformInvert(mask_matrix)); - CGContextTranslateCTM (surface->cgContext, 0.0f, rect.size.height); - CGContextScaleCTM (surface->cgContext, 1.0f, -1.0f); + mask_matrix = CGAffineTransformInvert(mask_matrix); + mask_matrix = CGAffineTransformTranslate (mask_matrix, 0.0, CGImageGetHeight (img)); + mask_matrix = CGAffineTransformScale (mask_matrix, 1.0, -1.0); + CGContextConcatCTM (surface->cgContext, mask_matrix); CGContextClipToMaskPtr (surface->cgContext, rect, img); CGContextSetCTM (surface->cgContext, ctm); - status = _cairo_quartz_surface_paint (surface, op, source, extents); + status = _cairo_quartz_surface_paint (surface, op, source, clip); CGContextRestoreGState (surface->cgContext); @@ -2541,7 +2676,7 @@ _cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface, unbounded_op_data_t ub; ub.op = UNBOUNDED_MASK; ub.u.mask.mask = img; - ub.u.mask.maskTransform = CGAffineTransformInvert(mask_matrix); + ub.u.mask.maskTransform = mask_matrix; _cairo_quartz_fixup_unbounded_operation (surface, &ub, CAIRO_ANTIALIAS_NONE); } @@ -2559,41 +2694,27 @@ _cairo_quartz_surface_mask_with_generic (cairo_quartz_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { - int width = surface->extents.width - surface->extents.x; - int height = surface->extents.height - surface->extents.y; + int width = surface->extents.width; + int height = surface->extents.height; cairo_surface_t *gradient_surf = NULL; - cairo_t *gradient_surf_cr = NULL; - cairo_surface_pattern_t surface_pattern; - cairo_pattern_t *mask_copy; cairo_int_status_t status; /* Render the gradient to a surface */ - gradient_surf = cairo_quartz_surface_create (CAIRO_FORMAT_ARGB32, + gradient_surf = cairo_quartz_surface_create (CAIRO_FORMAT_A8, width, height); - gradient_surf_cr = cairo_create(gradient_surf); - - /* make a copy of the pattern because because cairo_set_source doesn't take - * a 'const cairo_pattern_t *' */ - _cairo_pattern_create_copy (&mask_copy, mask); - cairo_set_source (gradient_surf_cr, mask_copy); - cairo_pattern_destroy (mask_copy); - - cairo_set_operator (gradient_surf_cr, CAIRO_OPERATOR_SOURCE); - cairo_paint (gradient_surf_cr); - status = cairo_status (gradient_surf_cr); - cairo_destroy (gradient_surf_cr); + status = _cairo_quartz_surface_paint (gradient_surf, CAIRO_OPERATOR_SOURCE, mask, NULL); if (status) goto BAIL; _cairo_pattern_init_for_surface (&surface_pattern, gradient_surf); - status = _cairo_quartz_surface_mask_with_surface (surface, op, source, &surface_pattern, extents); + status = _cairo_quartz_surface_mask_with_surface (surface, op, source, &surface_pattern, clip); _cairo_pattern_fini (&surface_pattern.base); @@ -2609,7 +2730,7 @@ _cairo_quartz_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; @@ -2619,12 +2740,16 @@ _cairo_quartz_surface_mask (void *abstract_surface, if (IS_EMPTY(surface)) return CAIRO_STATUS_SUCCESS; + rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (rv)) + return rv; + if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { /* This is easy; we just need to paint with the alpha. */ cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask; CGContextSetAlpha (surface->cgContext, solid_mask->color.alpha); - rv = _cairo_quartz_surface_paint (surface, op, source, extents); + rv = _cairo_quartz_surface_paint (surface, op, source, clip); CGContextSetAlpha (surface->cgContext, 1.0); return rv; @@ -2634,9 +2759,9 @@ _cairo_quartz_surface_mask (void *abstract_surface, if (CGContextClipToMaskPtr) { /* For these, we can skip creating a temporary surface, since we already have one */ if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && mask->extend == CAIRO_EXTEND_NONE) - return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask, extents); + return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask, clip); - return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask, extents); + return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask, clip); } /* So, CGContextClipToMask is not present in 10.3.9, so we're @@ -2649,14 +2774,15 @@ _cairo_quartz_surface_mask (void *abstract_surface, return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_quartz_surface_intersect_clip_path (void *abstract_surface, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) +static cairo_status_t +_cairo_quartz_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) { - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; + cairo_quartz_surface_t *surface = + cairo_container_of (clipper, cairo_quartz_surface_t, clipper); quartz_stroke_t stroke; cairo_status_t status; @@ -2717,8 +2843,6 @@ static const struct _cairo_surface_backend cairo_quartz_surface_backend = { NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ - NULL, /* set_clip_region */ - _cairo_quartz_surface_intersect_clip_path, _cairo_quartz_surface_get_extents, NULL, /* old_show_glyphs */ NULL, /* get_font_options */ @@ -2739,7 +2863,6 @@ static const struct _cairo_surface_backend cairo_quartz_surface_backend = { _cairo_quartz_surface_snapshot, NULL, /* is_similar */ - NULL, /* reset */ NULL /* fill_stroke */ }; @@ -2763,6 +2886,9 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext, _cairo_surface_init(&surface->base, &cairo_quartz_surface_backend, content); + _cairo_surface_clipper_init (&surface->clipper, + _cairo_quartz_surface_clipper_intersect_clip_path); + /* Save our extents */ surface->extents.x = surface->extents.y = 0; surface->extents.width = width; @@ -2883,20 +3009,11 @@ cairo_quartz_surface_create (cairo_format_t format, else bitinfo |= kCGImageAlphaNoneSkipFirst; bitsPerComponent = 8; - - /* The Apple docs say that for best performance, the stride and the data - * pointer should be 16-byte aligned. malloc already aligns to 16-bytes, - * so we don't have to anything special on allocation. - */ stride = width * 4; - stride += (16 - (stride & 15)) & 15; } else if (format == CAIRO_FORMAT_A8) { - cgColorspace = CGColorSpaceCreateDeviceGray(); - if (width % 4 == 0) - stride = width; - else - stride = (width & ~3) + 4; - bitinfo = kCGImageAlphaNone; + cgColorspace = NULL; + stride = width; + bitinfo = kCGImageAlphaOnly; bitsPerComponent = 8; } else if (format == CAIRO_FORMAT_A1) { /* I don't think we can usefully support this, as defined by @@ -2908,6 +3025,12 @@ cairo_quartz_surface_create (cairo_format_t format, return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); } + /* The Apple docs say that for best performance, the stride and the data + * pointer should be 16-byte aligned. malloc already aligns to 16-bytes, + * so we don't have to anything special on allocation. + */ + stride = (stride + 15) & ~15; + imageData = _cairo_malloc_ab (height, stride); if (!imageData) { CGColorSpaceRelease (cgColorspace); diff --git a/gfx/cairo/cairo/src/cairo-recording-surface-private.h b/gfx/cairo/cairo/src/cairo-recording-surface-private.h new file mode 100644 index 000000000000..f0824f50b488 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-recording-surface-private.h @@ -0,0 +1,171 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Kristian Høgsberg + * Adrian Johnson + */ + +#ifndef CAIRO_RECORDING_SURFACE_H +#define CAIRO_RECORDING_SURFACE_H + +#include "cairoint.h" +#include "cairo-path-fixed-private.h" +#include "cairo-clip-private.h" + +typedef enum { + /* The 5 basic drawing operations. */ + CAIRO_COMMAND_PAINT, + CAIRO_COMMAND_MASK, + CAIRO_COMMAND_STROKE, + CAIRO_COMMAND_FILL, + CAIRO_COMMAND_SHOW_TEXT_GLYPHS, +} cairo_command_type_t; + +typedef enum { + CAIRO_RECORDING_REGION_ALL, + CAIRO_RECORDING_REGION_NATIVE, + CAIRO_RECORDING_REGION_IMAGE_FALLBACK +} cairo_recording_region_type_t; + +typedef struct _cairo_command_header { + cairo_command_type_t type; + cairo_recording_region_type_t region; + cairo_operator_t op; + cairo_clip_t clip; +} cairo_command_header_t; + +typedef struct _cairo_command_paint { + cairo_command_header_t header; + cairo_pattern_union_t source; +} cairo_command_paint_t; + +typedef struct _cairo_command_mask { + cairo_command_header_t header; + cairo_pattern_union_t source; + cairo_pattern_union_t mask; +} cairo_command_mask_t; + +typedef struct _cairo_command_stroke { + cairo_command_header_t header; + cairo_pattern_union_t source; + cairo_path_fixed_t path; + cairo_stroke_style_t style; + cairo_matrix_t ctm; + cairo_matrix_t ctm_inverse; + double tolerance; + cairo_antialias_t antialias; +} cairo_command_stroke_t; + +typedef struct _cairo_command_fill { + cairo_command_header_t header; + cairo_pattern_union_t source; + cairo_path_fixed_t path; + cairo_fill_rule_t fill_rule; + double tolerance; + cairo_antialias_t antialias; +} cairo_command_fill_t; + +typedef struct _cairo_command_show_text_glyphs { + cairo_command_header_t header; + cairo_pattern_union_t source; + char *utf8; + int utf8_len; + cairo_glyph_t *glyphs; + unsigned int num_glyphs; + cairo_text_cluster_t *clusters; + int num_clusters; + cairo_text_cluster_flags_t cluster_flags; + cairo_scaled_font_t *scaled_font; +} cairo_command_show_text_glyphs_t; + +typedef union _cairo_command { + cairo_command_header_t header; + + cairo_command_paint_t paint; + cairo_command_mask_t mask; + cairo_command_stroke_t stroke; + cairo_command_fill_t fill; + cairo_command_show_text_glyphs_t show_text_glyphs; +} cairo_command_t; + +typedef struct _cairo_recording_surface { + cairo_surface_t base; + + cairo_content_t content; + + /* A recording-surface is logically unbounded, but when used as a + * source we need to render it to an image, so we need a size at + * which to create that image. */ + cairo_rectangle_t extents_pixels; + cairo_rectangle_int_t extents; + cairo_bool_t unbounded; + + cairo_clip_t clip; + + cairo_array_t commands; + cairo_surface_t *commands_owner; + + int replay_start_idx; +} cairo_recording_surface_t; + +slim_hidden_proto (cairo_recording_surface_create); + +cairo_private cairo_int_status_t +_cairo_recording_surface_get_path (cairo_surface_t *surface, + cairo_path_fixed_t *path); + +cairo_private cairo_status_t +_cairo_recording_surface_replay (cairo_surface_t *surface, + cairo_surface_t *target); + + +cairo_private cairo_status_t +_cairo_recording_surface_replay_analyze_recording_pattern (cairo_surface_t *surface, + cairo_surface_t *target); + +cairo_private cairo_status_t +_cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, + cairo_surface_t *target); +cairo_private cairo_status_t +_cairo_recording_surface_replay_region (cairo_surface_t *surface, + cairo_surface_t *target, + cairo_recording_region_type_t region); + +cairo_private cairo_status_t +_cairo_recording_surface_get_bbox (cairo_recording_surface_t *recording, + cairo_box_t *bbox, + const cairo_matrix_t *transform); + +cairo_private cairo_bool_t +_cairo_surface_is_recording (const cairo_surface_t *surface); + +#endif /* CAIRO_RECORDING_SURFACE_H */ diff --git a/gfx/cairo/cairo/src/cairo-recording-surface.c b/gfx/cairo/cairo/src/cairo-recording-surface.c new file mode 100644 index 000000000000..c50b330eec96 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-recording-surface.c @@ -0,0 +1,1096 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * Copyright © 2007 Adrian Johnson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Kristian Høgsberg + * Carl Worth + * Adrian Johnson + */ + +/* A recording surface is a surface that records all drawing operations at + * the highest level of the surface backend interface, (that is, the + * level of paint, mask, stroke, fill, and show_text_glyphs). The recording + * surface can then be "replayed" against any target surface by using it + * as a source surface. + * + * If you want to replay a surface so that the results in target will be + * identical to the results that would have been obtained if the original + * operations applied to the recording surface had instead been applied to the + * target surface, you can use code like this: + * + * cairo_t *cr; + * + * cr = cairo_create (target); + * cairo_set_source_surface (cr, recording_surface, 0.0, 0.0); + * cairo_paint (cr); + * cairo_destroy (cr); + * + * + * A recording surface is logically unbounded, i.e. it has no implicit constraint + * on the size of the drawing surface. However, in practice this is rarely + * useful as you wish to replay against a particular target surface with + * known bounds. For this case, it is more efficient to specify the target + * extents to the recording surface upon creation. + * + * The recording phase of the recording surface is careful to snapshot all + * necessary objects (paths, patterns, etc.), in order to achieve + * accurate replay. The efficiency of the recording surface could be + * improved by improving the implementation of snapshot for the + * various objects. For example, it would be nice to have a + * copy-on-write implementation for _cairo_surface_snapshot. + */ + +/* XXX Rename to recording surface */ + +#include "cairoint.h" +#include "cairo-analysis-surface-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-clip-private.h" +#include "cairo-surface-wrapper-private.h" + +typedef enum { + CAIRO_RECORDING_REPLAY, + CAIRO_RECORDING_CREATE_REGIONS +} cairo_recording_replay_type_t; + +static const cairo_surface_backend_t cairo_recording_surface_backend; + +/* Currently all recording surfaces do have a size which should be passed + * in as the maximum size of any target surface against which the + * recording-surface will ever be replayed. + * + * XXX: The naming of "pixels" in the size here is a misnomer. It's + * actually a size in whatever device-space units are desired (again, + * according to the intended replay target). + */ + +/** + * cairo_recording_surface_create: + * @content: the content of the recording surface + * @extents_pixels: the extents to record in pixels, can be %NULL to record + * unbounded operations. + * + * Creates a recording-surface which can be used to record all drawing operations + * at the highest level (that is, the level of paint, mask, stroke, fill + * and show_text_glyphs). The recording surface can then be "replayed" against + * any target surface by using it as a source to drawing operations. + * + * The recording phase of the recording surface is careful to snapshot all + * necessary objects (paths, patterns, etc.), in order to achieve + * accurate replay. + * + * Since 1.10 + **/ +cairo_surface_t * +cairo_recording_surface_create (cairo_content_t content, + const cairo_rectangle_t *extents) +{ + cairo_recording_surface_t *recording_surface; + cairo_status_t status; + + recording_surface = malloc (sizeof (cairo_recording_surface_t)); + if (unlikely (recording_surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&recording_surface->base, &cairo_recording_surface_backend, content); + + recording_surface->content = content; + + /* unbounded -> 'infinite' extents */ + if (extents != NULL) { + recording_surface->extents_pixels = *extents; + + /* XXX check for overflow */ + recording_surface->extents.x = floor (extents->x); + recording_surface->extents.y = floor (extents->y); + recording_surface->extents.width = ceil (extents->x + extents->width) - recording_surface->extents.x; + recording_surface->extents.height = ceil (extents->y + extents->height) - recording_surface->extents.y; + + status = _cairo_clip_init_rectangle (&recording_surface->clip, &recording_surface->extents); + if (unlikely (status)) { + free (recording_surface); + return _cairo_surface_create_in_error (status); + } + + recording_surface->unbounded = FALSE; + } else { + recording_surface->unbounded = TRUE; + _cairo_clip_init (&recording_surface->clip); + } + + _cairo_array_init (&recording_surface->commands, sizeof (cairo_command_t *)); + recording_surface->commands_owner = NULL; + + recording_surface->replay_start_idx = 0; + + return &recording_surface->base; +} +slim_hidden_def (cairo_recording_surface_create); + +static cairo_surface_t * +_cairo_recording_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_rectangle_t extents; + extents.x = extents.y = 0; + extents.width = width; + extents.height = height; + return cairo_recording_surface_create (content, &extents); +} + +static cairo_status_t +_cairo_recording_surface_finish (void *abstract_surface) +{ + cairo_recording_surface_t *recording_surface = abstract_surface; + cairo_command_t **elements; + int i, num_elements; + + if (recording_surface->commands_owner) { + cairo_surface_destroy (recording_surface->commands_owner); + return CAIRO_STATUS_SUCCESS; + } + + num_elements = recording_surface->commands.num_elements; + elements = _cairo_array_index (&recording_surface->commands, 0); + for (i = 0; i < num_elements; i++) { + cairo_command_t *command = elements[i]; + + _cairo_clip_reset (&command->header.clip); + + switch (command->header.type) { + case CAIRO_COMMAND_PAINT: + _cairo_pattern_fini_snapshot (&command->paint.source.base); + free (command); + break; + + case CAIRO_COMMAND_MASK: + _cairo_pattern_fini_snapshot (&command->mask.source.base); + _cairo_pattern_fini_snapshot (&command->mask.mask.base); + free (command); + break; + + case CAIRO_COMMAND_STROKE: + _cairo_pattern_fini_snapshot (&command->stroke.source.base); + _cairo_path_fixed_fini (&command->stroke.path); + _cairo_stroke_style_fini (&command->stroke.style); + free (command); + break; + + case CAIRO_COMMAND_FILL: + _cairo_pattern_fini_snapshot (&command->fill.source.base); + _cairo_path_fixed_fini (&command->fill.path); + free (command); + break; + + case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: + _cairo_pattern_fini_snapshot (&command->show_text_glyphs.source.base); + free (command->show_text_glyphs.utf8); + free (command->show_text_glyphs.glyphs); + free (command->show_text_glyphs.clusters); + cairo_scaled_font_destroy (command->show_text_glyphs.scaled_font); + free (command); + break; + + default: + ASSERT_NOT_REACHED; + } + } + + _cairo_array_fini (&recording_surface->commands); + _cairo_clip_reset (&recording_surface->clip); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_recording_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_status_t status; + cairo_recording_surface_t *surface = abstract_surface; + cairo_surface_t *image; + + image = _cairo_surface_has_snapshot (&surface->base, + &_cairo_image_surface_backend, + surface->content); + if (image != NULL) { + *image_out = (cairo_image_surface_t *) cairo_surface_reference (image); + *image_extra = NULL; + return CAIRO_STATUS_SUCCESS; + } + + image = _cairo_image_surface_create_with_content (surface->content, + surface->extents.width, + surface->extents.height); + if (unlikely (image->status)) + return image->status; + + cairo_surface_set_device_offset (image, + -surface->extents.x, + -surface->extents.y); + + status = _cairo_recording_surface_replay (&surface->base, image); + if (unlikely (status)) { + cairo_surface_destroy (image); + return status; + } + + status = _cairo_surface_attach_snapshot (&surface->base, image, NULL); + if (unlikely (status)) { + cairo_surface_destroy (image); + return status; + } + + *image_out = (cairo_image_surface_t *) image; + *image_extra = NULL; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_recording_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_command_init (cairo_recording_surface_t *recording_surface, + cairo_command_header_t *command, + cairo_command_type_t type, + cairo_operator_t op, + cairo_clip_t *clip) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + command->type = type; + command->op = op; + command->region = CAIRO_RECORDING_REGION_ALL; + _cairo_clip_init_copy (&command->clip, clip); + if (recording_surface->clip.path != NULL) + status = _cairo_clip_apply_clip (&command->clip, &recording_surface->clip); + + return status; +} + +static cairo_int_status_t +_cairo_recording_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_recording_surface_t *recording_surface = abstract_surface; + cairo_command_paint_t *command; + + command = malloc (sizeof (cairo_command_paint_t)); + if (unlikely (command == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _command_init (recording_surface, + &command->header, CAIRO_COMMAND_PAINT, op, clip); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_pattern_init_snapshot (&command->source.base, source); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_array_append (&recording_surface->commands, &command); + if (unlikely (status)) + goto CLEANUP_SOURCE; + + /* An optimisation that takes care to not replay what was done + * before surface is cleared. We don't erase recorded commands + * since we may have earlier snapshots of this surface. */ + if (op == CAIRO_OPERATOR_CLEAR && clip == NULL) + recording_surface->replay_start_idx = recording_surface->commands.num_elements; + + return CAIRO_STATUS_SUCCESS; + + CLEANUP_SOURCE: + _cairo_pattern_fini_snapshot (&command->source.base); + CLEANUP_COMMAND: + free (command); + return status; +} + +static cairo_int_status_t +_cairo_recording_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_recording_surface_t *recording_surface = abstract_surface; + cairo_command_mask_t *command; + + command = malloc (sizeof (cairo_command_mask_t)); + if (unlikely (command == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _command_init (recording_surface, + &command->header, CAIRO_COMMAND_MASK, op, clip); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_pattern_init_snapshot (&command->source.base, source); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_pattern_init_snapshot (&command->mask.base, mask); + if (unlikely (status)) + goto CLEANUP_SOURCE; + + status = _cairo_array_append (&recording_surface->commands, &command); + if (unlikely (status)) + goto CLEANUP_MASK; + + return CAIRO_STATUS_SUCCESS; + + CLEANUP_MASK: + _cairo_pattern_fini_snapshot (&command->mask.base); + CLEANUP_SOURCE: + _cairo_pattern_fini_snapshot (&command->source.base); + CLEANUP_COMMAND: + free (command); + return status; +} + +static cairo_int_status_t +_cairo_recording_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_stroke_style_t *style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_recording_surface_t *recording_surface = abstract_surface; + cairo_command_stroke_t *command; + + command = malloc (sizeof (cairo_command_stroke_t)); + if (unlikely (command == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _command_init (recording_surface, + &command->header, CAIRO_COMMAND_STROKE, op, clip); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_pattern_init_snapshot (&command->source.base, source); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_path_fixed_init_copy (&command->path, path); + if (unlikely (status)) + goto CLEANUP_SOURCE; + + status = _cairo_stroke_style_init_copy (&command->style, style); + if (unlikely (status)) + goto CLEANUP_PATH; + + command->ctm = *ctm; + command->ctm_inverse = *ctm_inverse; + command->tolerance = tolerance; + command->antialias = antialias; + + status = _cairo_array_append (&recording_surface->commands, &command); + if (unlikely (status)) + goto CLEANUP_STYLE; + + return CAIRO_STATUS_SUCCESS; + + CLEANUP_STYLE: + _cairo_stroke_style_fini (&command->style); + CLEANUP_PATH: + _cairo_path_fixed_fini (&command->path); + CLEANUP_SOURCE: + _cairo_pattern_fini_snapshot (&command->source.base); + CLEANUP_COMMAND: + free (command); + return status; +} + +static cairo_int_status_t +_cairo_recording_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_recording_surface_t *recording_surface = abstract_surface; + cairo_command_fill_t *command; + + command = malloc (sizeof (cairo_command_fill_t)); + if (unlikely (command == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status =_command_init (recording_surface, + &command->header, CAIRO_COMMAND_FILL, op, clip); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_pattern_init_snapshot (&command->source.base, source); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_path_fixed_init_copy (&command->path, path); + if (unlikely (status)) + goto CLEANUP_SOURCE; + + command->fill_rule = fill_rule; + command->tolerance = tolerance; + command->antialias = antialias; + + status = _cairo_array_append (&recording_surface->commands, &command); + if (unlikely (status)) + goto CLEANUP_PATH; + + return CAIRO_STATUS_SUCCESS; + + CLEANUP_PATH: + _cairo_path_fixed_fini (&command->path); + CLEANUP_SOURCE: + _cairo_pattern_fini_snapshot (&command->source.base); + CLEANUP_COMMAND: + free (command); + return status; +} + +static cairo_bool_t +_cairo_recording_surface_has_show_text_glyphs (void *abstract_surface) +{ + return TRUE; +} + +static cairo_int_status_t +_cairo_recording_surface_show_text_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_recording_surface_t *recording_surface = abstract_surface; + cairo_command_show_text_glyphs_t *command; + + command = malloc (sizeof (cairo_command_show_text_glyphs_t)); + if (unlikely (command == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _command_init (recording_surface, + &command->header, CAIRO_COMMAND_SHOW_TEXT_GLYPHS, + op, clip); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_pattern_init_snapshot (&command->source.base, source); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + command->utf8 = NULL; + command->utf8_len = utf8_len; + command->glyphs = NULL; + command->num_glyphs = num_glyphs; + command->clusters = NULL; + command->num_clusters = num_clusters; + + if (utf8_len) { + command->utf8 = malloc (utf8_len); + if (unlikely (command->utf8 == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_ARRAYS; + } + memcpy (command->utf8, utf8, utf8_len); + } + if (num_glyphs) { + command->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (glyphs[0])); + if (unlikely (command->glyphs == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_ARRAYS; + } + memcpy (command->glyphs, glyphs, sizeof (glyphs[0]) * num_glyphs); + } + if (num_clusters) { + command->clusters = _cairo_malloc_ab (num_clusters, sizeof (clusters[0])); + if (unlikely (command->clusters == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_ARRAYS; + } + memcpy (command->clusters, clusters, sizeof (clusters[0]) * num_clusters); + } + + command->cluster_flags = cluster_flags; + + command->scaled_font = cairo_scaled_font_reference (scaled_font); + + status = _cairo_array_append (&recording_surface->commands, &command); + if (unlikely (status)) + goto CLEANUP_SCALED_FONT; + + return CAIRO_STATUS_SUCCESS; + + CLEANUP_SCALED_FONT: + cairo_scaled_font_destroy (command->scaled_font); + CLEANUP_ARRAYS: + free (command->utf8); + free (command->glyphs); + free (command->clusters); + + _cairo_pattern_fini_snapshot (&command->source.base); + CLEANUP_COMMAND: + free (command); + return status; +} + +/** + * _cairo_recording_surface_snapshot + * @surface: a #cairo_surface_t which must be a recording surface + * + * Make an immutable copy of @surface. It is an error to call a + * surface-modifying function on the result of this function. + * + * The caller owns the return value and should call + * cairo_surface_destroy() when finished with it. This function will not + * return %NULL, but will return a nil surface instead. + * + * Return value: The snapshot surface. + **/ +static cairo_surface_t * +_cairo_recording_surface_snapshot (void *abstract_other) +{ + cairo_recording_surface_t *other = abstract_other; + cairo_recording_surface_t *recording_surface; + + recording_surface = malloc (sizeof (cairo_recording_surface_t)); + if (unlikely (recording_surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&recording_surface->base, &cairo_recording_surface_backend, + other->base.content); + + recording_surface->extents_pixels = other->extents_pixels; + recording_surface->extents = other->extents; + recording_surface->unbounded = other->unbounded; + recording_surface->replay_start_idx = other->replay_start_idx; + recording_surface->content = other->content; + + _cairo_array_init_snapshot (&recording_surface->commands, &other->commands); + recording_surface->commands_owner = cairo_surface_reference (&other->base); + + _cairo_clip_init_copy (&recording_surface->clip, &other->clip); + + return &recording_surface->base; +} + +static cairo_bool_t +_cairo_recording_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_recording_surface_t *surface = abstract_surface; + + if (surface->unbounded) + return FALSE; + + *rectangle = surface->extents; + return TRUE; +} + +/** + * _cairo_surface_is_recording: + * @surface: a #cairo_surface_t + * + * Checks if a surface is a #cairo_recording_surface_t + * + * Return value: %TRUE if the surface is a recording surface + **/ +cairo_bool_t +_cairo_surface_is_recording (const cairo_surface_t *surface) +{ + return surface->backend == &cairo_recording_surface_backend; +} + +static const cairo_surface_backend_t cairo_recording_surface_backend = { + CAIRO_SURFACE_TYPE_RECORDING, + _cairo_recording_surface_create_similar, + _cairo_recording_surface_finish, + _cairo_recording_surface_acquire_source_image, + _cairo_recording_surface_release_source_image, + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_recording_surface_get_extents, + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + + /* Here are the 5 basic drawing operations, (which are in some + * sense the only things that cairo_recording_surface should need to + * implement). However, we implement the more generic show_text_glyphs + * instead of show_glyphs. One or the other is eough. */ + + _cairo_recording_surface_paint, + _cairo_recording_surface_mask, + _cairo_recording_surface_stroke, + _cairo_recording_surface_fill, + NULL, + + _cairo_recording_surface_snapshot, + + NULL, /* is_similar */ + NULL, /* fill_stroke */ + NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ + + _cairo_recording_surface_has_show_text_glyphs, + _cairo_recording_surface_show_text_glyphs +}; + +cairo_int_status_t +_cairo_recording_surface_get_path (cairo_surface_t *surface, + cairo_path_fixed_t *path) +{ + cairo_recording_surface_t *recording_surface; + cairo_command_t **elements; + int i, num_elements; + cairo_int_status_t status; + + if (surface->status) + return surface->status; + + recording_surface = (cairo_recording_surface_t *) surface; + status = CAIRO_STATUS_SUCCESS; + + num_elements = recording_surface->commands.num_elements; + elements = _cairo_array_index (&recording_surface->commands, 0); + for (i = recording_surface->replay_start_idx; i < num_elements; i++) { + cairo_command_t *command = elements[i]; + + switch (command->header.type) { + case CAIRO_COMMAND_PAINT: + case CAIRO_COMMAND_MASK: + status = CAIRO_INT_STATUS_UNSUPPORTED; + break; + + case CAIRO_COMMAND_STROKE: + { + cairo_traps_t traps; + + _cairo_traps_init (&traps); + + /* XXX call cairo_stroke_to_path() when that is implemented */ + status = _cairo_path_fixed_stroke_to_traps (&command->stroke.path, + &command->stroke.style, + &command->stroke.ctm, + &command->stroke.ctm_inverse, + command->stroke.tolerance, + &traps); + + if (status == CAIRO_STATUS_SUCCESS) + status = _cairo_traps_path (&traps, path); + + _cairo_traps_fini (&traps); + break; + } + case CAIRO_COMMAND_FILL: + { + status = _cairo_path_fixed_append (path, + &command->fill.path, CAIRO_DIRECTION_FORWARD, + 0, 0); + break; + } + case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: + { + status = _cairo_scaled_font_glyph_path (command->show_text_glyphs.scaled_font, + command->show_text_glyphs.glyphs, + command->show_text_glyphs.num_glyphs, + path); + break; + } + + default: + ASSERT_NOT_REACHED; + } + + if (unlikely (status)) + break; + } + + return _cairo_surface_set_error (surface, status); +} + +#define _clip(c) ((c)->header.clip.path ? &(c)->header.clip : NULL) +static cairo_status_t +_cairo_recording_surface_replay_internal (cairo_surface_t *surface, + cairo_surface_t *target, + cairo_recording_replay_type_t type, + cairo_recording_region_type_t region) +{ + cairo_recording_surface_t *recording_surface; + cairo_command_t **elements; + int i, num_elements; + cairo_int_status_t status; + cairo_surface_wrapper_t wrapper; + + if (unlikely (surface->status)) + return surface->status; + + if (unlikely (target->status)) + return _cairo_surface_set_error (surface, target->status); + + _cairo_surface_wrapper_init (&wrapper, target); + + recording_surface = (cairo_recording_surface_t *) surface; + status = CAIRO_STATUS_SUCCESS; + + num_elements = recording_surface->commands.num_elements; + elements = _cairo_array_index (&recording_surface->commands, 0); + for (i = recording_surface->replay_start_idx; i < num_elements; i++) { + cairo_command_t *command = elements[i]; + + if (type == CAIRO_RECORDING_REPLAY && region != CAIRO_RECORDING_REGION_ALL) { + if (command->header.region != region) + continue; + } + + switch (command->header.type) { + case CAIRO_COMMAND_PAINT: + status = _cairo_surface_wrapper_paint (&wrapper, + command->header.op, + &command->paint.source.base, + _clip (command)); + break; + + case CAIRO_COMMAND_MASK: + status = _cairo_surface_wrapper_mask (&wrapper, + command->header.op, + &command->mask.source.base, + &command->mask.mask.base, + _clip (command)); + break; + + case CAIRO_COMMAND_STROKE: + { + status = _cairo_surface_wrapper_stroke (&wrapper, + command->header.op, + &command->stroke.source.base, + &command->stroke.path, + &command->stroke.style, + &command->stroke.ctm, + &command->stroke.ctm_inverse, + command->stroke.tolerance, + command->stroke.antialias, + _clip (command)); + break; + } + case CAIRO_COMMAND_FILL: + { + cairo_command_t *stroke_command; + + stroke_command = NULL; + if (type != CAIRO_RECORDING_CREATE_REGIONS && i < num_elements - 1) + stroke_command = elements[i + 1]; + + if (stroke_command != NULL && + type == CAIRO_RECORDING_REPLAY && + region != CAIRO_RECORDING_REGION_ALL) + { + if (stroke_command->header.region != region) + stroke_command = NULL; + } + + if (stroke_command != NULL && + stroke_command->header.type == CAIRO_COMMAND_STROKE && + _cairo_path_fixed_is_equal (&command->fill.path, + &stroke_command->stroke.path)) + { + status = _cairo_surface_wrapper_fill_stroke (&wrapper, + command->header.op, + &command->fill.source.base, + command->fill.fill_rule, + command->fill.tolerance, + command->fill.antialias, + &command->fill.path, + stroke_command->header.op, + &stroke_command->stroke.source.base, + &stroke_command->stroke.style, + &stroke_command->stroke.ctm, + &stroke_command->stroke.ctm_inverse, + stroke_command->stroke.tolerance, + stroke_command->stroke.antialias, + _clip (command)); + i++; + } + else + { + status = _cairo_surface_wrapper_fill (&wrapper, + command->header.op, + &command->fill.source.base, + &command->fill.path, + command->fill.fill_rule, + command->fill.tolerance, + command->fill.antialias, + _clip (command)); + } + break; + } + case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: + { + cairo_glyph_t *glyphs = command->show_text_glyphs.glyphs; + cairo_glyph_t *glyphs_copy; + int num_glyphs = command->show_text_glyphs.num_glyphs; + + /* show_text_glyphs is special because _cairo_surface_show_text_glyphs is allowed + * to modify the glyph array that's passed in. We must always + * copy the array before handing it to the backend. + */ + glyphs_copy = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); + if (unlikely (glyphs_copy == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + break; + } + + memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs); + + status = _cairo_surface_wrapper_show_text_glyphs (&wrapper, + command->header.op, + &command->show_text_glyphs.source.base, + command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len, + glyphs_copy, num_glyphs, + command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters, + command->show_text_glyphs.cluster_flags, + command->show_text_glyphs.scaled_font, + _clip (command)); + free (glyphs_copy); + break; + } + default: + ASSERT_NOT_REACHED; + } + + if (type == CAIRO_RECORDING_CREATE_REGIONS) { + if (status == CAIRO_STATUS_SUCCESS) { + command->header.region = CAIRO_RECORDING_REGION_NATIVE; + } else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) { + command->header.region = CAIRO_RECORDING_REGION_IMAGE_FALLBACK; + status = CAIRO_STATUS_SUCCESS; + } else { + assert (_cairo_status_is_error (status)); + } + } + + if (unlikely (status)) + break; + } + + /* free up any caches */ + for (i = recording_surface->replay_start_idx; i < num_elements; i++) { + cairo_command_t *command = elements[i]; + + _cairo_clip_drop_cache (&command->header.clip); + } + + _cairo_surface_wrapper_fini (&wrapper); + + return _cairo_surface_set_error (surface, status); +} + +/** + * _cairo_recording_surface_replay: + * @surface: the #cairo_recording_surface_t + * @target: a target #cairo_surface_t onto which to replay the operations + * @width_pixels: width of the surface, in pixels + * @height_pixels: height of the surface, in pixels + * + * A recording surface can be "replayed" against any target surface, + * after which the results in target will be identical to the results + * that would have been obtained if the original operations applied to + * the recording surface had instead been applied to the target surface. + **/ +cairo_status_t +_cairo_recording_surface_replay (cairo_surface_t *surface, + cairo_surface_t *target) +{ + return _cairo_recording_surface_replay_internal (surface, + target, + CAIRO_RECORDING_REPLAY, + CAIRO_RECORDING_REGION_ALL); +} + +/* Replay recording to surface. When the return status of each operation is + * one of %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED, or + * %CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY the status of each operation + * will be stored in the recording surface. Any other status will abort the + * replay and return the status. + */ +cairo_status_t +_cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, + cairo_surface_t *target) +{ + return _cairo_recording_surface_replay_internal (surface, + target, + CAIRO_RECORDING_CREATE_REGIONS, + CAIRO_RECORDING_REGION_ALL); +} + +cairo_status_t +_cairo_recording_surface_replay_region (cairo_surface_t *surface, + cairo_surface_t *target, + cairo_recording_region_type_t region) +{ + return _cairo_recording_surface_replay_internal (surface, + target, + CAIRO_RECORDING_REPLAY, + region); +} + +static cairo_status_t +_recording_surface_get_ink_bbox (cairo_recording_surface_t *surface, + cairo_box_t *bbox, + const cairo_matrix_t *transform) +{ + cairo_surface_t *null_surface; + cairo_surface_t *analysis_surface; + cairo_status_t status; + + null_surface = _cairo_null_surface_create (surface->content); + analysis_surface = _cairo_analysis_surface_create (null_surface); + cairo_surface_destroy (null_surface); + + status = analysis_surface->status; + if (unlikely (status)) + return status; + + if (transform != NULL) + _cairo_analysis_surface_set_ctm (analysis_surface, transform); + + status = _cairo_recording_surface_replay (&surface->base, analysis_surface); + _cairo_analysis_surface_get_bounding_box (analysis_surface, bbox); + cairo_surface_destroy (analysis_surface); + + return status; +} + +/** + * cairo_recording_surface_ink_extents: + * @surface: a #cairo_recording_surface_t + * @x0: the x-coordinate of the top-left of the ink bounding box + * @y0: the y-coordinate of the top-left of the ink bounding box + * @width: the width of the ink bounding box + * @height: the height of the ink bounding box + * + * Measures the extents of the operations stored within the recording-surface. + * This is useful to compute the required size of an image surface (or + * equivalent) into which to replay the full sequence of drawing operations. + * + * Since: 1.10 + **/ +void +cairo_recording_surface_ink_extents (cairo_surface_t *surface, + double *x0, + double *y0, + double *width, + double *height) +{ + cairo_status_t status; + cairo_box_t bbox; + + memset (&bbox, 0, sizeof (bbox)); + + if (! _cairo_surface_is_recording (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + goto DONE; + } + + status = _recording_surface_get_ink_bbox ((cairo_recording_surface_t *) surface, + &bbox, + NULL); + if (unlikely (status)) + status = _cairo_surface_set_error (surface, status); + +DONE: + if (x0) + *x0 = _cairo_fixed_to_double (bbox.p1.x); + if (y0) + *y0 = _cairo_fixed_to_double (bbox.p1.y); + if (width) + *width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x); + if (height) + *height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y); +} + +cairo_status_t +_cairo_recording_surface_get_bbox (cairo_recording_surface_t *surface, + cairo_box_t *bbox, + const cairo_matrix_t *transform) +{ + if (! surface->unbounded) { + _cairo_box_from_rectangle (bbox, &surface->extents); + if (transform != NULL) + _cairo_matrix_transform_bounding_box_fixed (transform, bbox, NULL); + + return CAIRO_STATUS_SUCCESS; + } + + return _recording_surface_get_ink_bbox (surface, bbox, transform); +} diff --git a/gfx/cairo/cairo/src/cairo-rectangle.c b/gfx/cairo/cairo/src/cairo-rectangle.c index b139624870ea..e887c75134aa 100644 --- a/gfx/cairo/cairo/src/cairo-rectangle.c +++ b/gfx/cairo/cairo/src/cairo-rectangle.c @@ -71,6 +71,29 @@ _cairo_box_from_rectangle (cairo_box_t *box, box->p2.y = _cairo_fixed_from_int (rect->y + rect->height); } +void +_cairo_boxes_get_extents (const cairo_box_t *boxes, + int num_boxes, + cairo_box_t *extents) +{ + int n; + + assert (num_boxes > 0); + *extents = *boxes; + + for (n = 1; n < num_boxes; n++) { + if (boxes[n].p1.x < extents->p1.x) + extents->p1.x = boxes[n].p1.x; + if (boxes[n].p2.x > extents->p2.x) + extents->p2.x = boxes[n].p2.x; + + if (boxes[n].p1.y < extents->p1.y) + extents->p1.y = boxes[n].p1.y; + if (boxes[n].p2.y > extents->p2.y) + extents->p2.y = boxes[n].p2.y; + } +} + /* XXX We currently have a confusing mix of boxes and rectangles as * exemplified by this function. A #cairo_box_t is a rectangular area * represented by the coordinates of the upper left and lower right diff --git a/gfx/cairo/cairo/src/cairo-region-private.h b/gfx/cairo/cairo/src/cairo-region-private.h index 61738062c621..507f72e842f0 100644 --- a/gfx/cairo/cairo/src/cairo-region-private.h +++ b/gfx/cairo/cairo/src/cairo-region-private.h @@ -1,7 +1,7 @@ /* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ -/* Cairo - a vector graphics library with display and print output +/* cairo - a vector graphics library with display and print output * - * Copyright © 2007 Mozilla Corporation + * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -28,25 +28,28 @@ * * The Original Code is the cairo graphics library. * - * The Initial Developer of the Original Code is Mozilla Foundation + * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): - * Vladimir Vukicevic + * Owen Taylor + * Vladimir Vukicevic + * Søren Sandmann */ #ifndef CAIRO_REGION_PRIVATE_H #define CAIRO_REGION_PRIVATE_H -#include "cairo-compiler-private.h" #include "cairo-types-private.h" +#include "cairo-reference-count-private.h" #include CAIRO_BEGIN_DECLS -/* #cairo_region_t is defined in cairoint.h */ - struct _cairo_region { + cairo_reference_count_t ref_count; + cairo_status_t status; + pixman_region32_t rgn; }; @@ -54,60 +57,12 @@ cairo_private void _cairo_region_init (cairo_region_t *region); cairo_private void -_cairo_region_init_rect (cairo_region_t *region, - cairo_rectangle_int_t *rect); - -cairo_private cairo_int_status_t -_cairo_region_init_boxes (cairo_region_t *region, - cairo_box_int_t *boxes, - int count); +_cairo_region_init_rectangle (cairo_region_t *region, + const cairo_rectangle_int_t *rectangle); cairo_private void _cairo_region_fini (cairo_region_t *region); -cairo_private cairo_int_status_t -_cairo_region_copy (cairo_region_t *dst, - cairo_region_t *src); - -cairo_private int -_cairo_region_num_boxes (cairo_region_t *region); - -cairo_private void -_cairo_region_get_box (cairo_region_t *region, - int nth_box, - cairo_box_int_t *box); - -cairo_private void -_cairo_region_get_extents (cairo_region_t *region, - cairo_rectangle_int_t *extents); - -cairo_private cairo_int_status_t -_cairo_region_subtract (cairo_region_t *dst, - cairo_region_t *a, - cairo_region_t *b); - -cairo_private cairo_int_status_t -_cairo_region_intersect (cairo_region_t *dst, - cairo_region_t *a, - cairo_region_t *b); - -cairo_private cairo_int_status_t -_cairo_region_union_rect (cairo_region_t *dst, - cairo_region_t *src, - cairo_rectangle_int_t *rect); - -cairo_private cairo_bool_t -_cairo_region_not_empty (cairo_region_t *region); - -cairo_private void -_cairo_region_translate (cairo_region_t *region, - int x, int y); - -cairo_private pixman_region_overlap_t -_cairo_region_contains_rectangle (cairo_region_t *region, - const cairo_rectangle_int_t *box); - - CAIRO_END_DECLS #endif /* CAIRO_REGION_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-region.c b/gfx/cairo/cairo/src/cairo-region.c index 6dfa9332de3f..2148fcabbc25 100644 --- a/gfx/cairo/cairo/src/cairo-region.c +++ b/gfx/cairo/cairo/src/cairo-region.c @@ -38,7 +38,13 @@ #include "cairoint.h" +#include "cairo-region-private.h" + +/* XXX need to update pixman headers to be const as appropriate */ +#define CONST_CAST (pixman_region32_t *) + static const cairo_region_t _cairo_region_nil = { + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ CAIRO_STATUS_NO_MEMORY, /* status */ }; @@ -82,6 +88,7 @@ _cairo_region_init (cairo_region_t *region) VG (VALGRIND_MAKE_MEM_UNDEFINED (region, sizeof (cairo_region_t))); region->status = CAIRO_STATUS_SUCCESS; + CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 0); pixman_region32_init (®ion->rgn); } @@ -92,6 +99,7 @@ _cairo_region_init_rectangle (cairo_region_t *region, VG (VALGRIND_MAKE_MEM_UNDEFINED (region, sizeof (cairo_region_t))); region->status = CAIRO_STATUS_SUCCESS; + CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 0); pixman_region32_init_rect (®ion->rgn, rectangle->x, rectangle->y, rectangle->width, rectangle->height); @@ -100,6 +108,7 @@ _cairo_region_init_rectangle (cairo_region_t *region, void _cairo_region_fini (cairo_region_t *region) { + assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (®ion->ref_count)); pixman_region32_fini (®ion->rgn); VG (VALGRIND_MAKE_MEM_NOACCESS (region, sizeof (cairo_region_t))); } @@ -127,6 +136,7 @@ cairo_region_create (void) return (cairo_region_t *) &_cairo_region_nil; region->status = CAIRO_STATUS_SUCCESS; + CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 1); pixman_region32_init (®ion->rgn); @@ -135,7 +145,7 @@ cairo_region_create (void) slim_hidden_def (cairo_region_create); cairo_region_t * -cairo_region_create_rectangles (cairo_rectangle_int_t *rects, +cairo_region_create_rectangles (const cairo_rectangle_int_t *rects, int count) { pixman_box32_t stack_pboxes[CAIRO_STACK_ARRAY_LENGTH (pixman_box32_t)]; @@ -144,18 +154,17 @@ cairo_region_create_rectangles (cairo_rectangle_int_t *rects, int i; region = _cairo_malloc (sizeof (cairo_region_t)); - - if (!region) - return (cairo_region_t *)&_cairo_region_nil; - - region->status = CAIRO_STATUS_SUCCESS; + if (unlikely (region == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_region_t *) &_cairo_region_nil; + } if (count > ARRAY_LENGTH (stack_pboxes)) { pboxes = _cairo_malloc_ab (count, sizeof (pixman_box32_t)); - if (unlikely (pboxes == NULL)) { free (region); - return (cairo_region_t *)&_cairo_region_nil; + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_region_t *) &_cairo_region_nil; } } @@ -166,15 +175,19 @@ cairo_region_create_rectangles (cairo_rectangle_int_t *rects, pboxes[i].y2 = rects[i].y + rects[i].height; } - if (! pixman_region32_init_rects (®ion->rgn, pboxes, count)) { - free (region); - - region = (cairo_region_t *)&_cairo_region_nil; - } + i = pixman_region32_init_rects (®ion->rgn, pboxes, count); if (pboxes != stack_pboxes) free (pboxes); + if (unlikely (i == 0)) { + free (region); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_region_t *) &_cairo_region_nil; + } + + CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 1); + region->status = CAIRO_STATUS_SUCCESS; return region; } slim_hidden_def (cairo_region_create_rectangles); @@ -199,10 +212,11 @@ cairo_region_create_rectangle (const cairo_rectangle_int_t *rectangle) cairo_region_t *region; region = _cairo_malloc (sizeof (cairo_region_t)); - if (region == NULL) + if (unlikely (region == NULL)) return (cairo_region_t *) &_cairo_region_nil; region->status = CAIRO_STATUS_SUCCESS; + CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 1); pixman_region32_init_rect (®ion->rgn, rectangle->x, rectangle->y, @@ -227,18 +241,20 @@ slim_hidden_def (cairo_region_create_rectangle); * Since: 1.10 **/ cairo_region_t * -cairo_region_copy (cairo_region_t *original) +cairo_region_copy (const cairo_region_t *original) { cairo_region_t *copy; - if (original->status) + if (original != NULL && original->status) return (cairo_region_t *) &_cairo_region_nil; copy = cairo_region_create (); - if (copy->status) + if (unlikely (copy->status)) return copy; - if (! pixman_region32_copy (©->rgn, &original->rgn)) { + if (original != NULL && + ! pixman_region32_copy (©->rgn, CONST_CAST &original->rgn)) + { cairo_region_destroy (copy); return (cairo_region_t *) &_cairo_region_nil; } @@ -247,6 +263,31 @@ cairo_region_copy (cairo_region_t *original) } slim_hidden_def (cairo_region_copy); +/** + * cairo_region_reference: + * @region: a #cairo_region_t + * + * Increases the reference count on @region by one. This prevents + * @region from being destroyed until a matching call to + * cairo_region_destroy() is made. + * + * Return value: the referenced #cairo_region_t. + * + * Since: 1.10 + **/ +cairo_region_t * +cairo_region_reference (cairo_region_t *region) +{ + if (region == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (®ion->ref_count)) + return NULL; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (®ion->ref_count)); + + _cairo_reference_count_inc (®ion->ref_count); + return region; +} +slim_hidden_def (cairo_region_reference); + /** * cairo_region_destroy: * @region: a #cairo_region_t @@ -260,10 +301,15 @@ slim_hidden_def (cairo_region_copy); void cairo_region_destroy (cairo_region_t *region) { - if (region == (cairo_region_t *) &_cairo_region_nil) + if (region == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (®ion->ref_count)) return; - pixman_region32_fini (®ion->rgn); + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (®ion->ref_count)); + + if (! _cairo_reference_count_dec_and_test (®ion->ref_count)) + return; + + _cairo_region_fini (region); free (region); } slim_hidden_def (cairo_region_destroy); @@ -279,12 +325,12 @@ slim_hidden_def (cairo_region_destroy); * Since: 1.10 **/ int -cairo_region_num_rectangles (cairo_region_t *region) +cairo_region_num_rectangles (const cairo_region_t *region) { if (region->status) return 0; - return pixman_region32_n_rects (®ion->rgn); + return pixman_region32_n_rects (CONST_CAST ®ion->rgn); } slim_hidden_def (cairo_region_num_rectangles); @@ -299,7 +345,7 @@ slim_hidden_def (cairo_region_num_rectangles); * Since: 1.10 **/ void -cairo_region_get_rectangle (cairo_region_t *region, +cairo_region_get_rectangle (const cairo_region_t *region, int nth, cairo_rectangle_int_t *rectangle) { @@ -311,7 +357,7 @@ cairo_region_get_rectangle (cairo_region_t *region, return; } - pbox = pixman_region32_rectangles (®ion->rgn, NULL) + nth; + pbox = pixman_region32_rectangles (CONST_CAST ®ion->rgn, NULL) + nth; rectangle->x = pbox->x1; rectangle->y = pbox->y1; @@ -330,7 +376,7 @@ slim_hidden_def (cairo_region_get_rectangle); * Since: 1.10 **/ void -cairo_region_get_extents (cairo_region_t *region, +cairo_region_get_extents (const cairo_region_t *region, cairo_rectangle_int_t *extents) { pixman_box32_t *pextents; @@ -341,7 +387,7 @@ cairo_region_get_extents (cairo_region_t *region, return; } - pextents = pixman_region32_extents (®ion->rgn); + pextents = pixman_region32_extents (CONST_CAST ®ion->rgn); extents->x = pextents->x1; extents->y = pextents->y1; @@ -362,7 +408,7 @@ slim_hidden_def (cairo_region_get_extents); * Since: 1.10 **/ cairo_status_t -cairo_region_status (cairo_region_t *region) +cairo_region_status (const cairo_region_t *region) { return region->status; } @@ -380,7 +426,7 @@ slim_hidden_def (cairo_region_status); * Since: 1.10 **/ cairo_status_t -cairo_region_subtract (cairo_region_t *dst, cairo_region_t *other) +cairo_region_subtract (cairo_region_t *dst, const cairo_region_t *other) { if (dst->status) return dst->status; @@ -388,8 +434,12 @@ cairo_region_subtract (cairo_region_t *dst, cairo_region_t *other) if (other->status) return _cairo_region_set_error (dst, other->status); - if (! pixman_region32_subtract (&dst->rgn, &dst->rgn, &other->rgn)) + if (! pixman_region32_subtract (&dst->rgn, + &dst->rgn, + CONST_CAST &other->rgn)) + { return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + } return CAIRO_STATUS_SUCCESS; } @@ -564,12 +614,12 @@ slim_hidden_def (cairo_region_union_rectangle); * Since: 1.10 **/ cairo_bool_t -cairo_region_is_empty (cairo_region_t *region) +cairo_region_is_empty (const cairo_region_t *region) { if (region->status) return TRUE; - return ! pixman_region32_not_empty (®ion->rgn); + return ! pixman_region32_not_empty (CONST_CAST ®ion->rgn); } slim_hidden_def (cairo_region_is_empty); @@ -610,7 +660,7 @@ slim_hidden_def (cairo_region_translate); * Since: 1.10 **/ cairo_region_overlap_t -cairo_region_contains_rectangle (cairo_region_t *region, +cairo_region_contains_rectangle (const cairo_region_t *region, const cairo_rectangle_int_t *rectangle) { pixman_box32_t pbox; @@ -624,7 +674,8 @@ cairo_region_contains_rectangle (cairo_region_t *region, pbox.x2 = rectangle->x + rectangle->width; pbox.y2 = rectangle->y + rectangle->height; - poverlap = pixman_region32_contains_rectangle (®ion->rgn, &pbox); + poverlap = pixman_region32_contains_rectangle (CONST_CAST ®ion->rgn, + &pbox); switch (poverlap) { default: case PIXMAN_REGION_OUT: return CAIRO_REGION_OVERLAP_OUT; @@ -647,14 +698,44 @@ slim_hidden_def (cairo_region_contains_rectangle); * Since: 1.10 **/ cairo_bool_t -cairo_region_contains_point (cairo_region_t *region, +cairo_region_contains_point (const cairo_region_t *region, int x, int y) { pixman_box32_t box; - + if (region->status) return FALSE; - return pixman_region32_contains_point (®ion->rgn, x, y, &box); + return pixman_region32_contains_point (CONST_CAST ®ion->rgn, x, y, &box); } slim_hidden_def (cairo_region_contains_point); + +/** + * cairo_region_equal: + * @region_a: a #cairo_region_t + * @region_b: a #cairo_region_t + * + * Compares whether region_a is equivalent to region_b. + * + * Return value: %TRUE if both regions contained the same coverage, + * %FALSE if it is not. + * + * Since: 1.10 + **/ +cairo_bool_t +cairo_region_equal (const cairo_region_t *a, + const cairo_region_t *b) +{ + /* error objects are never equal */ + if ((a != NULL && a->status) || (b != NULL && b->status)) + return FALSE; + + if (a == b) + return TRUE; + + if (a == NULL || b == NULL) + return FALSE; + + return pixman_region32_equal (CONST_CAST &a->rgn, CONST_CAST &b->rgn); +} +slim_hidden_def (cairo_region_equal); diff --git a/gfx/cairo/cairo/src/cairo-rtree-private.h b/gfx/cairo/cairo/src/cairo-rtree-private.h new file mode 100644 index 000000000000..edb7e3acb820 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-rtree-private.h @@ -0,0 +1,128 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Chris Wilson. + * + * Contributor(s): + * Chris Wilson + * + */ + +#ifndef CAIRO_RTREE_PRIVATE_H +#define CAIRO_RTREE_PRIVATE_H + +#include "cairo-compiler-private.h" +#include "cairo-types-private.h" + +#include "cairo-freelist-private.h" +#include "cairo-list-private.h" + +enum { + CAIRO_RTREE_NODE_AVAILABLE, + CAIRO_RTREE_NODE_DIVIDED, + CAIRO_RTREE_NODE_OCCUPIED, +}; + +typedef struct _cairo_rtree_node { + struct _cairo_rtree_node *children[4], *parent; + void **owner; + cairo_list_t link; + uint16_t pinned; + uint16_t state; + uint16_t x, y; + uint16_t width, height; +} cairo_rtree_node_t; + +typedef struct _cairo_rtree { + cairo_rtree_node_t root; + int min_size; + void (*evict) (void *node); + cairo_list_t pinned; + cairo_list_t available; + cairo_list_t evictable; + cairo_freepool_t node_freepool; +} cairo_rtree_t; + +cairo_private cairo_rtree_node_t * +_cairo_rtree_node_create (cairo_rtree_t *rtree, + cairo_rtree_node_t *parent, + int x, + int y, + int width, + int height); + +cairo_private cairo_status_t +_cairo_rtree_node_insert (cairo_rtree_t *rtree, + cairo_rtree_node_t *node, + int width, + int height, + cairo_rtree_node_t **out); + +cairo_private void +_cairo_rtree_node_collapse (cairo_rtree_t *rtree, cairo_rtree_node_t *node); + +cairo_private void +_cairo_rtree_node_remove (cairo_rtree_t *rtree, cairo_rtree_node_t *node); + +cairo_private void +_cairo_rtree_node_destroy (cairo_rtree_t *rtree, cairo_rtree_node_t *node); + +cairo_private void +_cairo_rtree_init (cairo_rtree_t *rtree, + int width, + int height, + int min_size, + int node_size, + void (*evict) (void *node)); + +cairo_private cairo_int_status_t +_cairo_rtree_insert (cairo_rtree_t *rtree, + int width, + int height, + cairo_rtree_node_t **out); + +cairo_private cairo_int_status_t +_cairo_rtree_evict_random (cairo_rtree_t *rtree, + int width, + int height, + cairo_rtree_node_t **out); + +cairo_private void * +_cairo_rtree_pin (cairo_rtree_t *rtree, cairo_rtree_node_t *node); + +cairo_private void +_cairo_rtree_unpin (cairo_rtree_t *rtree); + +cairo_private void +_cairo_rtree_reset (cairo_rtree_t *rtree); + +cairo_private void +_cairo_rtree_fini (cairo_rtree_t *rtree); + +#endif /* CAIRO_RTREE_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-rtree.c b/gfx/cairo/cairo/src/cairo-rtree.c new file mode 100644 index 000000000000..d4bdbd4bdaa0 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-rtree.c @@ -0,0 +1,408 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Chris Wilson. + * + * Contributor(s): + * Chris Wilson + * + */ + +#include "cairoint.h" + +#include "cairo-rtree-private.h" + +cairo_rtree_node_t * +_cairo_rtree_node_create (cairo_rtree_t *rtree, + cairo_rtree_node_t *parent, + int x, + int y, + int width, + int height) +{ + cairo_rtree_node_t *node; + + node = _cairo_freepool_alloc (&rtree->node_freepool); + if (node == NULL) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + node->children[0] = NULL; + node->parent = parent; + node->owner = NULL; + node->state = CAIRO_RTREE_NODE_AVAILABLE; + node->pinned = FALSE; + node->x = x; + node->y = y; + node->width = width; + node->height = height; + + cairo_list_add (&node->link, &rtree->available); + + return node; +} + +void +_cairo_rtree_node_destroy (cairo_rtree_t *rtree, cairo_rtree_node_t *node) +{ + int i; + + cairo_list_del (&node->link); + + if (node->state == CAIRO_RTREE_NODE_OCCUPIED) { + if (node->owner != NULL) + *node->owner = NULL; + if (rtree->evict != NULL) + rtree->evict (node); + } else { + for (i = 0; i < 4 && node->children[i] != NULL; i++) + _cairo_rtree_node_destroy (rtree, node->children[i]); + } + + _cairo_freepool_free (&rtree->node_freepool, node); +} + +void +_cairo_rtree_node_collapse (cairo_rtree_t *rtree, cairo_rtree_node_t *node) +{ + int i; + + assert (node->pinned == FALSE); + + do { + assert (node->state == CAIRO_RTREE_NODE_DIVIDED); + + for (i = 0; i < 4 && node->children[i] != NULL; i++) + if (node->children[i]->state != CAIRO_RTREE_NODE_AVAILABLE) + return; + + for (i = 0; i < 4 && node->children[i] != NULL; i++) + _cairo_rtree_node_destroy (rtree, node->children[i]); + + node->children[0] = NULL; + node->state = CAIRO_RTREE_NODE_AVAILABLE; + cairo_list_move (&node->link, &rtree->available); + + node = node->parent; + } while (node != NULL && ! node->pinned); +} + +cairo_status_t +_cairo_rtree_node_insert (cairo_rtree_t *rtree, + cairo_rtree_node_t *node, + int width, + int height, + cairo_rtree_node_t **out) +{ + int w, h, i; + + assert (node->state == CAIRO_RTREE_NODE_AVAILABLE); + assert (node->pinned == FALSE); + + if (node->width - width > rtree->min_size || + node->height - height > rtree->min_size) + { + w = node->width - width; + h = node->height - height; + + i = 0; + node->children[i] = _cairo_rtree_node_create (rtree, node, + node->x, node->y, + width, height); + if (unlikely (node->children[i] == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + i++; + + if (w > rtree->min_size) { + node->children[i] = _cairo_rtree_node_create (rtree, node, + node->x + width, + node->y, + w, height); + if (unlikely (node->children[i] == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + i++; + } + + if (h > rtree->min_size) { + node->children[i] = _cairo_rtree_node_create (rtree, node, + node->x, + node->y + height, + width, h); + if (unlikely (node->children[i] == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + i++; + + if (w > rtree->min_size) { + node->children[i] = _cairo_rtree_node_create (rtree, node, + node->x + width, + node->y + height, + w, h); + if (unlikely (node->children[i] == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + i++; + } + } + + if (i < 4) + node->children[i] = NULL; + + node->state = CAIRO_RTREE_NODE_DIVIDED; + cairo_list_move (&node->link, &rtree->evictable); + node = node->children[0]; + } + + node->state = CAIRO_RTREE_NODE_OCCUPIED; + cairo_list_move (&node->link, &rtree->evictable); + *out = node; + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_rtree_node_remove (cairo_rtree_t *rtree, cairo_rtree_node_t *node) +{ + assert (node->state == CAIRO_RTREE_NODE_OCCUPIED); + assert (node->pinned == FALSE); + + node->state = CAIRO_RTREE_NODE_AVAILABLE; + cairo_list_move (&node->link, &rtree->available); + + if (! node->parent->pinned) + _cairo_rtree_node_collapse (rtree, node->parent); +} + +cairo_int_status_t +_cairo_rtree_insert (cairo_rtree_t *rtree, + int width, + int height, + cairo_rtree_node_t **out) +{ + cairo_rtree_node_t *node; + + cairo_list_foreach_entry (node, cairo_rtree_node_t, + &rtree->available, link) + { + if (node->width >= width && node->height >= height) + return _cairo_rtree_node_insert (rtree, node, width, height, out); + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static uint32_t +hars_petruska_f54_1_random (void) +{ +#define rol(x,k) ((x << k) | (x >> (32-k))) + static uint32_t x; + return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849; +#undef rol +} + +cairo_int_status_t +_cairo_rtree_evict_random (cairo_rtree_t *rtree, + int width, + int height, + cairo_rtree_node_t **out) +{ + cairo_rtree_node_t *node; + int i, cnt; + + cnt = 0; + cairo_list_foreach_entry (node, cairo_rtree_node_t, + &rtree->evictable, link) + { + if (node->width >= width && node->height >= height) + cnt++; + } + + if (cnt == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cnt = hars_petruska_f54_1_random () % cnt; + cairo_list_foreach_entry (node, cairo_rtree_node_t, + &rtree->evictable, link) + { + if (node->width >= width && node->height >= height && cnt-- == 0) { + if (node->state == CAIRO_RTREE_NODE_OCCUPIED) { + if (node->owner != NULL) + *node->owner = NULL; + if (rtree->evict != NULL) + rtree->evict (node); + } else { + for (i = 0; i < 4 && node->children[i] != NULL; i++) + _cairo_rtree_node_destroy (rtree, node->children[i]); + node->children[0] = NULL; + } + + node->state = CAIRO_RTREE_NODE_AVAILABLE; + cairo_list_move (&node->link, &rtree->available); + + *out = node; + return CAIRO_STATUS_SUCCESS; + } + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +void * +_cairo_rtree_pin (cairo_rtree_t *rtree, cairo_rtree_node_t *node) +{ + void *ptr = node; + + while (node->pinned == FALSE) { + cairo_list_move (&node->link, &rtree->pinned); + node->pinned = TRUE; + node = node->parent; + if (node == NULL) + break; + } + + return ptr; +} + +void +_cairo_rtree_unpin (cairo_rtree_t *rtree) +{ + cairo_rtree_node_t *node, *next; + cairo_list_t can_collapse; + + if (cairo_list_is_empty (&rtree->pinned)) + return; + + cairo_list_init (&can_collapse); + + cairo_list_foreach_entry_safe (node, next, + cairo_rtree_node_t, + &rtree->pinned, + link) + { + node->pinned = FALSE; + if (node->state == CAIRO_RTREE_NODE_OCCUPIED && node->owner == NULL) { + cairo_bool_t all_available; + int i; + + node->state = CAIRO_RTREE_NODE_AVAILABLE; + cairo_list_move (&node->link, &rtree->available); + + all_available = TRUE; + node = node->parent; + for (i = 0; i < 4 && node->children[i] != NULL && all_available; i++) + all_available &= node->children[i]->state == CAIRO_RTREE_NODE_AVAILABLE; + + if (all_available) { + cairo_list_move (&node->link, &can_collapse); + for (i = 0; i < 4 && node->children[i] != NULL; i++) + cairo_list_del (&node->children[i]->link); + } + } + else + { + cairo_list_move (&node->link, &rtree->evictable); + } + } + + cairo_list_foreach_entry_safe (node, next, + cairo_rtree_node_t, + &can_collapse, + link) + { + _cairo_rtree_node_collapse (rtree, node); + } +} + +void +_cairo_rtree_init (cairo_rtree_t *rtree, + int width, + int height, + int min_size, + int node_size, + void (*evict) (void *node)) +{ + rtree->evict = evict; + + assert (node_size >= (int) sizeof (cairo_rtree_node_t)); + _cairo_freepool_init (&rtree->node_freepool, node_size); + + cairo_list_init (&rtree->available); + cairo_list_init (&rtree->pinned); + cairo_list_init (&rtree->evictable); + + rtree->min_size = min_size; + + memset (&rtree->root, 0, sizeof (rtree->root)); + rtree->root.width = width; + rtree->root.height = height; + rtree->root.state = CAIRO_RTREE_NODE_AVAILABLE; + cairo_list_add (&rtree->root.link, &rtree->available); +} + +void +_cairo_rtree_reset (cairo_rtree_t *rtree) +{ + int i; + + if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) { + if (rtree->root.owner != NULL) + *rtree->root.owner = NULL; + if (rtree->evict != NULL) + rtree->evict (&rtree->root); + } else { + for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++) + _cairo_rtree_node_destroy (rtree, rtree->root.children[i]); + rtree->root.children[0] = NULL; + } + + cairo_list_init (&rtree->available); + cairo_list_init (&rtree->evictable); + cairo_list_init (&rtree->pinned); + + rtree->root.state = CAIRO_RTREE_NODE_AVAILABLE; + rtree->root.pinned = FALSE; + cairo_list_add (&rtree->root.link, &rtree->available); +} + +void +_cairo_rtree_fini (cairo_rtree_t *rtree) +{ + int i; + + if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) { + if (rtree->root.owner != NULL) + *rtree->root.owner = NULL; + if (rtree->evict != NULL) + rtree->evict (&rtree->root); + } else { + for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++) + _cairo_rtree_node_destroy (rtree, rtree->root.children[i]); + } + + _cairo_freepool_fini (&rtree->node_freepool); +} diff --git a/gfx/cairo/cairo/src/cairo-scaled-font-private.h b/gfx/cairo/cairo/src/cairo-scaled-font-private.h index dc16ea871c7d..cdc3c73d2271 100644 --- a/gfx/cairo/cairo/src/cairo-scaled-font-private.h +++ b/gfx/cairo/cairo/src/cairo-scaled-font-private.h @@ -123,4 +123,7 @@ struct _cairo_scaled_font { const cairo_scaled_font_backend_t *backend; }; +cairo_private void +_cairo_scaled_font_revoke_ownership (cairo_scaled_font_t *scaled_font); + #endif /* CAIRO_SCALED_FONT_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-scaled-font-subsets.c b/gfx/cairo/cairo/src/cairo-scaled-font-subsets.c index 64c9d9aadb46..054782950040 100644 --- a/gfx/cairo/cairo/src/cairo-scaled-font-subsets.c +++ b/gfx/cairo/cairo/src/cairo-scaled-font-subsets.c @@ -294,7 +294,7 @@ _cairo_sub_font_create (cairo_scaled_font_subsets_t *parent, /* Reserve first glyph in subset for the .notdef glyph except for * Type 3 fonts */ - if (! _cairo_font_face_is_user (scaled_font->font_face)) { + if (! is_scaled) { status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, -1, &subset_glyph); if (unlikely (status)) { _cairo_hash_table_destroy (sub_font->sub_font_glyphs); @@ -570,6 +570,7 @@ _cairo_sub_font_collect (void *entry, void *closure) subset.scaled_font = sub_font->scaled_font; subset.is_composite = sub_font->is_composite; + subset.is_scaled = sub_font->is_scaled; subset.font_id = sub_font->font_id; subset.subset_id = i; subset.glyphs = collection->glyphs; @@ -1008,7 +1009,7 @@ _cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset } i = 0; - if (! _cairo_font_face_is_user (subset->scaled_font->font_face)) { + if (! subset->is_scaled) { subset->glyph_names[0] = strdup (".notdef"); if (unlikely (subset->glyph_names[0] == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); diff --git a/gfx/cairo/cairo/src/cairo-scaled-font.c b/gfx/cairo/cairo/src/cairo-scaled-font.c index 1f4da749168a..e1a89f8b0bac 100644 --- a/gfx/cairo/cairo/src/cairo-scaled-font.c +++ b/gfx/cairo/cairo/src/cairo-scaled-font.c @@ -199,8 +199,8 @@ _cairo_scaled_glyph_fini (cairo_scaled_font_t *scaled_font, if (scaled_glyph->path != NULL) _cairo_path_fixed_destroy (scaled_glyph->path); - if (scaled_glyph->meta_surface != NULL) - cairo_surface_destroy (scaled_glyph->meta_surface); + if (scaled_glyph->recording_surface != NULL) + cairo_surface_destroy (scaled_glyph->recording_surface); } #define ZOMBIE 0 @@ -838,6 +838,22 @@ _cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font) _cairo_user_data_array_fini (&scaled_font->user_data); } +/* XXX: allow multiple backends to share the font */ +void +_cairo_scaled_font_revoke_ownership (cairo_scaled_font_t *scaled_font) +{ + if (scaled_font->surface_backend == NULL) + return; + + _cairo_scaled_font_reset_cache (scaled_font); + + if (scaled_font->surface_backend->scaled_font_fini != NULL) + scaled_font->surface_backend->scaled_font_fini (scaled_font); + + scaled_font->surface_backend = NULL; + scaled_font->surface_private = NULL; +} + void _cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font) { @@ -931,6 +947,9 @@ cairo_scaled_font_create (cairo_font_face_t *font_face, ctm, options); } + + _cairo_scaled_font_init_key (&key, font_face, + font_matrix, ctm, options); } else { @@ -998,6 +1017,8 @@ cairo_scaled_font_create (cairo_font_face_t *font_face, _cairo_scaled_font_map_unlock (); cairo_scaled_font_destroy (old); + if (font_face != original_font_face) + cairo_font_face_destroy (font_face); return scaled_font; } @@ -1015,12 +1036,18 @@ cairo_scaled_font_create (cairo_font_face_t *font_face, /* Did we leave the backend in an error state? */ if (unlikely (status)) { _cairo_scaled_font_map_unlock (); + if (font_face != original_font_face) + cairo_font_face_destroy (font_face); + status = _cairo_font_face_set_error (font_face, status); return _cairo_scaled_font_create_in_error (status); } /* Or did we encounter an error whilst constructing the scaled font? */ if (unlikely (scaled_font->status)) { _cairo_scaled_font_map_unlock (); + if (font_face != original_font_face) + cairo_font_face_destroy (font_face); + return scaled_font; } @@ -1033,7 +1060,6 @@ cairo_scaled_font_create (cairo_font_face_t *font_face, scaled_font->original_font_face = cairo_font_face_reference (original_font_face); - assert (scaled_font->hash_entry.hash == key.hash_entry.hash); status = _cairo_hash_table_insert (font_map->hash_table, &scaled_font->hash_entry); if (likely (status == CAIRO_STATUS_SUCCESS)) { @@ -1044,6 +1070,10 @@ cairo_scaled_font_create (cairo_font_face_t *font_face, _cairo_scaled_font_map_unlock (); + cairo_scaled_font_destroy (old); + if (font_face != original_font_face) + cairo_font_face_destroy (font_face); + if (unlikely (status)) { /* We can't call _cairo_scaled_font_destroy here since it expects * that the font has already been successfully inserted into the @@ -1053,11 +1083,6 @@ cairo_scaled_font_create (cairo_font_face_t *font_face, return _cairo_scaled_font_create_in_error (status); } - cairo_scaled_font_destroy (old); - - if (font_face != original_font_face) - cairo_font_face_destroy (font_face); - return scaled_font; } slim_hidden_def (cairo_scaled_font_create); @@ -1428,22 +1453,22 @@ cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, extents->x_advance = 0.0; extents->y_advance = 0.0; - if (scaled_font->status) - return; + if (unlikely (scaled_font->status)) + goto ZERO_EXTENTS; if (num_glyphs == 0) - return; + goto ZERO_EXTENTS; - if (num_glyphs < 0) { + if (unlikely (num_glyphs < 0)) { _cairo_error_throw (CAIRO_STATUS_NEGATIVE_COUNT); /* XXX Can't propagate error */ - return; + goto ZERO_EXTENTS; } - if (glyphs == NULL) { + if (unlikely (glyphs == NULL)) { _cairo_error_throw (CAIRO_STATUS_NULL_POINTER); /* XXX Can't propagate error */ - return; + goto ZERO_EXTENTS; } _cairo_scaled_font_freeze_cache (scaled_font); @@ -1514,6 +1539,15 @@ cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, UNLOCK: _cairo_scaled_font_thaw_cache (scaled_font); + return; + +ZERO_EXTENTS: + extents->x_bearing = 0.0; + extents->y_bearing = 0.0; + extents->width = 0.0; + extents->height = 0.0; + extents->x_advance = 0.0; + extents->y_advance = 0.0; } slim_hidden_def (cairo_scaled_font_glyph_extents); @@ -1578,11 +1612,11 @@ slim_hidden_def (cairo_scaled_font_glyph_extents); * if (status == CAIRO_STATUS_SUCCESS) { * cairo_show_text_glyphs (cr, * utf8, utf8_len, - * *glyphs, *num_glyphs, - * *clusters, *num_clusters, *cluster_flags); + * glyphs, num_glyphs, + * clusters, num_clusters, cluster_flags); * - * cairo_glyph_free (*glyphs); - * cairo_text_cluster_free (*clusters); + * cairo_glyph_free (glyphs); + * cairo_text_cluster_free (clusters); * } * * @@ -1601,8 +1635,8 @@ slim_hidden_def (cairo_scaled_font_glyph_extents); * NULL); * * if (status == CAIRO_STATUS_SUCCESS) { - * cairo_show_glyphs (cr, *glyphs, *num_glyphs); - * cairo_glyph_free (*glyphs); + * cairo_show_glyphs (cr, glyphs, num_glyphs); + * cairo_glyph_free (glyphs); * } * * @@ -1628,13 +1662,13 @@ slim_hidden_def (cairo_scaled_font_glyph_extents); * if (status == CAIRO_STATUS_SUCCESS) { * cairo_show_text_glyphs (cr, * utf8, utf8_len, - * *glyphs, *num_glyphs, - * *clusters, *num_clusters, *cluster_flags); + * glyphs, num_glyphs, + * clusters, num_clusters, cluster_flags); * * if (glyphs != stack_glyphs) - * cairo_glyph_free (*glyphs); + * cairo_glyph_free (glyphs); * if (clusters != stack_clusters) - * cairo_text_cluster_free (*clusters); + * cairo_text_cluster_free (clusters); * } * * @@ -1893,6 +1927,15 @@ cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, } slim_hidden_def (cairo_scaled_font_text_to_glyphs); +static inline cairo_bool_t +_range_contains_glyph (const cairo_point_int_t *min, + const cairo_point_int_t *max, + int left, int top, + int right, int bottom) +{ + return right > min->x && left < max->x && bottom > min->y && top < max->y; +} + /* * Compute a device-space bounding box for the glyphs. */ @@ -1900,13 +1943,15 @@ cairo_status_t _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font, const cairo_glyph_t *glyphs, int num_glyphs, - cairo_rectangle_int_t *extents) + cairo_rectangle_int_t *extents, + cairo_bool_t *overlap_out) { cairo_status_t status = CAIRO_STATUS_SUCCESS; - int i; cairo_point_int_t min = { CAIRO_RECT_INT_MAX, CAIRO_RECT_INT_MAX }; cairo_point_int_t max = { CAIRO_RECT_INT_MIN, CAIRO_RECT_INT_MIN }; cairo_scaled_glyph_t *glyph_cache[64]; + cairo_bool_t overlap = overlap_out ? FALSE : TRUE; + int i; if (unlikely (scaled_font->status)) return scaled_font->status; @@ -1945,6 +1990,11 @@ _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font, right = x + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x); bottom = y + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y); + if (overlap == FALSE) { + overlap = _range_contains_glyph (&min, &max, + left, top, right, bottom); + } + if (left < min.x) min.x = left; if (right > max.x) max.x = right; if (top < min.y) min.y = top; @@ -1965,22 +2015,62 @@ _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font, extents->width = extents->height = 0; } + if (overlap_out != NULL) + *overlap_out = overlap; + return CAIRO_STATUS_SUCCESS; } +void +_cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t *scaled_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_rectangle_int_t *extents) +{ + double x0 = HUGE_VAL, x1 = -HUGE_VAL; + double y0 = HUGE_VAL, y1 = -HUGE_VAL; + int i; + + for (i = 0; i < num_glyphs; i++) { + double g; + + g = glyphs[i].x; + if (g < x0) x0 = g; + if (g > x1) x1 = g; + + g = glyphs[i].y; + if (g < y0) y0 = g; + if (g > y1) y1 = g; + } + + if (x0 <= x1 && y0 <= y1) { + extents->x = floor (x0 - scaled_font->extents.max_x_advance); + extents->width = ceil (x1 + scaled_font->extents.max_x_advance); + extents->width -= extents->x; + + extents->y = floor (y0 - scaled_font->extents.ascent); + extents->height = ceil (y1 + scaled_font->extents.descent); + extents->height -= extents->y; + } else { + extents->x = extents->y = 0; + extents->width = extents->height = 0; + } +} + cairo_status_t -_cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, - cairo_operator_t op, +_cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, + cairo_operator_t op, const cairo_pattern_t *pattern, - cairo_surface_t *surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs) + cairo_surface_t *surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_region_t *clip_region) { cairo_status_t status; cairo_surface_t *mask = NULL; @@ -2008,7 +2098,9 @@ _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, source_x, source_y, dest_x, dest_y, width, height, - glyphs, num_glyphs, &remaining_glyphs); + glyphs, num_glyphs, + clip_region, + &remaining_glyphs); glyphs += num_glyphs - remaining_glyphs; num_glyphs = remaining_glyphs; if (remaining_glyphs == 0) @@ -2025,7 +2117,6 @@ _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, for (i = 0; i < num_glyphs; i++) { int x, y; - cairo_surface_pattern_t glyph_pattern; cairo_image_surface_t *glyph_surface; cairo_scaled_glyph_t *scaled_glyph; @@ -2043,8 +2134,7 @@ _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, * glyph. Later we'll deal with different formats. */ if (mask == NULL) { mask_format = glyph_surface->format; - mask = cairo_image_surface_create (mask_format, - width, height); + mask = cairo_image_surface_create (mask_format, width, height); status = mask->status; if (unlikely (status)) goto CLEANUP_MASK; @@ -2071,24 +2161,26 @@ _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, break; } - new_mask = cairo_image_surface_create (mask_format, - width, height); - if (new_mask->status) { - status = new_mask->status; + new_mask = cairo_image_surface_create (mask_format, width, height); + status = new_mask->status; + if (unlikely (status)) { cairo_surface_destroy (new_mask); goto CLEANUP_MASK; } _cairo_pattern_init_for_surface (&mask_pattern, mask); - - status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, + /* Note that we only upgrade masks, i.e. A1 -> A8 -> ARGB32, so there is + * never any component alpha here. + */ + status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, &white_pattern.base, &mask_pattern.base, new_mask, 0, 0, 0, 0, 0, 0, - width, height); + width, height, + NULL); _cairo_pattern_fini (&mask_pattern.base); @@ -2101,40 +2193,50 @@ _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, mask = new_mask; } - /* round glyph locations to the nearest pixel */ - /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ - x = _cairo_lround (glyphs[i].x - glyph_surface->base.device_transform.x0); - y = _cairo_lround (glyphs[i].y - glyph_surface->base.device_transform.y0); + if (glyph_surface->width && glyph_surface->height) { + cairo_surface_pattern_t glyph_pattern; - _cairo_pattern_init_for_surface (&glyph_pattern, &glyph_surface->base); + /* round glyph locations to the nearest pixel */ + /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ + x = _cairo_lround (glyphs[i].x - + glyph_surface->base.device_transform.x0); + y = _cairo_lround (glyphs[i].y - + glyph_surface->base.device_transform.y0); - status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, - &white_pattern.base, - &glyph_pattern.base, - mask, - 0, 0, - 0, 0, - x - dest_x, y - dest_y, - glyph_surface->width, - glyph_surface->height); + _cairo_pattern_init_for_surface (&glyph_pattern, + &glyph_surface->base); + if (mask_format == CAIRO_FORMAT_ARGB32) + glyph_pattern.base.has_component_alpha = TRUE; - _cairo_pattern_fini (&glyph_pattern.base); + status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, + &white_pattern.base, + &glyph_pattern.base, + mask, + 0, 0, + 0, 0, + x - dest_x, y - dest_y, + glyph_surface->width, + glyph_surface->height, + NULL); - if (unlikely (status)) - goto CLEANUP_MASK; + _cairo_pattern_fini (&glyph_pattern.base); + + if (unlikely (status)) + goto CLEANUP_MASK; + } } - if (mask_format == CAIRO_FORMAT_ARGB32) - pixman_image_set_component_alpha (((cairo_image_surface_t*) mask)-> - pixman_image, TRUE); _cairo_pattern_init_for_surface (&mask_pattern, mask); + if (mask_format == CAIRO_FORMAT_ARGB32) + mask_pattern.base.has_component_alpha = TRUE; status = _cairo_surface_composite (op, pattern, &mask_pattern.base, surface, source_x, source_y, 0, 0, dest_x, dest_y, - width, height); + width, height, + clip_region); _cairo_pattern_fini (&mask_pattern.base); @@ -2148,67 +2250,15 @@ CLEANUP_MASK: return _cairo_scaled_font_set_error (scaled_font, status); } -typedef struct _cairo_scaled_glyph_path_closure { - cairo_point_t offset; - cairo_path_fixed_t *path; -} cairo_scaled_glyph_path_closure_t; - -static cairo_status_t -_scaled_glyph_path_move_to (void *abstract_closure, - const cairo_point_t *point) -{ - cairo_scaled_glyph_path_closure_t *closure = abstract_closure; - - return _cairo_path_fixed_move_to (closure->path, - point->x + closure->offset.x, - point->y + closure->offset.y); -} - -static cairo_status_t -_scaled_glyph_path_line_to (void *abstract_closure, - const cairo_point_t *point) -{ - cairo_scaled_glyph_path_closure_t *closure = abstract_closure; - - return _cairo_path_fixed_line_to (closure->path, - point->x + closure->offset.x, - point->y + closure->offset.y); -} - -static cairo_status_t -_scaled_glyph_path_curve_to (void *abstract_closure, - const cairo_point_t *p0, - const cairo_point_t *p1, - const cairo_point_t *p2) -{ - cairo_scaled_glyph_path_closure_t *closure = abstract_closure; - - return _cairo_path_fixed_curve_to (closure->path, - p0->x + closure->offset.x, - p0->y + closure->offset.y, - p1->x + closure->offset.x, - p1->y + closure->offset.y, - p2->x + closure->offset.x, - p2->y + closure->offset.y); -} - -static cairo_status_t -_scaled_glyph_path_close_path (void *abstract_closure) -{ - cairo_scaled_glyph_path_closure_t *closure = abstract_closure; - - return _cairo_path_fixed_close_path (closure->path); -} - /* Add a single-device-unit rectangle to a path. */ static cairo_status_t -_add_unit_rectangle_to_path (cairo_path_fixed_t *path, int x, int y) +_add_unit_rectangle_to_path (cairo_path_fixed_t *path, + cairo_fixed_t x, + cairo_fixed_t y) { cairo_status_t status; - status = _cairo_path_fixed_move_to (path, - _cairo_fixed_from_int (x), - _cairo_fixed_from_int (y)); + status = _cairo_path_fixed_move_to (path, x, y); if (unlikely (status)) return status; @@ -2230,11 +2280,7 @@ _add_unit_rectangle_to_path (cairo_path_fixed_t *path, int x, int y) if (unlikely (status)) return status; - status = _cairo_path_fixed_close_path (path); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; + return _cairo_path_fixed_close_path (path); } /** @@ -2256,12 +2302,15 @@ _add_unit_rectangle_to_path (cairo_path_fixed_t *path, int x, int y) **/ static cairo_status_t _trace_mask_to_path (cairo_image_surface_t *mask, - cairo_path_fixed_t *path) + cairo_path_fixed_t *path, + double tx, double ty) { const uint8_t *row; int rows, cols, bytes_per_row; int x, y, bit; double xoff, yoff; + cairo_fixed_t x0, y0; + cairo_fixed_t px, py; cairo_status_t status; mask = _cairo_image_surface_coerce (mask, CAIRO_FORMAT_A1); @@ -2270,12 +2319,15 @@ _trace_mask_to_path (cairo_image_surface_t *mask, return status; cairo_surface_get_device_offset (&mask->base, &xoff, &yoff); + x0 = _cairo_fixed_from_double (tx - xoff); + y0 = _cairo_fixed_from_double (ty - yoff); bytes_per_row = (mask->width + 7) / 8; row = mask->data; for (y = 0, rows = mask->height; rows--; row += mask->stride, y++) { const uint8_t *byte_ptr = row; x = 0; + py = _cairo_fixed_from_int (y); for (cols = bytes_per_row; cols--; ) { uint8_t byte = *byte_ptr++; if (byte == 0) { @@ -2286,8 +2338,10 @@ _trace_mask_to_path (cairo_image_surface_t *mask, byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (byte); for (bit = 1 << 7; bit && x < mask->width; bit >>= 1, x++) { if (byte & bit) { + px = _cairo_fixed_from_int (x); status = _add_unit_rectangle_to_path (path, - x - xoff, y - yoff); + px + x0, + py + y0); if (unlikely (status)) goto BAIL; } @@ -2309,14 +2363,11 @@ _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, { cairo_status_t status; int i; - cairo_scaled_glyph_path_closure_t closure; - cairo_path_fixed_t *glyph_path; status = scaled_font->status; if (unlikely (status)) return status; - closure.path = path; _cairo_scaled_font_freeze_cache (scaled_font); for (i = 0; i < num_glyphs; i++) { cairo_scaled_glyph_t *scaled_glyph; @@ -2325,14 +2376,16 @@ _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_PATH, &scaled_glyph); - if (status == CAIRO_STATUS_SUCCESS) - glyph_path = scaled_glyph->path; - else if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto BAIL; + if (status == CAIRO_STATUS_SUCCESS) { + status = _cairo_path_fixed_append (path, + scaled_glyph->path, CAIRO_DIRECTION_FORWARD, + _cairo_fixed_from_double (glyphs[i].x), + _cairo_fixed_from_double (glyphs[i].y)); - /* If the font is incapable of providing a path, then we'll - * have to trace our own from a surface. */ - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + /* If the font is incapable of providing a path, then we'll + * have to trace our own from a surface. + */ status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_SURFACE, @@ -2340,32 +2393,10 @@ _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, if (unlikely (status)) goto BAIL; - glyph_path = _cairo_path_fixed_create (); - if (unlikely (glyph_path == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - - status = _trace_mask_to_path (scaled_glyph->surface, glyph_path); - if (unlikely (status)) { - _cairo_path_fixed_destroy (glyph_path); - goto BAIL; - } + status = _trace_mask_to_path (scaled_glyph->surface, path, + glyphs[i].x, glyphs[i].y); } - closure.offset.x = _cairo_fixed_from_double (glyphs[i].x); - closure.offset.y = _cairo_fixed_from_double (glyphs[i].y); - - status = _cairo_path_fixed_interpret (glyph_path, - CAIRO_DIRECTION_FORWARD, - _scaled_glyph_path_move_to, - _scaled_glyph_path_line_to, - _scaled_glyph_path_curve_to, - _scaled_glyph_path_close_path, - &closure); - if (glyph_path != scaled_glyph->path) - _cairo_path_fixed_destroy (glyph_path); - if (unlikely (status)) goto BAIL; } @@ -2485,13 +2516,13 @@ _cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph, } void -_cairo_scaled_glyph_set_meta_surface (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font, - cairo_surface_t *meta_surface) +_cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_surface_t *recording_surface) { - if (scaled_glyph->meta_surface != NULL) - cairo_surface_destroy (meta_surface); - scaled_glyph->meta_surface = meta_surface; + if (scaled_glyph->recording_surface != NULL) + cairo_surface_destroy (recording_surface); + scaled_glyph->recording_surface = recording_surface; } static cairo_bool_t @@ -2672,10 +2703,10 @@ _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, need_info |= CAIRO_SCALED_GLYPH_INFO_PATH; } - if ((info & CAIRO_SCALED_GLYPH_INFO_META_SURFACE) != 0 && - scaled_glyph->meta_surface == NULL) + if ((info & CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE) != 0 && + scaled_glyph->recording_surface == NULL) { - need_info |= CAIRO_SCALED_GLYPH_INFO_META_SURFACE; + need_info |= CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE; } if (need_info) { @@ -2687,7 +2718,7 @@ _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, /* Don't trust the scaled_glyph_init() return value, the font * backend may not even know about some of the info. For example, - * no backend other than the user-fonts knows about meta-surface + * no backend other than the user-fonts knows about recording-surface * glyph info. */ if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0 && @@ -2704,8 +2735,8 @@ _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, goto CLEANUP; } - if ((info & CAIRO_SCALED_GLYPH_INFO_META_SURFACE) != 0 && - scaled_glyph->meta_surface == NULL) + if ((info & CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE) != 0 && + scaled_glyph->recording_surface == NULL) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto CLEANUP; @@ -2737,8 +2768,7 @@ _cairo_scaled_font_get_max_scale (cairo_scaled_font_t *scaled_font) * @scaled_font: a #cairo_scaled_font_t * * Gets the font face that this scaled font uses. This is the - * font face passed to cairo_scaled_font_create() if that font face - * was not of type %CAIRO_FONT_TYPE_TOY. + * font face passed to cairo_scaled_font_create(). * * Return value: The #cairo_font_face_t with which @scaled_font was * created. diff --git a/gfx/cairo/cairo/src/cairo-script-surface.c b/gfx/cairo/cairo/src/cairo-script-surface.c new file mode 100644 index 000000000000..ec06364b3388 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-script-surface.c @@ -0,0 +1,3448 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2008 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Chris Wilson. + * + * Contributor(s): + * Chris Wilson + */ + +/* The script surface is one that records all operations performed on + * it in the form of a procedural script, similar in fashion to + * PostScript but using Cairo's imaging model. In essence, this is + * equivalent to the recording-surface, but as there is no impedance mismatch + * between Cairo and CairoScript, we can generate output immediately + * without having to copy and hold the data in memory. + */ + +#include "cairoint.h" + +#include "cairo-script.h" + +#include "cairo-analysis-surface-private.h" +#include "cairo-ft-private.h" +#include "cairo-list-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-output-stream-private.h" +#include "cairo-scaled-font-private.h" +#include "cairo-surface-clipper-private.h" +#include "cairo-surface-wrapper-private.h" + +#include + +#define _cairo_output_stream_puts(S, STR) \ + _cairo_output_stream_write ((S), (STR), strlen (STR)) + +#define static cairo_warn static + +typedef struct _cairo_script_surface cairo_script_surface_t; +typedef struct _cairo_script_implicit_context cairo_script_implicit_context_t; +typedef struct _cairo_script_surface_font_private cairo_script_surface_font_private_t; + +typedef struct _operand { + enum { + SURFACE, + DEFERRED, + } type; + cairo_list_t link; +} operand_t; + + +struct deferred_finish { + cairo_list_t link; + operand_t operand; +}; + +struct _cairo_script_context { + cairo_status_t status; + + int ref; + int active; + + cairo_output_stream_t *stream; + cairo_script_mode_t mode; + + struct _bitmap { + unsigned long min; + unsigned long count; + unsigned int map[64]; + struct _bitmap *next; + } surface_id, font_id; + + cairo_list_t operands; + cairo_list_t deferred; + + cairo_list_t fonts; + cairo_list_t defines; +}; + +struct _cairo_script_surface_font_private { + cairo_script_context_t *ctx; + cairo_bool_t has_sfnt; + unsigned long id; + unsigned long subset_glyph_index; + cairo_list_t link; + cairo_scaled_font_t *parent; +}; + +struct _cairo_script_implicit_context { + cairo_operator_t current_operator; + cairo_fill_rule_t current_fill_rule; + double current_tolerance; + cairo_antialias_t current_antialias; + cairo_stroke_style_t current_style; + cairo_pattern_union_t current_source; + cairo_matrix_t current_ctm; + cairo_matrix_t current_stroke_matrix; + cairo_matrix_t current_font_matrix; + cairo_font_options_t current_font_options; + cairo_scaled_font_t *current_scaled_font; + cairo_path_fixed_t current_path; + cairo_bool_t has_clip; +}; + +struct _cairo_script_surface { + cairo_surface_t base; + + cairo_surface_wrapper_t wrapper; + + cairo_script_context_t *ctx; + cairo_surface_clipper_t clipper; + + operand_t operand; + cairo_bool_t emitted; + cairo_bool_t defined; + cairo_bool_t is_clear; + cairo_bool_t active; + + double width, height; + + /* implicit flattened context */ + cairo_script_implicit_context_t cr; +}; + +static const cairo_surface_backend_t _cairo_script_surface_backend; + +static cairo_script_surface_t * +_cairo_script_surface_create_internal (cairo_script_context_t *ctx, + cairo_content_t content, + double width, + double height, + cairo_surface_t *passthrough); + +static cairo_status_t +_context_destroy (cairo_script_context_t *ctx); + +static void +_cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); + +static void +_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr); + +static void +_cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr); + +static void +_bitmap_release_id (struct _bitmap *b, unsigned long token) +{ + struct _bitmap **prev = NULL; + + do { + if (token < b->min + sizeof (b->map) * CHAR_BIT) { + unsigned int bit, elem; + + token -= b->min; + elem = token / (sizeof (b->map[0]) * CHAR_BIT); + bit = token % (sizeof (b->map[0]) * CHAR_BIT); + b->map[elem] &= ~(1 << bit); + if (! --b->count && prev) { + *prev = b->next; + free (b); + } + return; + } + prev = &b->next; + b = b->next; + } while (b != NULL); +} + +static cairo_status_t +_bitmap_next_id (struct _bitmap *b, + unsigned long *id) +{ + struct _bitmap *bb, **prev = NULL; + unsigned long min = 0; + + do { + if (b->min != min) + break; + + if (b->count < sizeof (b->map) * CHAR_BIT) { + unsigned int n, m, bit; + for (n = 0; n < ARRAY_LENGTH (b->map); n++) { + if (b->map[n] == (unsigned int) -1) + continue; + + for (m=0, bit=1; mmap[0])*CHAR_BIT; m++, bit<<=1) { + if ((b->map[n] & bit) == 0) { + b->map[n] |= bit; + b->count++; + *id = n * sizeof (b->map[0])*CHAR_BIT + m + b->min; + return CAIRO_STATUS_SUCCESS; + } + } + } + } + min += sizeof (b->map) * CHAR_BIT; + + prev = &b->next; + b = b->next; + } while (b != NULL); + + bb = malloc (sizeof (struct _bitmap)); + if (unlikely (bb == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + *prev = bb; + bb->next = b; + bb->min = min; + bb->count = 1; + bb->map[0] = 0x1; + memset (bb->map + 1, 0, sizeof (bb->map) - sizeof (bb->map[0])); + *id = min; + + return CAIRO_STATUS_SUCCESS; +} + +static const char * +_direction_to_string (cairo_bool_t backward) +{ + static const char *names[] = { + "FORWARD", + "BACKWARD" + }; + assert (backward < ARRAY_LENGTH (names)); + return names[backward]; +} + +static const char * +_operator_to_string (cairo_operator_t op) +{ + static const char *names[] = { + "CLEAR", /* CAIRO_OPERATOR_CLEAR */ + + "SOURCE", /* CAIRO_OPERATOR_SOURCE */ + "OVER", /* CAIRO_OPERATOR_OVER */ + "IN", /* CAIRO_OPERATOR_IN */ + "OUT", /* CAIRO_OPERATOR_OUT */ + "ATOP", /* CAIRO_OPERATOR_ATOP */ + + "DEST", /* CAIRO_OPERATOR_DEST */ + "DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */ + "DEST_IN", /* CAIRO_OPERATOR_DEST_IN */ + "DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */ + "DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */ + + "XOR", /* CAIRO_OPERATOR_XOR */ + "ADD", /* CAIRO_OPERATOR_ADD */ + "SATURATE", /* CAIRO_OPERATOR_SATURATE */ + + "MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */ + "SCREEN", /* CAIRO_OPERATOR_SCREEN */ + "OVERLAY", /* CAIRO_OPERATOR_OVERLAY */ + "DARKEN", /* CAIRO_OPERATOR_DARKEN */ + "LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */ + "DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */ + "BURN", /* CAIRO_OPERATOR_COLOR_BURN */ + "HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */ + "SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */ + "DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */ + "EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */ + "HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */ + "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */ + "HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */ + "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */ + }; + assert (op < ARRAY_LENGTH (names)); + return names[op]; +} + +static const char * +_extend_to_string (cairo_extend_t extend) +{ + static const char *names[] = { + "EXTEND_NONE", /* CAIRO_EXTEND_NONE */ + "EXTEND_REPEAT", /* CAIRO_EXTEND_REPEAT */ + "EXTEND_REFLECT", /* CAIRO_EXTEND_REFLECT */ + "EXTEND_PAD" /* CAIRO_EXTEND_PAD */ + }; + assert (extend < ARRAY_LENGTH (names)); + return names[extend]; +} + +static const char * +_filter_to_string (cairo_filter_t filter) +{ + static const char *names[] = { + "FILTER_FAST", /* CAIRO_FILTER_FAST */ + "FILTER_GOOD", /* CAIRO_FILTER_GOOD */ + "FILTER_BEST", /* CAIRO_FILTER_BEST */ + "FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */ + "FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */ + "FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */ + }; + assert (filter < ARRAY_LENGTH (names)); + return names[filter]; +} + +static const char * +_fill_rule_to_string (cairo_fill_rule_t rule) +{ + static const char *names[] = { + "WINDING", /* CAIRO_FILL_RULE_WINDING */ + "EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */ + }; + assert (rule < ARRAY_LENGTH (names)); + return names[rule]; +} + +static const char * +_antialias_to_string (cairo_antialias_t antialias) +{ + static const char *names[] = { + "ANTIALIAS_DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */ + "ANTIALIAS_NONE", /* CAIRO_ANTIALIAS_NONE */ + "ANTIALIAS_GRAY", /* CAIRO_ANTIALIAS_GRAY */ + "ANTIALIAS_SUBPIXEL" /* CAIRO_ANTIALIAS_SUBPIXEL */ + }; + assert (antialias < ARRAY_LENGTH (names)); + return names[antialias]; +} + +static const char * +_line_cap_to_string (cairo_line_cap_t line_cap) +{ + static const char *names[] = { + "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */ + "LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */ + "LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */ + }; + assert (line_cap < ARRAY_LENGTH (names)); + return names[line_cap]; +} + +static const char * +_line_join_to_string (cairo_line_join_t line_join) +{ + static const char *names[] = { + "LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */ + "LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */ + "LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */ + }; + assert (line_join < ARRAY_LENGTH (names)); + return names[line_join]; +} + +static cairo_bool_t +target_is_active (cairo_script_surface_t *surface) +{ + return cairo_list_is_first (&surface->operand.link, + &surface->ctx->operands); +} + +static void +target_push (cairo_script_surface_t *surface) +{ + cairo_list_move (&surface->operand.link, &surface->ctx->operands); +} + +static int +target_depth (cairo_script_surface_t *surface) +{ + cairo_list_t *link; + int depth = 0; + + cairo_list_foreach (link, &surface->ctx->operands) { + if (link == &surface->operand.link) + break; + depth++; + } + + return depth; +} + +static void +_get_target (cairo_script_surface_t *surface) +{ + cairo_script_context_t *ctx = surface->ctx; + + if (surface->defined) { + _cairo_output_stream_printf (ctx->stream, "s%u ", + surface->base.unique_id); + } else { + assert (! cairo_list_is_empty (&surface->operand.link)); + if (! target_is_active (surface)) { + int depth = target_depth (surface); + if (ctx->active) { + _cairo_output_stream_printf (ctx->stream, "%d index ", depth); + _cairo_output_stream_puts (ctx->stream, "/target get exch pop "); + } else { + if (depth == 1) { + _cairo_output_stream_puts (surface->ctx->stream, + "exch\n"); + } else { + _cairo_output_stream_printf (surface->ctx->stream, + "%d -1 roll\n", + depth); + } + _cairo_output_stream_puts (ctx->stream, "/target get "); + target_push (surface); + } + } else { + _cairo_output_stream_puts (ctx->stream, "/target get "); + } + } +} + +static const char * +_content_to_string (cairo_content_t content) +{ + switch (content) { + case CAIRO_CONTENT_ALPHA: return "ALPHA"; + case CAIRO_CONTENT_COLOR: return "COLOR"; + default: + case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA"; + } +} + +static cairo_status_t +_emit_surface (cairo_script_surface_t *surface) +{ + _cairo_output_stream_printf (surface->ctx->stream, + "<< /content //%s", + _content_to_string (surface->base.content)); + if (surface->width != -1 && surface->height != -1) { + _cairo_output_stream_printf (surface->ctx->stream, + " /width %f /height %f", + surface->width, + surface->height); + } + + if (surface->base.x_fallback_resolution != + CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT || + surface->base.y_fallback_resolution != + CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT) + { + _cairo_output_stream_printf (surface->ctx->stream, + " /fallback-resolution [%f %f]", + surface->base.x_fallback_resolution, + surface->base.y_fallback_resolution); + } + + if (surface->base.device_transform.x0 != 0. || + surface->base.device_transform.y0 != 0.) + { + /* XXX device offset is encoded into the pattern matrices etc. */ + if (0) { + _cairo_output_stream_printf (surface->ctx->stream, + " /device-offset [%f %f]", + surface->base.device_transform.x0, + surface->base.device_transform.y0); + } + } + + _cairo_output_stream_puts (surface->ctx->stream, " >> surface context\n"); + surface->emitted = TRUE; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_context (cairo_script_surface_t *surface) +{ + cairo_script_context_t *ctx = surface->ctx; + + if (target_is_active (surface)) + return CAIRO_STATUS_SUCCESS; + + while (! cairo_list_is_empty (&ctx->operands)) { + operand_t *op; + cairo_script_surface_t *old; + + op = cairo_list_first_entry (&ctx->operands, + operand_t, + link); + if (op->type == DEFERRED) + break; + + old = cairo_container_of (op, cairo_script_surface_t, operand); + if (old == surface) + break; + if (old->active) + break; + + if (! old->defined) { + assert (old->emitted); + _cairo_output_stream_printf (ctx->stream, + "/target get /s%u exch def pop\n", + old->base.unique_id); + old->defined = TRUE; + } else { + _cairo_output_stream_puts (ctx->stream, "pop\n"); + } + + cairo_list_del (&old->operand.link); + } + + if (target_is_active (surface)) + return CAIRO_STATUS_SUCCESS; + + if (! surface->emitted) { + cairo_status_t status; + + status = _emit_surface (surface); + if (unlikely (status)) + return status; + } else if (cairo_list_is_empty (&surface->operand.link)) { + assert (surface->defined); + _cairo_output_stream_printf (ctx->stream, + "s%u context\n", + surface->base.unique_id); + _cairo_script_implicit_context_reset (&surface->cr); + _cairo_surface_clipper_reset (&surface->clipper); + } else { + int depth = target_depth (surface); + if (depth == 1) { + _cairo_output_stream_puts (ctx->stream, "exch\n"); + } else { + _cairo_output_stream_printf (ctx->stream, + "%d -1 roll\n", + depth); + } + } + target_push (surface); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_operator (cairo_script_surface_t *surface, + cairo_operator_t op) +{ + assert (target_is_active (surface)); + + if (surface->cr.current_operator == op) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_operator = op; + + _cairo_output_stream_printf (surface->ctx->stream, + "//%s set-operator\n", + _operator_to_string (op)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_fill_rule (cairo_script_surface_t *surface, + cairo_fill_rule_t fill_rule) +{ + assert (target_is_active (surface)); + + if (surface->cr.current_fill_rule == fill_rule) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_fill_rule = fill_rule; + + _cairo_output_stream_printf (surface->ctx->stream, + "//%s set-fill-rule\n", + _fill_rule_to_string (fill_rule)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_tolerance (cairo_script_surface_t *surface, + double tolerance, + cairo_bool_t force) +{ + assert (target_is_active (surface)); + + if ((! force || + fabs (tolerance - CAIRO_GSTATE_TOLERANCE_DEFAULT) < 1e-5) && + surface->cr.current_tolerance == tolerance) + { + return CAIRO_STATUS_SUCCESS; + } + + surface->cr.current_tolerance = tolerance; + + _cairo_output_stream_printf (surface->ctx->stream, + "%f set-tolerance\n", + tolerance); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_antialias (cairo_script_surface_t *surface, + cairo_antialias_t antialias) +{ + assert (target_is_active (surface)); + + if (surface->cr.current_antialias == antialias) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_antialias = antialias; + + _cairo_output_stream_printf (surface->ctx->stream, + "//%s set-antialias\n", + _antialias_to_string (antialias)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_line_width (cairo_script_surface_t *surface, + double line_width, + cairo_bool_t force) +{ + assert (target_is_active (surface)); + + if ((! force || + fabs (line_width - CAIRO_GSTATE_LINE_WIDTH_DEFAULT) < 1e-5) && + surface->cr.current_style.line_width == line_width) + { + return CAIRO_STATUS_SUCCESS; + } + + surface->cr.current_style.line_width = line_width; + + _cairo_output_stream_printf (surface->ctx->stream, + "%f set-line-width\n", + line_width); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_line_cap (cairo_script_surface_t *surface, + cairo_line_cap_t line_cap) +{ + assert (target_is_active (surface)); + + if (surface->cr.current_style.line_cap == line_cap) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_style.line_cap = line_cap; + + _cairo_output_stream_printf (surface->ctx->stream, + "//%s set-line-cap\n", + _line_cap_to_string (line_cap)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_line_join (cairo_script_surface_t *surface, + cairo_line_join_t line_join) +{ + assert (target_is_active (surface)); + + if (surface->cr.current_style.line_join == line_join) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_style.line_join = line_join; + + _cairo_output_stream_printf (surface->ctx->stream, + "//%s set-line-join\n", + _line_join_to_string (line_join)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_miter_limit (cairo_script_surface_t *surface, + double miter_limit, + cairo_bool_t force) +{ + assert (target_is_active (surface)); + + if ((! force || + fabs (miter_limit - CAIRO_GSTATE_MITER_LIMIT_DEFAULT) < 1e-5) && + surface->cr.current_style.miter_limit == miter_limit) + { + return CAIRO_STATUS_SUCCESS; + } + + surface->cr.current_style.miter_limit = miter_limit; + + _cairo_output_stream_printf (surface->ctx->stream, + "%f set-miter-limit\n", + miter_limit); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_dashes_equal (const double *a, const double *b, int num_dashes) +{ + while (num_dashes--) { + if (fabs (*a - *b) > 1e-5) + return FALSE; + a++, b++; + } + + return TRUE; +} + +static cairo_status_t +_emit_dash (cairo_script_surface_t *surface, + const double *dash, + unsigned int num_dashes, + double offset, + cairo_bool_t force) +{ + unsigned int n; + + assert (target_is_active (surface)); + + if (force && + num_dashes == 0 && + surface->cr.current_style.num_dashes == 0) + { + return CAIRO_STATUS_SUCCESS; + } + + if (! force && + (surface->cr.current_style.num_dashes == num_dashes && + (num_dashes == 0 || + (fabs (surface->cr.current_style.dash_offset - offset) < 1e-5 && + _dashes_equal (surface->cr.current_style.dash, dash, num_dashes))))) + { + return CAIRO_STATUS_SUCCESS; + } + + + if (num_dashes) { + surface->cr.current_style.dash = _cairo_realloc_ab + (surface->cr.current_style.dash, num_dashes, sizeof (double)); + if (unlikely (surface->cr.current_style.dash == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (surface->cr.current_style.dash, dash, + sizeof (double) * num_dashes); + } else { + if (surface->cr.current_style.dash != NULL) { + free (surface->cr.current_style.dash); + surface->cr.current_style.dash = NULL; + } + } + + surface->cr.current_style.num_dashes = num_dashes; + surface->cr.current_style.dash_offset = offset; + + _cairo_output_stream_puts (surface->ctx->stream, "["); + for (n = 0; n < num_dashes; n++) { + _cairo_output_stream_printf (surface->ctx->stream, "%f", dash[n]); + if (n < num_dashes-1) + _cairo_output_stream_puts (surface->ctx->stream, " "); + } + _cairo_output_stream_printf (surface->ctx->stream, + "] %f set-dash\n", + offset); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_stroke_style (cairo_script_surface_t *surface, + const cairo_stroke_style_t *style, + cairo_bool_t force) +{ + cairo_status_t status; + + assert (target_is_active (surface)); + + status = _emit_line_width (surface, style->line_width, force); + if (unlikely (status)) + return status; + + status = _emit_line_cap (surface, style->line_cap); + if (unlikely (status)) + return status; + + status = _emit_line_join (surface, style->line_join); + if (unlikely (status)) + return status; + + status = _emit_miter_limit (surface, style->miter_limit, force); + if (unlikely (status)) + return status; + + status = _emit_dash (surface, + style->dash, style->num_dashes, style->dash_offset, + force); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +static const char * +_format_to_string (cairo_format_t format) +{ + static const char *names[] = { + "ARGB32", /* CAIRO_FORMAT_ARGB32 */ + "RGB24", /* CAIRO_FORMAT_RGB24 */ + "A8", /* CAIRO_FORMAT_A8 */ + "A1" /* CAIRO_FORMAT_A1 */ + }; + assert (format < ARRAY_LENGTH (names)); + return names[format]; +} + +static cairo_status_t +_emit_solid_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; + + if (solid->content & CAIRO_CONTENT_ALPHA && + ! CAIRO_COLOR_IS_OPAQUE (&solid->color)) + { + if (! (solid->content & CAIRO_CONTENT_COLOR) || + ! (surface->base.content & CAIRO_CONTENT_COLOR) || + ((solid->color.red_short == 0 || solid->color.red_short == 0xffff) && + (solid->color.green_short == 0 || solid->color.green_short == 0xffff) && + (solid->color.blue_short == 0 || solid->color.blue_short == 0xffff) )) + { + _cairo_output_stream_printf (surface->ctx->stream, + "%f a", + solid->color.alpha); + } + else + { + _cairo_output_stream_printf (surface->ctx->stream, + "%f %f %f %f rgba", + solid->color.red, + solid->color.green, + solid->color.blue, + solid->color.alpha); + } + } + else + { + if (solid->color.red_short == solid->color.green_short && + solid->color.red_short == solid->color.blue_short) + { + _cairo_output_stream_printf (surface->ctx->stream, + "%f g", + solid->color.red); + } + else + { + _cairo_output_stream_printf (surface->ctx->stream, + "%f %f %f rgb", + solid->color.red, + solid->color.green, + solid->color.blue); + } + } + + return CAIRO_STATUS_SUCCESS; +} + + +static cairo_status_t +_emit_gradient_color_stops (cairo_gradient_pattern_t *gradient, + cairo_output_stream_t *output) +{ + unsigned int n; + + for (n = 0; n < gradient->n_stops; n++) { + _cairo_output_stream_printf (output, + "\n %f %f %f %f %f add-color-stop", + gradient->stops[n].offset, + gradient->stops[n].color.red, + gradient->stops[n].color.green, + gradient->stops[n].color.blue, + gradient->stops[n].color.alpha); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_linear_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_linear_pattern_t *linear; + + linear = (cairo_linear_pattern_t *) pattern; + + _cairo_output_stream_printf (surface->ctx->stream, + "%f %f %f %f linear", + _cairo_fixed_to_double (linear->p1.x), + _cairo_fixed_to_double (linear->p1.y), + _cairo_fixed_to_double (linear->p2.x), + _cairo_fixed_to_double (linear->p2.y)); + return _emit_gradient_color_stops (&linear->base, surface->ctx->stream); +} + +static cairo_status_t +_emit_radial_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_radial_pattern_t *radial; + + radial = (cairo_radial_pattern_t *) pattern; + + _cairo_output_stream_printf (surface->ctx->stream, + "%f %f %f %f %f %f radial", + _cairo_fixed_to_double (radial->c1.x), + _cairo_fixed_to_double (radial->c1.y), + _cairo_fixed_to_double (radial->r1), + _cairo_fixed_to_double (radial->c2.x), + _cairo_fixed_to_double (radial->c2.y), + _cairo_fixed_to_double (radial->r2)); + return _emit_gradient_color_stops (&radial->base, surface->ctx->stream); +} + +static cairo_status_t +_emit_recording_surface_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_script_implicit_context_t old_cr; + cairo_surface_pattern_t *surface_pattern; + cairo_recording_surface_t *source; + cairo_script_surface_t *similar; + cairo_status_t status; + cairo_box_t bbox; + cairo_rectangle_int_t rect; + + surface_pattern = (cairo_surface_pattern_t *) pattern; + source = (cairo_recording_surface_t *) surface_pattern->surface; + + /* first measure the extents */ + status = _cairo_recording_surface_get_bbox (source, &bbox, NULL); + if (unlikely (status)) + return status; + + /* convert to extents so that it matches the public api */ + _cairo_box_round_to_rectangle (&bbox, &rect); + + similar = _cairo_script_surface_create_internal (surface->ctx, + source->content, + rect.width, + rect.height, + NULL); + if (unlikely (similar->base.status)) + return similar->base.status; + + cairo_surface_set_device_offset (&similar->base, -rect.x, -rect.y); + surface->is_clear = TRUE; + + _get_target (surface); + _cairo_output_stream_printf (surface->ctx->stream, + "%d %d //%s similar dup context\n", + rect.width, rect.height, + _content_to_string (source->content)); + target_push (similar); + similar->emitted = TRUE; + + old_cr = surface->cr; + _cairo_script_implicit_context_init (&surface->cr); + status = _cairo_recording_surface_replay (&source->base, &similar->base); + surface->cr = old_cr; + + if (unlikely (status)) { + cairo_surface_destroy (&similar->base); + return status; + } + + cairo_list_del (&similar->operand.link); + assert (target_is_active (surface)); + + _cairo_output_stream_puts (surface->ctx->stream, "pop pattern"); + cairo_surface_destroy (&similar->base); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_script_surface_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_surface_pattern_t *surface_pattern; + cairo_script_surface_t *source; + + surface_pattern = (cairo_surface_pattern_t *) pattern; + source = (cairo_script_surface_t *) surface_pattern->surface; + + _get_target (source); + _cairo_output_stream_puts (surface->ctx->stream, "pattern"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_write_image_surface (cairo_output_stream_t *output, + const cairo_image_surface_t *image) +{ + int stride, row, width; + uint8_t row_stack[CAIRO_STACK_BUFFER_SIZE]; + uint8_t *rowdata; + uint8_t *data; + + stride = image->stride; + width = image->width; + data = image->data; +#if WORDS_BIGENDIAN + switch (image->format) { + case CAIRO_FORMAT_A1: + for (row = image->height; row--; ) { + _cairo_output_stream_write (output, data, (width+7)/8); + data += stride; + } + break; + case CAIRO_FORMAT_A8: + for (row = image->height; row--; ) { + _cairo_output_stream_write (output, data, width); + data += stride; + } + break; + case CAIRO_FORMAT_RGB24: + for (row = image->height; row--; ) { + int col; + rowdata = data; + for (col = width; col--; ) { + _cairo_output_stream_write (output, rowdata, 3); + rowdata+=4; + } + data += stride; + } + break; + case CAIRO_FORMAT_ARGB32: + for (row = image->height; row--; ) { + _cairo_output_stream_write (output, data, 4*width); + data += stride; + } + break; + default: + ASSERT_NOT_REACHED; + break; + } +#else + if (stride > ARRAY_LENGTH (row_stack)) { + rowdata = malloc (stride); + if (unlikely (rowdata == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else + rowdata = row_stack; + + switch (image->format) { + case CAIRO_FORMAT_A1: + for (row = image->height; row--; ) { + int col; + for (col = 0; col < (width + 7)/8; col++) + rowdata[col] = CAIRO_BITSWAP8 (data[col]); + _cairo_output_stream_write (output, rowdata, (width+7)/8); + data += stride; + } + break; + case CAIRO_FORMAT_A8: + for (row = image->height; row--; ) { + _cairo_output_stream_write (output, data, width); + data += stride; + } + break; + case CAIRO_FORMAT_RGB24: + for (row = image->height; row--; ) { + uint8_t *src = data; + int col; + for (col = 0; col < width; col++) { + rowdata[3*col+2] = *src++; + rowdata[3*col+1] = *src++; + rowdata[3*col+0] = *src++; + src++; + } + _cairo_output_stream_write (output, rowdata, 3*width); + data += stride; + } + break; + case CAIRO_FORMAT_ARGB32: + for (row = image->height; row--; ) { + uint32_t *src = (uint32_t *) data; + uint32_t *dst = (uint32_t *) rowdata; + int col; + for (col = 0; col < width; col++) + dst[col] = bswap_32 (src[col]); + _cairo_output_stream_write (output, rowdata, 4*width); + data += stride; + } + break; + default: + ASSERT_NOT_REACHED; + break; + } + if (rowdata != row_stack) + free (rowdata); +#endif + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_emit_png_surface (cairo_script_surface_t *surface, + cairo_image_surface_t *image) +{ + cairo_output_stream_t *base85_stream; + cairo_status_t status; + const uint8_t *mime_data; + unsigned int mime_data_length; + + cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_PNG, + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_output_stream_printf (surface->ctx->stream, + "<< " + "/width %d " + "/height %d " + "/format //%s " + "/mime-type (image/png) " + "/source <~", + image->width, image->height, + _format_to_string (image->format)); + + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, "~> >> image "); + return CAIRO_STATUS_SUCCESS; +} + +struct def { + cairo_script_context_t *ctx; + cairo_user_data_array_t *user_data; + unsigned int tag; + cairo_list_t link; +}; + +static void +_undef (void *data) +{ + struct def *def = data; + + cairo_list_del (&def->link); + _cairo_output_stream_printf (def->ctx->stream, "/s%u undef\n", def->tag); + free (def); +} + +static cairo_status_t +_emit_image_surface (cairo_script_surface_t *surface, + cairo_image_surface_t *image) +{ + cairo_output_stream_t *base85_stream; + cairo_output_stream_t *zlib_stream; + cairo_status_t status, status2; + const uint8_t *mime_data; + unsigned int mime_data_length; + struct def *tag; + + if (_cairo_user_data_array_get_data (&image->base.user_data, + (cairo_user_data_key_t *) surface->ctx)) + { + _cairo_output_stream_printf (surface->ctx->stream, + "s%u pattern ", + image->base.unique_id); + return CAIRO_STATUS_SUCCESS; + } + + status = _emit_png_surface (surface, image); + if (_cairo_status_is_error (status)) { + return status; + } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + cairo_image_surface_t *clone; + uint32_t len; + + if (image->format == CAIRO_FORMAT_INVALID) { + clone = + _cairo_image_surface_coerce (image, + _cairo_format_from_content (image->base.content)); + } else { + clone = (cairo_image_surface_t *) + cairo_surface_reference (&image->base); + } + + _cairo_output_stream_printf (surface->ctx->stream, + "<< " + "/width %d " + "/height %d " + "/format //%s " + "/source ", + clone->width, clone->height, + _format_to_string (clone->format)); + + switch (clone->format) { + case CAIRO_FORMAT_A1: + len = (clone->width + 7)/8; + break; + case CAIRO_FORMAT_A8: + len = clone->width; + break; + case CAIRO_FORMAT_RGB24: + len = clone->width * 3; + break; + case CAIRO_FORMAT_ARGB32: + len = clone->width * 4; + break; + } + len *= clone->height; + + if (len > 24) { + _cairo_output_stream_puts (surface->ctx->stream, "<|"); + + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + + _cairo_output_stream_write (base85_stream, &len, sizeof (len)); + + zlib_stream = _cairo_deflate_stream_create (base85_stream); + status = _write_image_surface (zlib_stream, clone); + + status2 = _cairo_output_stream_destroy (zlib_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + status2 = _cairo_output_stream_destroy (base85_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + if (unlikely (status)) + return status; + } else { + _cairo_output_stream_puts (surface->ctx->stream, "<~"); + + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + status = _write_image_surface (base85_stream, clone); + status2 = _cairo_output_stream_destroy (base85_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + if (unlikely (status)) + return status; + } + _cairo_output_stream_puts (surface->ctx->stream, "~> >> image "); + + cairo_surface_destroy (&clone->base); + } + + tag = malloc (sizeof (*tag)); + if (unlikely (tag == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + tag->ctx = surface->ctx; + tag->tag = image->base.unique_id; + tag->user_data = &image->base.user_data; + cairo_list_add (&tag->link, &surface->ctx->defines); + status = _cairo_user_data_array_set_data (&image->base.user_data, + (cairo_user_data_key_t *) surface->ctx, + tag, _undef); + if (unlikely (status)) { + free (tag); + return status; + } + + _cairo_output_stream_printf (surface->ctx->stream, + "dup /s%u exch def ", + image->base.unique_id); + + cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JPEG, + &mime_data, &mime_data_length); + if (mime_data != NULL) { + _cairo_output_stream_printf (surface->ctx->stream, + "\n (%s) <~", + CAIRO_MIME_TYPE_JPEG); + + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, "~> set-mime-data\n"); + } + + cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JP2, + &mime_data, &mime_data_length); + if (mime_data != NULL) { + _cairo_output_stream_printf (surface->ctx->stream, + "\n (%s) <~", + CAIRO_MIME_TYPE_JP2); + + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, "~> set-mime-data\n"); + } + + _cairo_output_stream_puts (surface->ctx->stream, "pattern"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_image_surface_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_surface_pattern_t *surface_pattern; + cairo_image_surface_t *image; + cairo_status_t status; + + /* XXX keeping a copy is nasty, but we want to hook into the surface's + * lifetime. Using a snapshot is a convenient method. + */ + surface_pattern = (cairo_surface_pattern_t *) pattern; + image = (cairo_image_surface_t *) + _cairo_surface_snapshot (surface_pattern->surface); + status = _emit_image_surface (surface, image); + cairo_surface_destroy (&image->base); + + return status; +} + +static cairo_status_t +_emit_surface_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_surface_pattern_t *surface_pattern; + cairo_surface_t *source; + + surface_pattern = (cairo_surface_pattern_t *) pattern; + source = surface_pattern->surface; + + switch ((int) source->type) { + case CAIRO_SURFACE_TYPE_RECORDING: + return _emit_recording_surface_pattern (surface, pattern); + case CAIRO_SURFACE_TYPE_SCRIPT: + return _emit_script_surface_pattern (surface, pattern); + default: + return _emit_image_surface_pattern (surface, pattern); + } +} + +static cairo_status_t +_emit_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_status_t status; + cairo_bool_t is_default_extend; + cairo_bool_t need_newline = TRUE; + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + /* solid colors do not need filter/extend/matrix */ + return _emit_solid_pattern (surface, pattern); + + case CAIRO_PATTERN_TYPE_LINEAR: + status = _emit_linear_pattern (surface, pattern); + is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT; + break; + case CAIRO_PATTERN_TYPE_RADIAL: + status = _emit_radial_pattern (surface, pattern); + is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT; + break; + case CAIRO_PATTERN_TYPE_SURFACE: + status = _emit_surface_pattern (surface, pattern); + is_default_extend = pattern->extend == CAIRO_EXTEND_SURFACE_DEFAULT; + break; + + default: + ASSERT_NOT_REACHED; + status = CAIRO_INT_STATUS_UNSUPPORTED; + } + if (unlikely (status)) + return status; + + if (! _cairo_matrix_is_identity (&pattern->matrix)) { + if (need_newline) { + _cairo_output_stream_puts (surface->ctx->stream, "\n "); + need_newline = FALSE; + } + + _cairo_output_stream_printf (surface->ctx->stream, + " [%f %f %f %f %f %f] set-matrix\n ", + pattern->matrix.xx, pattern->matrix.yx, + pattern->matrix.xy, pattern->matrix.yy, + pattern->matrix.x0, pattern->matrix.y0); + } + + /* XXX need to discriminate the user explicitly setting the default */ + if (pattern->filter != CAIRO_FILTER_DEFAULT) { + if (need_newline) { + _cairo_output_stream_puts (surface->ctx->stream, "\n "); + need_newline = FALSE; + } + + _cairo_output_stream_printf (surface->ctx->stream, + " //%s set-filter\n ", + _filter_to_string (pattern->filter)); + } + if (! is_default_extend ){ + if (need_newline) { + _cairo_output_stream_puts (surface->ctx->stream, "\n "); + need_newline = FALSE; + } + + _cairo_output_stream_printf (surface->ctx->stream, + " //%s set-extend\n ", + _extend_to_string (pattern->extend)); + } + + if (need_newline) + _cairo_output_stream_puts (surface->ctx->stream, "\n "); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_identity (cairo_script_surface_t *surface, + cairo_bool_t *matrix_updated) +{ + assert (target_is_active (surface)); + + if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) + return CAIRO_STATUS_SUCCESS; + + _cairo_output_stream_puts (surface->ctx->stream, + "identity set-matrix\n"); + + *matrix_updated = TRUE; + cairo_matrix_init_identity (&surface->cr.current_ctm); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_source (cairo_script_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source) +{ + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + + assert (target_is_active (surface)); + + if (op == CAIRO_OPERATOR_CLEAR) { + /* the source is ignored, so don't change it */ + return CAIRO_STATUS_SUCCESS; + } + + if (_cairo_pattern_equal (&surface->cr.current_source.base, source)) + return CAIRO_STATUS_SUCCESS; + + _cairo_pattern_fini (&surface->cr.current_source.base); + status = _cairo_pattern_init_copy (&surface->cr.current_source.base, + source); + if (unlikely (status)) + return status; + + status = _emit_identity (surface, &matrix_updated); + if (unlikely (status)) + return status; + + status = _emit_pattern (surface, source); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, + " set-source\n"); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_path_move_to (void *closure, + const cairo_point_t *point) +{ + _cairo_output_stream_printf (closure, + " %f %f m", + _cairo_fixed_to_double (point->x), + _cairo_fixed_to_double (point->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_path_line_to (void *closure, + const cairo_point_t *point) +{ + _cairo_output_stream_printf (closure, + " %f %f l", + _cairo_fixed_to_double (point->x), + _cairo_fixed_to_double (point->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_path_curve_to (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2, + const cairo_point_t *p3) +{ + _cairo_output_stream_printf (closure, + " %f %f %f %f %f %f c", + _cairo_fixed_to_double (p1->x), + _cairo_fixed_to_double (p1->y), + _cairo_fixed_to_double (p2->x), + _cairo_fixed_to_double (p2->y), + _cairo_fixed_to_double (p3->x), + _cairo_fixed_to_double (p3->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_path_close (void *closure) +{ + _cairo_output_stream_printf (closure, + " h"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_path (cairo_script_surface_t *surface, + cairo_path_fixed_t *path) +{ + cairo_box_t box; + cairo_status_t status; + + assert (target_is_active (surface)); + assert (_cairo_matrix_is_identity (&surface->cr.current_ctm)); + + if (_cairo_path_fixed_equal (&surface->cr.current_path, path)) + return CAIRO_STATUS_SUCCESS; + + _cairo_path_fixed_fini (&surface->cr.current_path); + + _cairo_output_stream_puts (surface->ctx->stream, "n"); + + if (path == NULL) { + _cairo_path_fixed_init (&surface->cr.current_path); + } else if (_cairo_path_fixed_is_box (path, &box)) { + double x1 = _cairo_fixed_to_double (box.p1.x); + double y1 = _cairo_fixed_to_double (box.p1.y); + double x2 = _cairo_fixed_to_double (box.p2.x); + double y2 = _cairo_fixed_to_double (box.p2.y); + + status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->ctx->stream, + " %f %f %f %f rectangle", + x1, y1, x2 - x1, y2 - y1); + } else { + cairo_status_t status; + + status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _path_move_to, + _path_line_to, + _path_curve_to, + _path_close, + surface->ctx->stream); + if (unlikely (status)) + return status; + } + + _cairo_output_stream_puts (surface->ctx->stream, "\n"); + + return CAIRO_STATUS_SUCCESS; +} +static cairo_bool_t +_scaling_matrix_equal (const cairo_matrix_t *a, + const cairo_matrix_t *b) +{ + return fabs (a->xx - b->xx) < 1e-5 && + fabs (a->xy - b->xy) < 1e-5 && + fabs (a->yx - b->yx) < 1e-5 && + fabs (a->yy - b->yy) < 1e-5; +} + +static cairo_status_t +_emit_scaling_matrix (cairo_script_surface_t *surface, + const cairo_matrix_t *ctm, + cairo_bool_t *matrix_updated) +{ + cairo_bool_t was_identity; + assert (target_is_active (surface)); + + if (_scaling_matrix_equal (&surface->cr.current_ctm, ctm)) + return CAIRO_STATUS_SUCCESS; + + was_identity = _cairo_matrix_is_identity (&surface->cr.current_ctm); + + *matrix_updated = TRUE; + surface->cr.current_ctm = *ctm; + surface->cr.current_ctm.x0 = 0.; + surface->cr.current_ctm.y0 = 0.; + + if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) { + _cairo_output_stream_puts (surface->ctx->stream, + "identity set-matrix\n"); + } else if (was_identity && fabs (ctm->yx) < 1e-5 && fabs (ctm->xy) < 1e-5) { + _cairo_output_stream_printf (surface->ctx->stream, + "%f %f scale\n", + ctm->xx, ctm->yy); + } else { + _cairo_output_stream_printf (surface->ctx->stream, + "[%f %f %f %f 0 0] set-matrix\n", + ctm->xx, ctm->yx, + ctm->xy, ctm->yy); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_font_matrix (cairo_script_surface_t *surface, + const cairo_matrix_t *font_matrix) +{ + assert (target_is_active (surface)); + + if (memcmp (&surface->cr.current_font_matrix, + font_matrix, + sizeof (cairo_matrix_t)) == 0) + { + return CAIRO_STATUS_SUCCESS; + } + + surface->cr.current_font_matrix = *font_matrix; + + if (_cairo_matrix_is_identity (font_matrix)) { + _cairo_output_stream_puts (surface->ctx->stream, + "identity set-font-matrix\n"); + } else { + _cairo_output_stream_printf (surface->ctx->stream, + "[%f %f %f %f %f %f] set-font-matrix\n", + font_matrix->xx, font_matrix->yx, + font_matrix->xy, font_matrix->yy, + font_matrix->x0, font_matrix->y0); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_surface_t * +_cairo_script_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_script_surface_t *surface, *other = abstract_surface; + cairo_surface_t *passthrough = NULL; + cairo_script_context_t *ctx; + cairo_status_t status; + + ctx = other->ctx; + + if (! other->emitted) { + status = _emit_surface (other); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + target_push (other); + } + + if (_cairo_surface_wrapper_is_active (&other->wrapper)) { + passthrough = + _cairo_surface_wrapper_create_similar (&other->wrapper, + content, width, height); + if (unlikely (passthrough->status)) + return passthrough; + } + + surface = _cairo_script_surface_create_internal (ctx, + content, + width, height, + passthrough); + cairo_surface_destroy (passthrough); + + if (unlikely (surface->base.status)) + return &surface->base; + + _get_target (other); + _cairo_output_stream_printf (ctx->stream, + "%u %u //%s similar dup /s%u exch def context\n", + width, height, + _content_to_string (content), + surface->base.unique_id); + surface->emitted = TRUE; + surface->defined = TRUE; + surface->is_clear = TRUE; + target_push (surface); + + return &surface->base; +} + +static cairo_status_t +_context_destroy (cairo_script_context_t *ctx) +{ + cairo_status_t status; + + assert (ctx->ref > 0); + if (--ctx->ref) + return _cairo_output_stream_flush (ctx->stream); + + while (! cairo_list_is_empty (&ctx->fonts)) { + cairo_script_surface_font_private_t *font; + + font = cairo_list_first_entry (&ctx->fonts, + cairo_script_surface_font_private_t, + link); + cairo_list_del (&font->link); + _cairo_script_surface_scaled_font_fini (font->parent); + } + + while (! cairo_list_is_empty (&ctx->defines)) { + struct def *def = cairo_list_first_entry (&ctx->defines, + struct def, link); + + status = _cairo_user_data_array_set_data (def->user_data, + (cairo_user_data_key_t *) ctx, + NULL, NULL); + assert (status == CAIRO_STATUS_SUCCESS); + } + + status = _cairo_output_stream_destroy (ctx->stream); + + free (ctx); + + return status; +} + +static cairo_status_t +_cairo_script_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_script_surface_t *surface = abstract_surface; + + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { + return _cairo_surface_wrapper_acquire_source_image (&surface->wrapper, + image_out, + image_extra); + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static void +_cairo_script_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_script_surface_t *surface = abstract_surface; + + assert (_cairo_surface_wrapper_is_active (&surface->wrapper)); + _cairo_surface_wrapper_release_source_image (&surface->wrapper, + image, + image_extra); +} + +static cairo_status_t +_cairo_script_surface_finish (void *abstract_surface) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status = CAIRO_STATUS_SUCCESS, status2; + + _cairo_surface_wrapper_fini (&surface->wrapper); + + if (surface->cr.current_style.dash != NULL) { + free (surface->cr.current_style.dash); + surface->cr.current_style.dash = NULL; + } + _cairo_pattern_fini (&surface->cr.current_source.base); + _cairo_path_fixed_fini (&surface->cr.current_path); + _cairo_surface_clipper_reset (&surface->clipper); + + if (surface->emitted) { + assert (! surface->active); + + if (! cairo_list_is_empty (&surface->operand.link)) { + if (! surface->ctx->active) { + if (target_is_active (surface)) { + _cairo_output_stream_printf (surface->ctx->stream, + "pop\n"); + } else { + int depth = target_depth (surface); + if (depth == 1) { + _cairo_output_stream_printf (surface->ctx->stream, + "exch pop\n"); + } else { + _cairo_output_stream_printf (surface->ctx->stream, + "%d -1 roll pop\n", + depth); + } + } + cairo_list_del (&surface->operand.link); + } else { + struct deferred_finish *link = malloc (sizeof (*link)); + if (link == NULL) { + status2 = _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + cairo_list_del (&surface->operand.link); + } else { + link->operand.type = DEFERRED; + cairo_list_swap (&link->operand.link, + &surface->operand.link); + cairo_list_add (&link->link, &surface->ctx->deferred); + } + } + } + + if (surface->defined) { + _cairo_output_stream_printf (surface->ctx->stream, + "/s%u undef\n", + surface->base.unique_id); + } + } + + status2 = _context_destroy (surface->ctx); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + return status; +} + +static cairo_int_status_t +_cairo_script_surface_copy_page (void *abstract_surface) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, "copy-page\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_script_surface_show_page (void *abstract_surface) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, "show-page\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_script_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_script_surface_t *surface = cairo_container_of (clipper, + cairo_script_surface_t, + clipper); + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + cairo_box_t box; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + if (path == NULL) { + if (surface->cr.has_clip) { + _cairo_output_stream_puts (surface->ctx->stream, "reset-clip\n"); + surface->cr.has_clip = FALSE; + } + return CAIRO_STATUS_SUCCESS; + } + + /* skip the trivial clip covering the surface extents */ + if (surface->width >=0 && surface->height >= 0 && + _cairo_path_fixed_is_box (path, &box)) + { + if (box.p1.x <= 0 && box.p1.y <= 0 && + box.p2.x - box.p1.x >= _cairo_fixed_from_double (surface->width) && + box.p2.y - box.p1.y >= _cairo_fixed_from_double (surface->height)) + { + return CAIRO_STATUS_SUCCESS; + } + } + + status = _emit_identity (surface, &matrix_updated); + if (unlikely (status)) + return status; + + status = _emit_fill_rule (surface, fill_rule); + if (unlikely (status)) + return status; + + if (! path->is_rectilinear) { + status = _emit_tolerance (surface, tolerance, matrix_updated); + if (unlikely (status)) + return status; + } + + if (! path->maybe_fill_region) { + status = _emit_antialias (surface, antialias); + if (unlikely (status)) + return status; + } + + status = _emit_path (surface, path); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, "clip+\n"); + surface->cr.has_clip = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +static void +active (cairo_script_surface_t *surface) +{ + if (surface->active++ == 0) + surface->ctx->active++; +} + +static void +inactive (cairo_script_surface_t *surface) +{ + cairo_script_context_t *ctx = surface->ctx; + cairo_list_t sorted; + + if (--surface->active) + return; + + if (--ctx->active) + return; + + cairo_list_init (&sorted); + while (! cairo_list_is_empty (&ctx->deferred)) { + struct deferred_finish *df; + cairo_list_t *operand; + int depth; + + df = cairo_list_first_entry (&ctx->deferred, + struct deferred_finish, + link); + + depth = 0; + cairo_list_foreach (operand, &ctx->operands) { + if (operand == &df->operand.link) + break; + depth++; + } + + df->operand.type = depth; + + if (cairo_list_is_empty (&sorted)) { + cairo_list_move (&df->link, &sorted); + } else { + struct deferred_finish *pos; + + cairo_list_foreach_entry (pos, struct deferred_finish, + &sorted, + link) + { + if (df->operand.type < pos->operand.type) + break; + } + cairo_list_move_tail (&df->link, &pos->link); + } + } + + while (! cairo_list_is_empty (&sorted)) { + struct deferred_finish *df; + cairo_list_t *operand; + int depth; + + df = cairo_list_first_entry (&sorted, + struct deferred_finish, + link); + + depth = 0; + cairo_list_foreach (operand, &ctx->operands) { + if (operand == &df->operand.link) + break; + depth++; + } + + if (depth == 0) { + _cairo_output_stream_printf (ctx->stream, + "pop\n"); + } else if (depth == 1) { + _cairo_output_stream_printf (ctx->stream, + "exch pop\n"); + } else { + _cairo_output_stream_printf (ctx->stream, + "%d -1 roll pop\n", + depth); + } + + cairo_list_del (&df->operand.link); + cairo_list_del (&df->link); + free (df); + } +} + +static cairo_int_status_t +_cairo_script_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (op == CAIRO_OPERATOR_CLEAR) { + if (surface->is_clear) + goto DONE; + } + + active (surface); + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + status = _emit_source (surface, op, source); + if (unlikely (status)) + return status; + + status = _emit_operator (surface, op); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, + "paint\n"); + + surface->is_clear = op == CAIRO_OPERATOR_CLEAR && clip == NULL; + + inactive (surface); + + DONE: + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { + return _cairo_surface_wrapper_paint (&surface->wrapper, + op, source, clip); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_script_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (op == CAIRO_OPERATOR_CLEAR) { + if (surface->is_clear) + goto DONE; + } + + active (surface); + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + status = _emit_source (surface, op, source); + if (unlikely (status)) + return status; + + status = _emit_operator (surface, op); + if (unlikely (status)) + return status; + + if (_cairo_pattern_equal (source, mask)) { + _cairo_output_stream_puts (surface->ctx->stream, "/source get"); + } else { + status = _emit_pattern (surface, mask); + if (unlikely (status)) + return status; + } + + assert (surface->cr.current_operator == op); + + _cairo_output_stream_puts (surface->ctx->stream, + " mask\n"); + + surface->is_clear = FALSE; + + inactive (surface); + + DONE: + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { + return _cairo_surface_wrapper_mask (&surface->wrapper, + op, source, mask, clip); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_script_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_stroke_style_t *style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + + if (op == CAIRO_OPERATOR_CLEAR) { + if (surface->is_clear) + goto DONE; + } + + active (surface); + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + status = _emit_identity (surface, &matrix_updated); + if (unlikely (status)) + return status; + + status = _emit_path (surface, path); + if (unlikely (status)) + return status; + + status = _emit_source (surface, op, source); + if (unlikely (status)) + return status; + + status = _emit_scaling_matrix (surface, ctm, &matrix_updated); + if (unlikely (status)) + return status; + + status = _emit_operator (surface, op); + if (unlikely (status)) + return status; + + if (_scaling_matrix_equal (&surface->cr.current_ctm, + &surface->cr.current_stroke_matrix)) + { + matrix_updated = FALSE; + } + else + { + matrix_updated = TRUE; + surface->cr.current_stroke_matrix = surface->cr.current_ctm; + } + + status = _emit_stroke_style (surface, style, matrix_updated); + if (unlikely (status)) + return status; + + if (! path->is_rectilinear) { + status = _emit_tolerance (surface, tolerance, matrix_updated); + if (unlikely (status)) + return status; + } + + status = _emit_antialias (surface, antialias); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, "stroke+\n"); + + surface->is_clear = FALSE; + + inactive (surface); + + DONE: + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { + return _cairo_surface_wrapper_stroke (&surface->wrapper, + op, source, path, + style, + ctm, ctm_inverse, + tolerance, antialias, + clip); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_script_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + cairo_box_t box; + + if (op == CAIRO_OPERATOR_CLEAR) { + if (surface->is_clear) + goto DONE; + } + + active (surface); + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + status = _emit_identity (surface, &matrix_updated); + if (unlikely (status)) + return status; + + status = _emit_source (surface, op, source); + if (unlikely (status)) + return status; + + if (! _cairo_path_fixed_is_box (path, &box)) { + status = _emit_fill_rule (surface, fill_rule); + if (unlikely (status)) + return status; + } + + if (! path->is_rectilinear) { + status = _emit_tolerance (surface, tolerance, matrix_updated); + if (unlikely (status)) + return status; + } + + if (! path->maybe_fill_region) { + status = _emit_antialias (surface, antialias); + if (unlikely (status)) + return status; + } + + status = _emit_path (surface, path); + if (unlikely (status)) + return status; + + status = _emit_operator (surface, op); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, "fill+\n"); + + surface->is_clear = FALSE; + + inactive (surface); + + DONE: + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { + return _cairo_surface_wrapper_fill (&surface->wrapper, + op, source, path, + fill_rule, + tolerance, + antialias, + clip); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_surface_t * +_cairo_script_surface_snapshot (void *abstract_surface) +{ + cairo_script_surface_t *surface = abstract_surface; + + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) + return _cairo_surface_wrapper_snapshot (&surface->wrapper); + + return NULL; +} + +static cairo_bool_t +_cairo_script_surface_has_show_text_glyphs (void *abstract_surface) +{ + return TRUE; +} + +static const char * +_subpixel_order_to_string (cairo_subpixel_order_t subpixel_order) +{ + static const char *names[] = { + "SUBPIXEL_ORDER_DEFAULT", /* CAIRO_SUBPIXEL_ORDER_DEFAULT */ + "SUBPIXEL_ORDER_RGB", /* CAIRO_SUBPIXEL_ORDER_RGB */ + "SUBPIXEL_ORDER_BGR", /* CAIRO_SUBPIXEL_ORDER_BGR */ + "SUBPIXEL_ORDER_VRGB", /* CAIRO_SUBPIXEL_ORDER_VRGB */ + "SUBPIXEL_ORDER_VBGR" /* CAIRO_SUBPIXEL_ORDER_VBGR */ + }; + return names[subpixel_order]; +} +static const char * +_hint_style_to_string (cairo_hint_style_t hint_style) +{ + static const char *names[] = { + "HINT_STYLE_DEFAULT", /* CAIRO_HINT_STYLE_DEFAULT */ + "HINT_STYLE_NONE", /* CAIRO_HINT_STYLE_NONE */ + "HINT_STYLE_SLIGHT", /* CAIRO_HINT_STYLE_SLIGHT */ + "HINT_STYLE_MEDIUM", /* CAIRO_HINT_STYLE_MEDIUM */ + "HINT_STYLE_FULL" /* CAIRO_HINT_STYLE_FULL */ + }; + return names[hint_style]; +} +static const char * +_hint_metrics_to_string (cairo_hint_metrics_t hint_metrics) +{ + static const char *names[] = { + "HINT_METRICS_DEFAULT", /* CAIRO_HINT_METRICS_DEFAULT */ + "HINT_METRICS_OFF", /* CAIRO_HINT_METRICS_OFF */ + "HINT_METRICS_ON" /* CAIRO_HINT_METRICS_ON */ + }; + return names[hint_metrics]; +} + +static cairo_status_t +_emit_font_options (cairo_script_surface_t *surface, + cairo_font_options_t *font_options) +{ + if (cairo_font_options_equal (&surface->cr.current_font_options, + font_options)) + { + return CAIRO_STATUS_SUCCESS; + } + + _cairo_output_stream_printf (surface->ctx->stream, "<<"); + + if (font_options->antialias != surface->cr.current_font_options.antialias) { + _cairo_output_stream_printf (surface->ctx->stream, + " /antialias //%s", + _antialias_to_string (font_options->antialias)); + } + + if (font_options->subpixel_order != + surface->cr.current_font_options.subpixel_order) + { + _cairo_output_stream_printf (surface->ctx->stream, + " /subpixel-order //%s", + _subpixel_order_to_string (font_options->subpixel_order)); + } + + if (font_options->hint_style != + surface->cr.current_font_options.hint_style) + { + _cairo_output_stream_printf (surface->ctx->stream, + " /hint-style //%s", + _hint_style_to_string (font_options->hint_style)); + } + + if (font_options->hint_metrics != + surface->cr.current_font_options.hint_metrics) + { + _cairo_output_stream_printf (surface->ctx->stream, + " /hint-metrics //%s", + _hint_metrics_to_string (font_options->hint_metrics)); + } + + _cairo_output_stream_printf (surface->ctx->stream, + " >> set-font-options\n"); + + surface->cr.current_font_options = *font_options; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) +{ + cairo_script_surface_font_private_t *font_private; + + font_private = scaled_font->surface_private; + if (font_private != NULL) { + _cairo_output_stream_printf (font_private->ctx->stream, + "/f%lu undef /sf%lu undef\n", + font_private->id, + font_private->id); + + _bitmap_release_id (&font_private->ctx->font_id, font_private->id); + cairo_list_del (&font_private->link); + free (font_private); + + scaled_font->surface_private = NULL; + } +} + +static cairo_status_t +_emit_type42_font (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + const cairo_scaled_font_backend_t *backend; + cairo_script_surface_font_private_t *font_private; + cairo_output_stream_t *base85_stream; + cairo_output_stream_t *zlib_stream; + cairo_status_t status, status2; + unsigned long size; + unsigned int load_flags; + uint32_t len; + uint8_t *buf; + + backend = scaled_font->backend; + if (backend->load_truetype_table == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + size = 0; + status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size); + if (unlikely (status)) + return status; + + buf = malloc (size); + if (unlikely (buf == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = backend->load_truetype_table (scaled_font, 0, 0, buf, NULL); + if (unlikely (status)) { + free (buf); + return status; + } + + load_flags = _cairo_ft_scaled_font_get_load_flags (scaled_font); + _cairo_output_stream_printf (surface->ctx->stream, + "<< " + "/type 42 " + "/index 0 " + "/flags %d " + "/source <|", + load_flags); + + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + len = size; + _cairo_output_stream_write (base85_stream, &len, sizeof (len)); + + zlib_stream = _cairo_deflate_stream_create (base85_stream); + + _cairo_output_stream_write (zlib_stream, buf, size); + free (buf); + + status2 = _cairo_output_stream_destroy (zlib_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + status2 = _cairo_output_stream_destroy (base85_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + font_private = scaled_font->surface_private; + _cairo_output_stream_printf (surface->ctx->stream, + "~> >> font dup /f%lu exch def set-font-face", + font_private->id); + + return status; +} + +static cairo_status_t +_emit_scaled_font_init (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + cairo_script_surface_font_private_t *font_private; + cairo_status_t status; + + font_private = malloc (sizeof (cairo_script_surface_font_private_t)); + if (unlikely (font_private == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font_private->ctx = surface->ctx; + font_private->parent = scaled_font; + font_private->subset_glyph_index = 0; + font_private->has_sfnt = TRUE; + + cairo_list_add (&font_private->link, &surface->ctx->fonts); + + status = _bitmap_next_id (&surface->ctx->font_id, + &font_private->id); + if (unlikely (status)) { + free (font_private); + return status; + } + + scaled_font->surface_private = font_private; + scaled_font->surface_backend = &_cairo_script_surface_backend; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + status = _emit_type42_font (surface, scaled_font); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + font_private->has_sfnt = FALSE; + _cairo_output_stream_printf (surface->ctx->stream, + "dict\n" + " /type 3 set\n" + " /metrics [%f %f %f %f %f] set\n" + " /glyphs array set\n" + " font dup /f%lu exch def set-font-face", + scaled_font->fs_extents.ascent, + scaled_font->fs_extents.descent, + scaled_font->fs_extents.height, + scaled_font->fs_extents.max_x_advance, + scaled_font->fs_extents.max_y_advance, + font_private->id); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_scaled_font (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + cairo_matrix_t matrix; + cairo_font_options_t options; + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + cairo_script_surface_font_private_t *font_private; + + cairo_scaled_font_get_ctm (scaled_font, &matrix); + status = _emit_scaling_matrix (surface, &matrix, &matrix_updated); + if (unlikely (status)) + return status; + + if (! matrix_updated && surface->cr.current_scaled_font == scaled_font) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_scaled_font = scaled_font; + + if (! (scaled_font->surface_backend == NULL || + scaled_font->surface_backend == &_cairo_script_surface_backend)) + { + _cairo_scaled_font_revoke_ownership (scaled_font); + } + + font_private = scaled_font->surface_private; + if (font_private == NULL) { + cairo_scaled_font_get_font_matrix (scaled_font, &matrix); + status = _emit_font_matrix (surface, &matrix); + if (unlikely (status)) + return status; + + cairo_scaled_font_get_font_options (scaled_font, &options); + status = _emit_font_options (surface, &options); + if (unlikely (status)) + return status; + + status = _emit_scaled_font_init (surface, scaled_font); + if (unlikely (status)) + return status; + + font_private = scaled_font->surface_private; + assert (font_private != NULL); + + assert (target_is_active (surface)); + _cairo_output_stream_printf (surface->ctx->stream, + " /scaled-font get /sf%lu exch def\n", + font_private->id); + } else { + _cairo_output_stream_printf (surface->ctx->stream, + "sf%lu set-scaled-font\n", + font_private->id); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_scaled_glyph_vector (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_script_surface_font_private_t *font_private; + cairo_script_implicit_context_t old_cr; + cairo_status_t status; + unsigned long index; + + font_private = scaled_font->surface_private; + index = ++font_private->subset_glyph_index; + scaled_glyph->surface_private = (void *) index; + + _cairo_output_stream_printf (surface->ctx->stream, + "%lu <<\n" + " /metrics [%f %f %f %f %f %f]\n" + " /render {\n", + index, + scaled_glyph->fs_metrics.x_bearing, + scaled_glyph->fs_metrics.y_bearing, + scaled_glyph->fs_metrics.width, + scaled_glyph->fs_metrics.height, + scaled_glyph->fs_metrics.x_advance, + scaled_glyph->fs_metrics.y_advance); + + if (! _cairo_matrix_is_identity (&scaled_font->scale_inverse)) { + _cairo_output_stream_printf (surface->ctx->stream, + "[%f %f %f %f %f %f] transform\n", + scaled_font->scale_inverse.xx, + scaled_font->scale_inverse.yx, + scaled_font->scale_inverse.xy, + scaled_font->scale_inverse.yy, + scaled_font->scale_inverse.x0, + scaled_font->scale_inverse.y0); + } + + old_cr = surface->cr; + _cairo_script_implicit_context_init (&surface->cr); + status = _cairo_recording_surface_replay (scaled_glyph->recording_surface, + &surface->base); + surface->cr = old_cr; + + _cairo_output_stream_puts (surface->ctx->stream, "} >> set\n"); + + return status; +} + +static cairo_status_t +_emit_scaled_glyph_bitmap (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_script_surface_font_private_t *font_private; + cairo_status_t status; + unsigned long index; + + font_private = scaled_font->surface_private; + index = ++font_private->subset_glyph_index; + scaled_glyph->surface_private = (void *) index; + + _cairo_output_stream_printf (surface->ctx->stream, + "%lu <<\n" + " /metrics [%f %f %f %f %f %f]\n" + " /render {\n" + "%f %f translate\n", + index, + scaled_glyph->fs_metrics.x_bearing, + scaled_glyph->fs_metrics.y_bearing, + scaled_glyph->fs_metrics.width, + scaled_glyph->fs_metrics.height, + scaled_glyph->fs_metrics.x_advance, + scaled_glyph->fs_metrics.y_advance, + scaled_glyph->fs_metrics.x_bearing, + scaled_glyph->fs_metrics.y_bearing); + + status = _emit_image_surface (surface, scaled_glyph->surface); + if (unlikely (status)) + return status; + + if (! _cairo_matrix_is_identity (&scaled_font->font_matrix)) { + _cairo_output_stream_printf (surface->ctx->stream, + "\n [%f %f %f %f %f %f] set-matrix\n", + scaled_font->font_matrix.xx, + scaled_font->font_matrix.yx, + scaled_font->font_matrix.xy, + scaled_font->font_matrix.yy, + scaled_font->font_matrix.x0, + scaled_font->font_matrix.y0); + } + _cairo_output_stream_puts (surface->ctx->stream, + "mask\n} >> set\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_scaled_glyph_prologue (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + cairo_script_surface_font_private_t *font_private; + + assert (scaled_font->surface_backend == &_cairo_script_surface_backend); + + font_private = scaled_font->surface_private; + + _cairo_output_stream_printf (surface->ctx->stream, + "f%lu /glyphs get\n", + font_private->id); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_scaled_glyphs (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + unsigned int num_glyphs) +{ + cairo_script_surface_font_private_t *font_private; + cairo_status_t status; + unsigned int n; + cairo_bool_t have_glyph_prologue = FALSE; + + if (num_glyphs == 0) + return CAIRO_STATUS_SUCCESS; + + font_private = scaled_font->surface_private; + if (font_private->has_sfnt) + return CAIRO_STATUS_SUCCESS; + + _cairo_scaled_font_freeze_cache (scaled_font); + for (n = 0; n < num_glyphs; n++) { + cairo_scaled_glyph_t *scaled_glyph; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) + break; + + if (scaled_glyph->surface_private != NULL) + continue; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, + &scaled_glyph); + if (_cairo_status_is_error (status)) + break; + + if (status == CAIRO_STATUS_SUCCESS) { + if (! have_glyph_prologue) { + status = _emit_scaled_glyph_prologue (surface, scaled_font); + if (unlikely (status)) + break; + + have_glyph_prologue = TRUE; + } + + status = _emit_scaled_glyph_vector (surface, + scaled_font, + scaled_glyph); + if (unlikely (status)) + break; + + continue; + } + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (_cairo_status_is_error (status)) + break; + + if (status == CAIRO_STATUS_SUCCESS) { + if (! have_glyph_prologue) { + status = _emit_scaled_glyph_prologue (surface, scaled_font); + if (unlikely (status)) + break; + + have_glyph_prologue = TRUE; + } + + status = _emit_scaled_glyph_bitmap (surface, + scaled_font, + scaled_glyph); + if (unlikely (status)) + break; + + continue; + } + } + _cairo_scaled_font_thaw_cache (scaled_font); + + if (have_glyph_prologue) { + _cairo_output_stream_puts (surface->ctx->stream, "pop pop\n"); + } + + return status; +} + +static void +_emit_string_literal (cairo_script_surface_t *surface, + const char *utf8, int len) +{ + char c; + const char *end; + + _cairo_output_stream_puts (surface->ctx->stream, "("); + + if (utf8 == NULL) { + end = utf8; + } else { + if (len < 0) + len = strlen (utf8); + end = utf8 + len; + } + + while (utf8 < end) { + switch ((c = *utf8++)) { + case '\n': + c = 'n'; + goto ESCAPED_CHAR; + case '\r': + c = 'r'; + goto ESCAPED_CHAR; + case '\t': + c = 't'; + goto ESCAPED_CHAR; + case '\b': + c = 'b'; + goto ESCAPED_CHAR; + case '\f': + c = 'f'; + goto ESCAPED_CHAR; + case '\\': + case '(': + case ')': +ESCAPED_CHAR: + _cairo_output_stream_printf (surface->ctx->stream, "\\%c", c); + break; + default: + if (isprint (c) || isspace (c)) { + _cairo_output_stream_printf (surface->ctx->stream, "%c", c); + } else { + int octal = 0; + while (c) { + octal *= 10; + octal += c&7; + c /= 8; + } + _cairo_output_stream_printf (surface->ctx->stream, + "\\%03d", octal); + } + break; + } + } + _cairo_output_stream_puts (surface->ctx->stream, ")"); +} + +static cairo_int_status_t +_cairo_script_surface_show_text_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t backward, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_script_surface_font_private_t *font_private; + cairo_scaled_glyph_t *scaled_glyph; + cairo_matrix_t matrix; + cairo_status_t status; + double x, y, ix, iy; + int n; + cairo_output_stream_t *base85_stream = NULL; + + if (op == CAIRO_OPERATOR_CLEAR) { + if (surface->is_clear) + goto DONE; + } + + active (surface); + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + status = _emit_source (surface, op, source); + if (unlikely (status)) + return status; + + status = _emit_scaled_font (surface, scaled_font); + if (unlikely (status)) + return status; + + status = _emit_operator (surface, op); + if (unlikely (status)) + return status; + + status = _emit_scaled_glyphs (surface, scaled_font, glyphs, num_glyphs); + if (unlikely (status)) + return status; + + /* (utf8) [cx cy [glyphs]] [clusters] backward show_text_glyphs */ + /* [cx cy [glyphs]] show_glyphs */ + + if (utf8 != NULL && clusters != NULL) { + _emit_string_literal (surface, utf8, utf8_len); + _cairo_output_stream_puts (surface->ctx->stream, " "); + } + + matrix = surface->cr.current_ctm; + status = cairo_matrix_invert (&matrix); + assert (status == CAIRO_STATUS_SUCCESS); + + ix = x = glyphs[0].x; + iy = y = glyphs[0].y; + cairo_matrix_transform_point (&matrix, &ix, &iy); + ix -= scaled_font->font_matrix.x0; + iy -= scaled_font->font_matrix.y0; + + _cairo_scaled_font_freeze_cache (scaled_font); + font_private = scaled_font->surface_private; + + _cairo_output_stream_printf (surface->ctx->stream, + "[%f %f ", + ix, iy); + + for (n = 0; n < num_glyphs; n++) { + if (font_private->has_sfnt) { + if (glyphs[n].index > 256) + break; + } else { + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) { + _cairo_scaled_font_thaw_cache (scaled_font); + return status; + } + + if ((long unsigned) scaled_glyph->surface_private > 256) + break; + } + } + + if (n == num_glyphs) { + _cairo_output_stream_puts (surface->ctx->stream, "<~"); + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + } else + _cairo_output_stream_puts (surface->ctx->stream, "["); + + for (n = 0; n < num_glyphs; n++) { + double dx, dy; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) + break; + + if (fabs (glyphs[n].x - x) > 1e-5 || fabs (glyphs[n].y - y) > 1e-5) { + if (fabs (glyphs[n].y - y) < 1e-5) { + if (base85_stream != NULL) { + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) { + base85_stream = NULL; + break; + } + + _cairo_output_stream_printf (surface->ctx->stream, + "~> %f <~", glyphs[n].x - x); + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + } else { + _cairo_output_stream_printf (surface->ctx->stream, + " ] %f [ ", glyphs[n].x - x); + } + + x = glyphs[n].x; + } else { + ix = x = glyphs[n].x; + iy = y = glyphs[n].y; + cairo_matrix_transform_point (&matrix, &ix, &iy); + ix -= scaled_font->font_matrix.x0; + iy -= scaled_font->font_matrix.y0; + if (base85_stream != NULL) { + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) { + base85_stream = NULL; + break; + } + + _cairo_output_stream_printf (surface->ctx->stream, + "~> %f %f <~", + ix, iy); + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + } else { + _cairo_output_stream_printf (surface->ctx->stream, + " ] %f %f [ ", + ix, iy); + } + } + } + if (base85_stream != NULL) { + uint8_t c; + + if (font_private->has_sfnt) + c = glyphs[n].index; + else + c = (uint8_t) (long unsigned) scaled_glyph->surface_private; + + _cairo_output_stream_write (base85_stream, &c, 1); + } else { + if (font_private->has_sfnt) + _cairo_output_stream_printf (surface->ctx->stream, " %lu", + glyphs[n].index); + else + _cairo_output_stream_printf (surface->ctx->stream, " %lu", + (long unsigned) scaled_glyph->surface_private); + } + + dx = scaled_glyph->metrics.x_advance; + dy = scaled_glyph->metrics.y_advance; + cairo_matrix_transform_distance (&scaled_font->ctm, &dx, &dy); + x += dx; + y += dy; + } + _cairo_scaled_font_thaw_cache (scaled_font); + + if (base85_stream != NULL) { + cairo_status_t status2; + + status2 = _cairo_output_stream_destroy (base85_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + _cairo_output_stream_printf (surface->ctx->stream, "~>"); + } else { + _cairo_output_stream_puts (surface->ctx->stream, " ]"); + } + if (unlikely (status)) + return status; + + if (utf8 != NULL && clusters != NULL) { + for (n = 0; n < num_clusters; n++) { + if (clusters[n].num_bytes > UCHAR_MAX || + clusters[n].num_glyphs > UCHAR_MAX) + { + break; + } + } + + if (n < num_clusters) { + _cairo_output_stream_puts (surface->ctx->stream, "] [ "); + for (n = 0; n < num_clusters; n++) { + _cairo_output_stream_printf (surface->ctx->stream, + "%d %d ", + clusters[n].num_bytes, + clusters[n].num_glyphs); + } + _cairo_output_stream_puts (surface->ctx->stream, "]"); + } + else + { + _cairo_output_stream_puts (surface->ctx->stream, "] <~"); + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + for (n = 0; n < num_clusters; n++) { + uint8_t c[2]; + c[0] = clusters[n].num_bytes; + c[1] = clusters[n].num_glyphs; + _cairo_output_stream_write (base85_stream, c, 2); + } + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, "~>"); + } + + _cairo_output_stream_printf (surface->ctx->stream, + " //%s show-text-glyphs\n", + _direction_to_string (backward)); + } else { + _cairo_output_stream_puts (surface->ctx->stream, + "] show-glyphs\n"); + } + + surface->is_clear = FALSE; + + inactive (surface); + + DONE: + if (_cairo_surface_wrapper_is_active (&surface->wrapper)){ + return _cairo_surface_wrapper_show_text_glyphs (&surface->wrapper, + op, source, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + backward, + scaled_font, + clip); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_script_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_script_surface_t *surface = abstract_surface; + + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { + return _cairo_surface_wrapper_get_extents (&surface->wrapper, + rectangle); + } + + if (surface->width < 0 || surface->height < 0) + return FALSE; + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = surface->width; + rectangle->height = surface->height; + + return TRUE; +} + +static const cairo_surface_backend_t +_cairo_script_surface_backend = { + CAIRO_SURFACE_TYPE_SCRIPT, + _cairo_script_surface_create_similar, + _cairo_script_surface_finish, + _cairo_script_surface_acquire_source_image, + _cairo_script_surface_release_source_image, + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + _cairo_script_surface_copy_page, + _cairo_script_surface_show_page, + _cairo_script_surface_get_extents, + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + _cairo_script_surface_scaled_font_fini, + NULL, /* scaled_glyph_fini */ + + /* The 5 high level operations */ + _cairo_script_surface_paint, + _cairo_script_surface_mask, + _cairo_script_surface_stroke, + _cairo_script_surface_fill, + NULL, + + _cairo_script_surface_snapshot, + + NULL, /* is_similar */ + /* XXX need fill-stroke for passthrough */ + NULL, /* fill_stroke */ + NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ + + /* The alternate high-level text operation */ + _cairo_script_surface_has_show_text_glyphs, + _cairo_script_surface_show_text_glyphs +}; + +static void +_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr) +{ + cr->current_operator = CAIRO_GSTATE_OPERATOR_DEFAULT; + cr->current_fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT; + cr->current_tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT; + cr->current_antialias = CAIRO_ANTIALIAS_DEFAULT; + _cairo_stroke_style_init (&cr->current_style); + _cairo_pattern_init_solid (&cr->current_source.solid, + CAIRO_COLOR_BLACK, + CAIRO_CONTENT_COLOR); + _cairo_path_fixed_init (&cr->current_path); + cairo_matrix_init_identity (&cr->current_ctm); + cairo_matrix_init_identity (&cr->current_stroke_matrix); + cairo_matrix_init_identity (&cr->current_font_matrix); + _cairo_font_options_init_default (&cr->current_font_options); + cr->current_scaled_font = NULL; + cr->has_clip = FALSE; +} + +static void +_cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr) +{ + if (cr->current_style.dash != NULL) { + free (cr->current_style.dash); + cr->current_style.dash = NULL; + } + _cairo_pattern_fini (&cr->current_source.base); + _cairo_path_fixed_fini (&cr->current_path); + + _cairo_script_implicit_context_init (cr); +} + +static cairo_script_surface_t * +_cairo_script_surface_create_internal (cairo_script_context_t *ctx, + cairo_content_t content, + double width, + double height, + cairo_surface_t *passthrough) +{ + cairo_script_surface_t *surface; + + if (unlikely (ctx == NULL)) + return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); + + surface = malloc (sizeof (cairo_script_surface_t)); + if (unlikely (surface == NULL)) + return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &_cairo_script_surface_backend, + content); + + _cairo_surface_wrapper_init (&surface->wrapper, passthrough); + + _cairo_surface_clipper_init (&surface->clipper, + _cairo_script_surface_clipper_intersect_clip_path); + + surface->ctx = ctx; + ctx->ref++; + + surface->width = width; + surface->height = height; + + surface->emitted = FALSE; + surface->defined = FALSE; + surface->is_clear = FALSE; + surface->active = FALSE; + surface->operand.type = SURFACE; + cairo_list_init (&surface->operand.link); + + _cairo_script_implicit_context_init (&surface->cr); + + return surface; +} + +static const cairo_script_context_t _nil_context = { + CAIRO_STATUS_NO_MEMORY, + -1 +}; + +static cairo_script_context_t * +_cairo_script_context_create_internal (cairo_output_stream_t *stream) +{ + cairo_script_context_t *ctx; + + ctx = malloc (sizeof (cairo_script_context_t)); + if (unlikely (ctx == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_script_context_t *) &_nil_context; + } + + memset (ctx, 0, sizeof (cairo_script_context_t)); + ctx->status = CAIRO_STATUS_SUCCESS; + ctx->ref = 1; + + cairo_list_init (&ctx->operands); + cairo_list_init (&ctx->deferred); + ctx->stream = stream; + ctx->mode = CAIRO_SCRIPT_MODE_ASCII; + + cairo_list_init (&ctx->fonts); + cairo_list_init (&ctx->defines); + + _cairo_output_stream_puts (ctx->stream, "%!CairoScript\n"); + + return ctx; +} + +cairo_script_context_t * +cairo_script_context_create (const char *filename) +{ + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create_for_filename (filename); + if (_cairo_output_stream_get_status (stream)) + return (cairo_script_context_t *) &_nil_context; + + return _cairo_script_context_create_internal (stream); +} + +cairo_script_context_t * +cairo_script_context_create_for_stream (cairo_write_func_t write_func, + void *closure) +{ + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create (write_func, NULL, closure); + if (_cairo_output_stream_get_status (stream)) + return (cairo_script_context_t *) &_nil_context; + + return _cairo_script_context_create_internal (stream); +} + +void +cairo_script_context_write_comment (cairo_script_context_t *context, + const char *comment, + int len) +{ + if (len < 0) + len = strlen (comment); + + _cairo_output_stream_puts (context->stream, "% "); + _cairo_output_stream_write (context->stream, comment, len); + _cairo_output_stream_puts (context->stream, "\n"); +} + +void +cairo_script_context_set_mode (cairo_script_context_t *context, + cairo_script_mode_t mode) +{ + context->mode = mode; +} + +cairo_script_mode_t +cairo_script_context_get_mode (cairo_script_context_t *context) +{ + return context->mode; +} + +cairo_surface_t * +cairo_script_surface_create (cairo_script_context_t *context, + cairo_content_t content, + double width, + double height) +{ + return &_cairo_script_surface_create_internal (context, + content, + width, height, + NULL)->base; +} + +cairo_surface_t * +cairo_script_surface_create_for_target (cairo_script_context_t *context, + cairo_surface_t *target) +{ + cairo_rectangle_int_t extents; + + if (unlikely (target->status)) + return _cairo_surface_create_in_error (target->status); + + if (! _cairo_surface_get_extents (target, &extents)) + extents.width = extents.height = -1; + + return &_cairo_script_surface_create_internal (context, + target->content, + extents.width, + extents.height, + target)->base; +} + +cairo_status_t +cairo_script_from_recording_surface (cairo_script_context_t *context, + cairo_surface_t *recording_surface) +{ + cairo_box_t bbox; + cairo_rectangle_int_t extents; + cairo_surface_t *surface; + cairo_status_t status; + + if (unlikely (context->status)) + return context->status; + + if (unlikely (recording_surface->status)) + return recording_surface->status; + + if (unlikely (! _cairo_surface_is_recording (recording_surface))) + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + + status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, + &bbox, NULL); + if (unlikely (status)) + return status; + + _cairo_box_round_to_rectangle (&bbox, &extents); + + surface = &_cairo_script_surface_create_internal (context, + recording_surface->content, + extents.width, + extents.height, + NULL)->base; + if (unlikely (surface->status)) + return surface->status; + + cairo_surface_set_device_offset (surface, -extents.x, -extents.y); + status = _cairo_recording_surface_replay (recording_surface, surface); + cairo_surface_destroy (surface); + + return status; +} + +void +cairo_script_context_destroy (cairo_script_context_t *context) +{ + cairo_status_t status_ignored; + + if (context == NULL || context->ref < 0) + return; + + status_ignored = _context_destroy (context); +} diff --git a/gfx/cairo/cairo/src/cairo-script.h b/gfx/cairo/cairo/src/cairo-script.h new file mode 100644 index 000000000000..5af625c1016a --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-script.h @@ -0,0 +1,94 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2008 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Chris Wilson + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_SCRIPT_H +#define CAIRO_SCRIPT_H + +#include "cairo.h" + +#if CAIRO_HAS_SCRIPT_SURFACE + +CAIRO_BEGIN_DECLS + +typedef struct _cairo_script_context cairo_script_context_t; + +typedef enum { + CAIRO_SCRIPT_MODE_BINARY, + CAIRO_SCRIPT_MODE_ASCII +} cairo_script_mode_t; + +cairo_public cairo_script_context_t * +cairo_script_context_create (const char *filename); + +cairo_public cairo_script_context_t * +cairo_script_context_create_for_stream (cairo_write_func_t write_func, + void *closure); + +cairo_public void +cairo_script_context_write_comment (cairo_script_context_t *context, + const char *comment, + int len); + +cairo_public void +cairo_script_context_set_mode (cairo_script_context_t *context, + cairo_script_mode_t mode); + +cairo_public cairo_script_mode_t +cairo_script_context_get_mode (cairo_script_context_t *context); + +cairo_public void +cairo_script_context_destroy (cairo_script_context_t *context); + +cairo_public cairo_surface_t * +cairo_script_surface_create (cairo_script_context_t *context, + cairo_content_t content, + double width, + double height); + +cairo_public cairo_surface_t * +cairo_script_surface_create_for_target (cairo_script_context_t *context, + cairo_surface_t *target); + +cairo_public cairo_status_t +cairo_script_from_recording_surface (cairo_script_context_t *context, + cairo_surface_t *recording_surface); + +CAIRO_END_DECLS + +#else /*CAIRO_HAS_SCRIPT_SURFACE*/ +# error Cairo was not compiled with support for the CairoScript backend +#endif /*CAIRO_HAS_SCRIPT_SURFACE*/ + +#endif /*CAIRO_SCRIPT_H*/ diff --git a/gfx/cairo/cairo/src/cairo-skia.h b/gfx/cairo/cairo/src/cairo-skia.h new file mode 100644 index 000000000000..046599faa312 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-skia.h @@ -0,0 +1,84 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + */ + +#ifndef CAIRO_SKIA_H +#define CAIRO_SKIA_H + +#include "cairo.h" + +#ifdef CAIRO_HAS_SKIA_SURFACE + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_skia_surface_create (cairo_format_t format, + int width, + int height); + +cairo_public cairo_surface_t * +cairo_skia_surface_create_for_data (unsigned char *data, + cairo_format_t format, + int width, + int height, + int stride); + +cairo_public unsigned char * +cairo_skia_surface_get_data (cairo_surface_t *surface); + +cairo_public cairo_format_t +cairo_skia_surface_get_format (cairo_surface_t *surface); + +cairo_public int +cairo_skia_surface_get_width (cairo_surface_t *surface); + +cairo_public int +cairo_skia_surface_get_height (cairo_surface_t *surface); + +cairo_public int +cairo_skia_surface_get_stride (cairo_surface_t *surface); + +cairo_public cairo_surface_t * +cairo_skia_surface_get_image (cairo_surface_t *surface); + +CAIRO_END_DECLS + +#else + +# error Cairo was not compiled with support for the Skia backend + +#endif + +#endif diff --git a/gfx/cairo/cairo/src/cairo-skiplist-private.h b/gfx/cairo/cairo/src/cairo-skiplist-private.h deleted file mode 100644 index 250b5a26e488..000000000000 --- a/gfx/cairo/cairo/src/cairo-skiplist-private.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright © 2006 Keith Packard - * Copyright © 2006 Carl Worth - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that copyright - * notice and this permission notice appear in supporting documentation, and - * that the name of the copyright holders not be used in advertising or - * publicity pertaining to distribution of the software without specific, - * written prior permission. The copyright holders make no representations - * about the suitability of this software for any purpose. It is provided "as - * is" without express or implied warranty. - * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO - * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, - * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THIS SOFTWARE. - */ - -#ifndef SKIPLIST_H -#define SKIPLIST_H - -#include "cairoint.h" - -/* - * Skip lists are described in detail here: - * - * http://citeseer.ist.psu.edu/pugh90skip.html - */ - -/* Note that random_level() called from alloc_node_for_level() depends on - * this being not more than 16. - */ -#define MAX_LEVEL 15 - -/* Returns the index of the free-list to use for a node at level 'level' */ -#define FREELIST_FOR_LEVEL(level) (((level) - 1) / 2) - -/* Returns the maximum level that uses the same free-list as 'level' does */ -#define FREELIST_MAX_LEVEL_FOR(level) (((level) + 1) & ~1) - -#define MAX_FREELIST_LEVEL (FREELIST_FOR_LEVEL (MAX_LEVEL - 1) + 1) - -/* - * Skip list element. In order to use the skip list, the caller must - * generate a structure for list elements that has as its final member - * a skip_elt_t, (which will be allocated with variable size). - * - * The caller must also pass the size of the structure to - * _cairo_skip_list_init. - */ -typedef struct _skip_elt { - int prev_index; - struct _skip_elt *prev; - struct _skip_elt *next[1]; -} skip_elt_t; - -#define SKIP_LIST_ELT_TO_DATA(type, elt) ((type *) ((char *) (elt) - (sizeof (type) - sizeof (skip_elt_t)))) - -typedef int -(*cairo_skip_list_compare_t) (void *list, void *a, void *b); - -typedef struct _skip_list { - cairo_skip_list_compare_t compare; - size_t elt_size; - size_t data_size; - skip_elt_t *chains[MAX_LEVEL]; - skip_elt_t *freelists[MAX_FREELIST_LEVEL]; - int max_level; - struct pool *pool; - char pool_embedded[1024]; -} cairo_skip_list_t; - -/* Initialize a new skip list. The compare function accepts a pointer - * to the list as well as pointers to two elements. The function must - * return a value greater than zero, zero, or less then 0 if the first - * element is considered respectively greater than, equal to, or less - * than the second element. The size of each object, (as computed by - * sizeof) is passed for elt_size. Note that the structure used for - * list elements must have as its final member a skip_elt_t - */ -cairo_private void -_cairo_skip_list_init (cairo_skip_list_t *list, - cairo_skip_list_compare_t compare, - size_t elt_size); - - -/* Deallocate resources associated with a skip list and all elements - * in it. (XXX: currently this simply deletes all elements.) - */ -cairo_private void -_cairo_skip_list_fini (cairo_skip_list_t *list); - -/* Insert a new element into the list at the correct sort order as - * determined by compare. If unique is true, then duplicate elements - * are ignored and the already inserted element is returned. - * Otherwise data will be copied (elt_size bytes from via - * memcpy) and the new element is returned. */ -cairo_private void * -_cairo_skip_list_insert (cairo_skip_list_t *list, void *data, int unique); - -/* Find an element which compare considers equal to */ -cairo_private void * -_cairo_skip_list_find (cairo_skip_list_t *list, void *data); - -/* Delete an element which compare considers equal to */ -cairo_private void -_cairo_skip_list_delete (cairo_skip_list_t *list, void *data); - -/* Delete the given element from the list. */ -cairo_private void -_cairo_skip_list_delete_given (cairo_skip_list_t *list, skip_elt_t *given); - -#endif diff --git a/gfx/cairo/cairo/src/cairo-skiplist.c b/gfx/cairo/cairo/src/cairo-skiplist.c deleted file mode 100644 index 18d69ca6cc7f..000000000000 --- a/gfx/cairo/cairo/src/cairo-skiplist.c +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Copyright © 2006 Keith Packard - * Copyright © 2006 Carl Worth - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that copyright - * notice and this permission notice appear in supporting documentation, and - * that the name of the copyright holders not be used in advertising or - * publicity pertaining to distribution of the software without specific, - * written prior permission. The copyright holders make no representations - * about the suitability of this software for any purpose. It is provided "as - * is" without express or implied warranty. - * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO - * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, - * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THIS SOFTWARE. - */ - -#include "cairoint.h" - -#include "cairo-skiplist-private.h" - -#if HAVE_FFS -#include /* ffs() */ -#endif - -#define ELT_DATA(elt) (void *) ((char*) (elt) - list->data_size) -#define NEXT_TO_ELT(next) (skip_elt_t *) ((char *) (next) - offsetof (skip_elt_t, next)) - -static uint32_t -hars_petruska_f54_1_random (void) -{ -# define rol(x,k) ((x << k) | (x >> (32-k))) - static uint32_t x = 0; - x = (x ^ rol(x, 5) ^ rol(x, 24)) + 0x37798849; - return x; -# undef rol -} - -struct pool { - struct pool *next; - char *ptr; - unsigned int rem; -}; - -static struct pool * -pool_new (void) -{ - struct pool *pool; - - pool = malloc (8192 - 8); - if (unlikely (pool == NULL)) - return NULL; - - pool->next = NULL; - pool->rem = 8192 - 8 - sizeof (struct pool); - pool->ptr = (char *) (pool + 1); - - return pool; -} - -static void -pools_destroy (struct pool *pool) -{ - while (pool->next != NULL) { - struct pool *next = pool->next; - free (pool); - pool = next; - } -} - -/* - * Initialize an empty skip list - */ -void -_cairo_skip_list_init (cairo_skip_list_t *list, - cairo_skip_list_compare_t compare, - size_t elt_size) -{ - int i; - - list->compare = compare; - list->elt_size = elt_size; - list->data_size = elt_size - sizeof (skip_elt_t); - list->pool = (struct pool *) list->pool_embedded; - list->pool->next = NULL; - list->pool->rem = sizeof (list->pool_embedded) - sizeof (struct pool); - list->pool->ptr = list->pool_embedded + sizeof (struct pool); - - for (i = 0; i < MAX_LEVEL; i++) { - list->chains[i] = NULL; - } - - for (i = 0; i < MAX_FREELIST_LEVEL; i++) { - list->freelists[i] = NULL; - } - - list->max_level = 0; -} - -void -_cairo_skip_list_fini (cairo_skip_list_t *list) -{ - pools_destroy (list->pool); -} - -/* - * Generate a random level number, distributed - * so that each level is 1/4 as likely as the one before - * - * Note that level numbers run 1 <= level < MAX_LEVEL - */ -static int -random_level (void) -{ - /* tricky bit -- each bit is '1' 75% of the time. - * This works because we only use the lower MAX_LEVEL - * bits, and MAX_LEVEL < 16 */ - uint32_t bits = hars_petruska_f54_1_random (); -#if HAVE_FFS - return ffs (-(1<> 16); -#else - int level = 1; - - bits |= -(1<> 16; - while ((bits & 1) == 0) { - level++; - bits >>= 1; - } - return level; -#endif -} - -static void * -pool_alloc (cairo_skip_list_t *list, - unsigned int level) -{ - unsigned int size; - struct pool *pool; - void *ptr; - - size = list->elt_size + - (FREELIST_MAX_LEVEL_FOR (level) - 1) * sizeof (skip_elt_t *); - - pool = list->pool; - if (size > pool->rem) { - pool = pool_new (); - if (unlikely (pool == NULL)) - return NULL; - - pool->next = list->pool; - list->pool = pool; - } - - ptr = pool->ptr; - pool->ptr += size; - pool->rem -= size; - - return ptr; -} - -static void * -alloc_node_for_level (cairo_skip_list_t *list, unsigned level) -{ - int freelist_level = FREELIST_FOR_LEVEL (level); - if (list->freelists[freelist_level]) { - skip_elt_t *elt = list->freelists[freelist_level]; - list->freelists[freelist_level] = elt->prev; - return ELT_DATA(elt); - } - return pool_alloc (list, level); -} - -static void -free_elt (cairo_skip_list_t *list, skip_elt_t *elt) -{ - int level = elt->prev_index + 1; - int freelist_level = FREELIST_FOR_LEVEL (level); - elt->prev = list->freelists[freelist_level]; - list->freelists[freelist_level] = elt; -} - -/* - * Insert 'data' into the list - */ -void * -_cairo_skip_list_insert (cairo_skip_list_t *list, void *data, int unique) -{ - skip_elt_t **update[MAX_LEVEL]; - skip_elt_t *prev[MAX_LEVEL]; - char *data_and_elt; - skip_elt_t *elt, **next; - int i, level, prev_index; - - /* - * Find links along each chain - */ - elt = NULL; - next = list->chains; - for (i = list->max_level; --i >= 0; ) - { - if (elt != next[i]) - { - for (; (elt = next[i]); next = elt->next) - { - int cmp = list->compare (list, ELT_DATA(elt), data); - if (unique && 0 == cmp) - return ELT_DATA(elt); - if (cmp > 0) - break; - } - } - update[i] = next; - if (next != list->chains) - prev[i] = NEXT_TO_ELT (next); - else - prev[i] = NULL; - } - level = random_level (); - prev_index = level - 1; - - /* - * Create new list element - */ - if (level > list->max_level) - { - level = list->max_level + 1; - prev_index = level - 1; - prev[prev_index] = NULL; - update[list->max_level] = list->chains; - list->max_level = level; - } - - data_and_elt = alloc_node_for_level (list, level); - if (unlikely (data_and_elt == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return NULL; - } - - memcpy (data_and_elt, data, list->data_size); - elt = (skip_elt_t *) (data_and_elt + list->data_size); - - elt->prev_index = prev_index; - elt->prev = prev[prev_index]; - - /* - * Insert into all chains - */ - for (i = 0; i < level; i++) - { - elt->next[i] = update[i][i]; - if (elt->next[i] && elt->next[i]->prev_index == i) - elt->next[i]->prev = elt; - update[i][i] = elt; - } - - return data_and_elt; -} - -void * -_cairo_skip_list_find (cairo_skip_list_t *list, void *data) -{ - int i; - skip_elt_t **next = list->chains; - skip_elt_t *elt; - - /* - * Walk chain pointers one level at a time - */ - for (i = list->max_level; --i >= 0;) - while (next[i] && list->compare (list, data, ELT_DATA(next[i])) > 0) - { - next = next[i]->next; - } - /* - * Here we are - */ - elt = next[0]; - if (elt && list->compare (list, data, ELT_DATA (elt)) == 0) - return ELT_DATA (elt); - - return NULL; -} - -void -_cairo_skip_list_delete (cairo_skip_list_t *list, void *data) -{ - skip_elt_t **update[MAX_LEVEL], *prev[MAX_LEVEL]; - skip_elt_t *elt, **next; - int i; - - /* - * Find links along each chain - */ - next = list->chains; - for (i = list->max_level; --i >= 0; ) - { - for (; (elt = next[i]); next = elt->next) - { - if (list->compare (list, ELT_DATA (elt), data) >= 0) - break; - } - update[i] = &next[i]; - if (next == list->chains) - prev[i] = NULL; - else - prev[i] = NEXT_TO_ELT (next); - } - elt = next[0]; - assert (list->compare (list, ELT_DATA (elt), data) == 0); - for (i = 0; i < list->max_level && *update[i] == elt; i++) { - *update[i] = elt->next[i]; - if (elt->next[i] && elt->next[i]->prev_index == i) - elt->next[i]->prev = prev[i]; - } - while (list->max_level > 0 && list->chains[list->max_level - 1] == NULL) - list->max_level--; - free_elt (list, elt); -} - -void -_cairo_skip_list_delete_given (cairo_skip_list_t *list, skip_elt_t *given) -{ - skip_elt_t **update[MAX_LEVEL], *prev[MAX_LEVEL]; - skip_elt_t *elt, **next; - int i; - - /* - * Find links along each chain - */ - if (given->prev) - next = given->prev->next; - else - next = list->chains; - for (i = given->prev_index + 1; --i >= 0; ) - { - for (; (elt = next[i]); next = elt->next) - { - if (elt == given) - break; - } - update[i] = &next[i]; - if (next == list->chains) - prev[i] = NULL; - else - prev[i] = NEXT_TO_ELT (next); - } - elt = next[0]; - assert (elt == given); - for (i = 0; i < (given->prev_index + 1) && *update[i] == elt; i++) { - *update[i] = elt->next[i]; - if (elt->next[i] && elt->next[i]->prev_index == i) - elt->next[i]->prev = prev[i]; - } - while (list->max_level > 0 && list->chains[list->max_level - 1] == NULL) - list->max_level--; - free_elt (list, elt); -} - -#if MAIN -typedef struct { - int n; - skip_elt_t elt; -} test_elt_t; - -static int -test_cmp (void *list, void *A, void *B) -{ - const test_elt_t *a = A, *b = B; - return a->n - b->n; -} - -int -main (void) -{ - cairo_skip_list_t list; - test_elt_t elt; - int n; - - _cairo_skip_list_init (&list, test_cmp, sizeof (test_elt_t)); - for (n = 0; n < 10000000; n++) { - void *elt_and_data; - elt.n = n; - elt_and_data = _cairo_skip_list_insert (&list, &elt, TRUE); - assert (elt_and_data != NULL); - } - _cairo_skip_list_fini (&list); - - return 0; -} - -/* required supporting stubs */ -cairo_status_t _cairo_error (cairo_status_t status) { return status; } -#endif diff --git a/gfx/cairo/cairo/src/cairo-slope-private.h b/gfx/cairo/cairo/src/cairo-slope-private.h new file mode 100644 index 000000000000..64f0dde381d1 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-slope-private.h @@ -0,0 +1,72 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2005 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + */ + +#ifndef _CAIRO_SLOPE_PRIVATE_H +#define _CAIRO_SLOPE_PRIVATE_H + +#include "cairo-types-private.h" +#include "cairo-fixed-private.h" + +static inline void +_cairo_slope_init (cairo_slope_t *slope, + const cairo_point_t *a, + const cairo_point_t *b) +{ + slope->dx = b->x - a->x; + slope->dy = b->y - a->y; +} + +static inline cairo_bool_t +_cairo_slope_equal (const cairo_slope_t *a, const cairo_slope_t *b) +{ + return _cairo_int64_eq (_cairo_int32x32_64_mul (a->dy, b->dx), + _cairo_int32x32_64_mul (b->dy, a->dx)); +} + +static inline cairo_bool_t +_cairo_slope_backwards (const cairo_slope_t *a, const cairo_slope_t *b) +{ + return _cairo_int64_negative (_cairo_int64_add (_cairo_int32x32_64_mul (a->dx, b->dx), + _cairo_int32x32_64_mul (a->dy, b->dy))); +} + +cairo_private int +_cairo_slope_compare (const cairo_slope_t *a, + const cairo_slope_t *b) cairo_pure; + + +#endif /* _CAIRO_SLOPE_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-slope.c b/gfx/cairo/cairo/src/cairo-slope.c index 35c537221e87..bb3b411e6123 100644 --- a/gfx/cairo/cairo/src/cairo-slope.c +++ b/gfx/cairo/cairo/src/cairo-slope.c @@ -36,14 +36,7 @@ #include "cairoint.h" -void -_cairo_slope_init (cairo_slope_t *slope, - const cairo_point_t *a, - const cairo_point_t *b) -{ - slope->dx = b->x - a->x; - slope->dy = b->y - a->y; -} +#include "cairo-slope-private.h" /* Compare two slopes. Slope angles begin at 0 in the direction of the positive X axis and increase in the direction of the positive Y @@ -94,9 +87,7 @@ _cairo_slope_compare (const cairo_slope_t *a, const cairo_slope_t *b) * of b by an infinitesimally small amount, (that is, 'a' will * always be considered less than 'b'). */ - if (((a->dx > 0) != (b->dx > 0)) || - ((a->dy > 0) != (b->dy > 0))) - { + if ((a->dx ^ b->dx) < 0 || (a->dy ^ b->dy) < 0) { if (a->dx > 0 || (a->dx == 0 && a->dy > 0)) return +1; else diff --git a/gfx/cairo/cairo/src/cairo-spans-private.h b/gfx/cairo/cairo/src/cairo-spans-private.h index c285f94959f2..af3b38ca9e02 100644 --- a/gfx/cairo/cairo/src/cairo-spans-private.h +++ b/gfx/cairo/cairo/src/cairo-spans-private.h @@ -47,26 +47,24 @@ typedef struct _cairo_half_open_span { * surfaces if they want to composite spans instead of trapezoids. */ typedef struct _cairo_span_renderer cairo_span_renderer_t; struct _cairo_span_renderer { + /* Private status variable. */ + cairo_status_t status; + /* Called to destroy the renderer. */ cairo_destroy_func_t destroy; - /* Render the spans on row y of the source by whatever compositing - * method is required. The function should ignore spans outside - * the bounding box set by the init() function. */ - cairo_status_t (*render_row)( - void *abstract_renderer, - int y, - const cairo_half_open_span_t *coverages, - unsigned num_coverages); + /* Render the spans on row y of the destination by whatever compositing + * method is required. */ + cairo_warn cairo_status_t + (*render_rows) (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *coverages, + unsigned num_coverages); /* Called after all rows have been rendered to perform whatever * final rendering step is required. This function is called just * once before the renderer is destroyed. */ - cairo_status_t (*finish)( - void *abstract_renderer); - - /* Private status variable. */ - cairo_status_t status; + cairo_status_t (*finish) (void *abstract_renderer); }; /* Scan converter interface. */ @@ -75,22 +73,22 @@ struct _cairo_scan_converter { /* Destroy this scan converter. */ cairo_destroy_func_t destroy; - /* Add an edge to the converter. */ - cairo_status_t - (*add_edge)( - void *abstract_converter, - cairo_fixed_t x1, - cairo_fixed_t y1, - cairo_fixed_t x2, - cairo_fixed_t y2); + /* Add a single edge to the converter. */ + cairo_status_t (*add_edge) (void *abstract_converter, + const cairo_point_t *p1, + const cairo_point_t *p2, + int top, int bottom, + int dir); + + /* Add a polygon (set of edges) to the converter. */ + cairo_status_t (*add_polygon) (void *abstract_converter, + const cairo_polygon_t *polygon); /* Generates coverage spans for rows for the added edges and calls * the renderer function for each row. After generating spans the * only valid thing to do with the converter is to destroy it. */ - cairo_status_t - (*generate)( - void *abstract_converter, - cairo_span_renderer_t *renderer); + cairo_status_t (*generate) (void *abstract_converter, + cairo_span_renderer_t *renderer); /* Private status. Read with _cairo_scan_converter_status(). */ cairo_status_t status; @@ -99,12 +97,11 @@ struct _cairo_scan_converter { /* Scan converter constructors. */ cairo_private cairo_scan_converter_t * -_cairo_tor_scan_converter_create( - int xmin, - int ymin, - int xmax, - int ymax, - cairo_fill_rule_t fill_rule); +_cairo_tor_scan_converter_create (int xmin, + int ymin, + int xmax, + int ymax, + cairo_fill_rule_t fill_rule); /* cairo-spans.c: */ @@ -132,13 +129,25 @@ _cairo_span_renderer_set_error (void *abstract_renderer, cairo_status_t error); cairo_private cairo_status_t -_cairo_path_fixed_fill_using_spans ( - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_path_fixed_t *path, - cairo_surface_t *dst, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects); +_cairo_surface_composite_polygon (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects, + cairo_polygon_t *polygon, + cairo_region_t *clip_region); + +cairo_private cairo_status_t +_cairo_surface_composite_trapezoids_as_polygon (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_antialias_t antialias, + int src_x, int src_y, + int dst_x, int dst_y, + int width, int height, + cairo_trapezoid_t *traps, + int num_traps, + cairo_region_t *clip_region); + #endif /* CAIRO_SPANS_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-spans.c b/gfx/cairo/cairo/src/cairo-spans.c index cf8f767764ab..69894c11f50d 100644 --- a/gfx/cairo/cairo/src/cairo-spans.c +++ b/gfx/cairo/cairo/src/cairo-spans.c @@ -26,107 +26,7 @@ */ #include "cairoint.h" -typedef struct { - cairo_scan_converter_t *converter; - cairo_point_t current_point; - cairo_point_t first_point; - cairo_bool_t has_first_point; -} scan_converter_filler_t; - -static void -scan_converter_filler_init (scan_converter_filler_t *filler, - cairo_scan_converter_t *converter) -{ - filler->converter = converter; - filler->current_point.x = 0; - filler->current_point.y = 0; - filler->first_point = filler->current_point; - filler->has_first_point = FALSE; -} - -static cairo_status_t -scan_converter_filler_close_path (void *closure) -{ - scan_converter_filler_t *filler = closure; - cairo_status_t status; - - filler->has_first_point = FALSE; - - if (filler->first_point.x == filler->current_point.x && - filler->first_point.y == filler->current_point.y) - { - return CAIRO_STATUS_SUCCESS; - } - - status = filler->converter->add_edge ( - filler->converter, - filler->current_point.x, filler->current_point.y, - filler->first_point.x, filler->first_point.y); - - filler->current_point = filler->first_point; - return status; -} - -static cairo_status_t -scan_converter_filler_move_to (void *closure, - const cairo_point_t *p) -{ - scan_converter_filler_t *filler = closure; - if (filler->has_first_point) { - cairo_status_t status = scan_converter_filler_close_path (closure); - if (status) - return status; - } - filler->current_point.x = p->x; - filler->current_point.y = p->y; - filler->first_point = filler->current_point; - filler->has_first_point = TRUE; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -scan_converter_filler_line_to (void *closure, - const cairo_point_t *p) -{ - scan_converter_filler_t *filler = closure; - cairo_status_t status; - cairo_point_t to; - - to.x = p->x; - to.y = p->y; - - status = filler->converter->add_edge ( - filler->converter, - filler->current_point.x, filler->current_point.y, - to.x, to.y); - - filler->current_point = to; - - return status; -} - -static cairo_status_t -_cairo_path_fixed_fill_to_scan_converter ( - cairo_path_fixed_t *path, - double tolerance, - cairo_scan_converter_t *converter) -{ - scan_converter_filler_t filler; - cairo_status_t status; - - scan_converter_filler_init (&filler, converter); - - status = _cairo_path_fixed_interpret_flat ( - path, CAIRO_DIRECTION_FORWARD, - scan_converter_filler_move_to, - scan_converter_filler_line_to, - scan_converter_filler_close_path, - &filler, tolerance); - if (status) - return status; - - return scan_converter_filler_close_path (&filler); -} +#include "cairo-fixed-private.h" static cairo_scan_converter_t * _create_scan_converter (cairo_fill_rule_t fill_rule, @@ -135,51 +35,115 @@ _create_scan_converter (cairo_fill_rule_t fill_rule, { if (antialias == CAIRO_ANTIALIAS_NONE) { ASSERT_NOT_REACHED; - return _cairo_scan_converter_create_in_error ( - CAIRO_INT_STATUS_UNSUPPORTED); - } - else { - return _cairo_tor_scan_converter_create ( - rects->mask.x, - rects->mask.y, - rects->mask.x + rects->width, - rects->mask.y + rects->height, - fill_rule); + return NULL; } + + return _cairo_tor_scan_converter_create (rects->mask.x, + rects->mask.y, + rects->mask.x + rects->width, + rects->mask.y + rects->height, + fill_rule); +} + +/* XXX Add me to the compositor interface. Ok, first create the compositor + * interface, and then add this with associated fallback! + */ +cairo_status_t +_cairo_surface_composite_polygon (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects, + cairo_polygon_t *polygon, + cairo_region_t *clip_region) +{ + cairo_span_renderer_t *renderer; + cairo_scan_converter_t *converter; + cairo_status_t status; + + converter = _create_scan_converter (fill_rule, antialias, rects); + status = converter->add_polygon (converter, polygon); + if (unlikely (status)) + goto CLEANUP_CONVERTER; + + renderer = _cairo_surface_create_span_renderer (op, pattern, surface, + antialias, rects, + clip_region); + status = converter->generate (converter, renderer); + if (unlikely (status)) + goto CLEANUP_RENDERER; + + status = renderer->finish (renderer); + + CLEANUP_RENDERER: + renderer->destroy (renderer); + CLEANUP_CONVERTER: + converter->destroy (converter); + return status; } cairo_status_t -_cairo_path_fixed_fill_using_spans ( - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_path_fixed_t *path, - cairo_surface_t *dst, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects) +_cairo_surface_composite_trapezoids_as_polygon (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_antialias_t antialias, + int src_x, int src_y, + int dst_x, int dst_y, + int width, int height, + cairo_trapezoid_t *traps, + int num_traps, + cairo_region_t *clip_region) { + cairo_span_renderer_t *renderer; + cairo_scan_converter_t *converter; + cairo_composite_rectangles_t rects; cairo_status_t status; - cairo_span_renderer_t *renderer = _cairo_surface_create_span_renderer ( - op, pattern, dst, antialias, rects); - cairo_scan_converter_t *converter = _create_scan_converter ( - fill_rule, antialias, rects); - status = _cairo_path_fixed_fill_to_scan_converter ( - path, tolerance, converter); - if (status) - goto BAIL; + rects.src.x = src_x; + rects.src.y = src_y; + rects.dst.x = dst_x; + rects.dst.y = dst_y; + rects.mask.x = dst_x; + rects.mask.y = dst_y; + rects.width = width; + rects.height = height; + converter = _create_scan_converter (CAIRO_FILL_RULE_WINDING, + antialias, + &rects); + status = converter->status; + if (unlikely (status)) + goto CLEANUP_CONVERTER; + + while (num_traps--) { + status = converter->add_edge (converter, + &traps->left.p1, &traps->left.p2, + traps->top, traps->bottom, 1); + if (unlikely (status)) + goto CLEANUP_CONVERTER; + + status = converter->add_edge (converter, + &traps->right.p1, &traps->right.p2, + traps->top, traps->bottom, -1); + if (unlikely (status)) + goto CLEANUP_CONVERTER; + + traps++; + } + + renderer = _cairo_surface_create_span_renderer (op, pattern, surface, + antialias, &rects, + clip_region); status = converter->generate (converter, renderer); - if (status) - goto BAIL; + if (unlikely (status)) + goto CLEANUP_RENDERER; status = renderer->finish (renderer); - if (status) - goto BAIL; - BAIL: + CLEANUP_RENDERER: renderer->destroy (renderer); + CLEANUP_CONVERTER: converter->destroy (converter); return status; } @@ -191,17 +155,27 @@ _cairo_nil_destroy (void *abstract) } static cairo_status_t -_cairo_nil_scan_converter_add_edge (void *abstract_converter, - cairo_fixed_t x1, - cairo_fixed_t y1, - cairo_fixed_t x2, - cairo_fixed_t y2) +_cairo_nil_scan_converter_add_polygon (void *abstract_converter, + const cairo_polygon_t *polygon) { (void) abstract_converter; - (void) x1; - (void) y1; - (void) x2; - (void) y2; + (void) polygon; + return _cairo_scan_converter_status (abstract_converter); +} + +static cairo_status_t +_cairo_nil_scan_converter_add_edge (void *abstract_converter, + const cairo_point_t *p1, + const cairo_point_t *p2, + int top, int bottom, + int dir) +{ + (void) abstract_converter; + (void) p1; + (void) p2; + (void) top; + (void) bottom; + (void) dir; return _cairo_scan_converter_status (abstract_converter); } @@ -229,6 +203,7 @@ _cairo_scan_converter_set_error (void *abstract_converter, if (error == CAIRO_STATUS_SUCCESS) ASSERT_NOT_REACHED; if (converter->status == CAIRO_STATUS_SUCCESS) { + converter->add_polygon = _cairo_nil_scan_converter_add_polygon; converter->add_edge = _cairo_nil_scan_converter_add_edge; converter->generate = _cairo_nil_scan_converter_generate; converter->status = error; @@ -300,13 +275,15 @@ _cairo_scan_converter_create_in_error (cairo_status_t status) } static cairo_status_t -_cairo_nil_span_renderer_render_row ( +_cairo_nil_span_renderer_render_rows ( void *abstract_renderer, int y, + int height, const cairo_half_open_span_t *coverages, unsigned num_coverages) { (void) y; + (void) height; (void) coverages; (void) num_coverages; return _cairo_span_renderer_status (abstract_renderer); @@ -335,7 +312,7 @@ _cairo_span_renderer_set_error ( ASSERT_NOT_REACHED; } if (renderer->status == CAIRO_STATUS_SUCCESS) { - renderer->render_row = _cairo_nil_span_renderer_render_row; + renderer->render_rows = _cairo_nil_span_renderer_render_rows; renderer->finish = _cairo_nil_span_renderer_finish; renderer->status = error; } diff --git a/gfx/cairo/cairo/src/cairo-spline.c b/gfx/cairo/cairo/src/cairo-spline.c index 45eedbd6aa1e..639d9bdb7058 100644 --- a/gfx/cairo/cairo/src/cairo-spline.c +++ b/gfx/cairo/cairo/src/cairo-spline.c @@ -36,6 +36,8 @@ #include "cairoint.h" +#include "cairo-slope-private.h" + cairo_bool_t _cairo_spline_init (cairo_spline_t *spline, cairo_spline_add_point_func_t add_point_func, @@ -122,12 +124,10 @@ _cairo_spline_error_squared (const cairo_spline_knots_t *knots) double bdx, bdy, berr; double cdx, cdy, cerr; - /* Intersection point (px): - * px = p1 + u(p2 - p1) - * (p - px) ∙ (p2 - p1) = 0 - * Thus: - * u = ((p - p1) ∙ (p2 - p1)) / ∥p2 - p1∥²; - */ + /* We are going to compute the distance (squared) between each of the the b + * and c control points and the segment a-b. The maximum of these two + * distances will be our approximation error. */ + bdx = _cairo_fixed_to_double (knots->b.x - knots->a.x); bdy = _cairo_fixed_to_double (knots->b.y - knots->a.y); @@ -135,6 +135,13 @@ _cairo_spline_error_squared (const cairo_spline_knots_t *knots) cdy = _cairo_fixed_to_double (knots->c.y - knots->a.y); if (knots->a.x != knots->d.x || knots->a.y != knots->d.y) { + /* Intersection point (px): + * px = p1 + u(p2 - p1) + * (p - px) ∙ (p2 - p1) = 0 + * Thus: + * u = ((p - p1) ∙ (p2 - p1)) / ∥p2 - p1∥²; + */ + double dx, dy, u, v; dx = _cairo_fixed_to_double (knots->d.x - knots->a.x); diff --git a/gfx/cairo/cairo/src/cairo-stroke-style.c b/gfx/cairo/cairo/src/cairo-stroke-style.c index 9ab91e535a00..36afeeb017ee 100644 --- a/gfx/cairo/cairo/src/cairo-stroke-style.c +++ b/gfx/cairo/cairo/src/cairo-stroke-style.c @@ -110,13 +110,132 @@ _cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style, style_expansion = M_SQRT1_2; if (style->line_join == CAIRO_LINE_JOIN_MITER && - style_expansion < style->miter_limit) + style_expansion < M_SQRT2 * style->miter_limit) { - style_expansion = style->miter_limit; + style_expansion = M_SQRT2 * style->miter_limit; } style_expansion *= style->line_width; - *dx = style_expansion * (fabs (ctm->xx) + fabs (ctm->xy)); - *dy = style_expansion * (fabs (ctm->yy) + fabs (ctm->yx)); + *dx = style_expansion * hypot (ctm->xx, ctm->xy); + *dy = style_expansion * hypot (ctm->yy, ctm->yx); +} + +/* + * Computes the period of a dashed stroke style. + * Returns 0 for non-dashed styles. + */ +double +_cairo_stroke_style_dash_period (const cairo_stroke_style_t *style) +{ + double period; + unsigned int i; + + period = 0.0; + for (i = 0; i < style->num_dashes; i++) + period += style->dash[i]; + + if (style->num_dashes & 1) + period *= 2.0; + + return period; +} + +/* + * Coefficient of the linear approximation (minimizing square difference) + * of the surface covered by round caps + */ +#define ROUND_MINSQ_APPROXIMATION (9*M_PI/32) + +/* + * Computes the length of the "on" part of a dashed stroke style, + * taking into account also line caps. + * Returns 0 for non-dashed styles. + */ +double +_cairo_stroke_style_dash_stroked (const cairo_stroke_style_t *style) +{ + double stroked, cap_scale; + unsigned int i; + + switch (style->line_cap) { + default: ASSERT_NOT_REACHED; + case CAIRO_LINE_CAP_BUTT: cap_scale = 0.0; break; + case CAIRO_LINE_CAP_ROUND: cap_scale = ROUND_MINSQ_APPROXIMATION; break; + case CAIRO_LINE_CAP_SQUARE: cap_scale = 1.0; break; + } + + stroked = 0.0; + if (style->num_dashes & 1) { + /* Each dash element is used both as on and as off. The order in which they are summed is + * irrelevant, so sum the coverage of one dash element, taken both on and off at each iteration */ + for (i = 0; i < style->num_dashes; i++) + stroked += style->dash[i] + cap_scale * MIN (style->dash[i], style->line_width); + } else { + /* Even (0, 2, ...) dashes are on and simply counted for the coverage, odd dashes are off, thus + * their coverage is approximated based on the area covered by the caps of adjacent on dases. */ + for (i = 0; i < style->num_dashes; i+=2) + stroked += style->dash[i] + cap_scale * MIN (style->dash[i+1], style->line_width); + } + + return stroked; +} + +/* + * Verifies if _cairo_stroke_style_dash_approximate should be used to generate + * an approximation of the dash pattern in the specified style, when used for + * stroking a path with the given CTM and tolerance. + * Always %FALSE for non-dashed styles. + */ +cairo_bool_t +_cairo_stroke_style_dash_can_approximate (const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + double tolerance) +{ + double period; + + if (! style->num_dashes) + return FALSE; + + period = _cairo_stroke_style_dash_period (style); + return _cairo_matrix_transformed_circle_major_axis (ctm, period) < tolerance; +} + +/* + * Create a 2-dashes approximation of a dashed style, by making the "on" and "off" + * parts respect the original ratio. + */ +void +_cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + double tolerance, + double *dash_offset, + double *dashes, + unsigned int *num_dashes) +{ + double coverage, scale, offset; + cairo_bool_t on = TRUE; + unsigned int i = 0; + + coverage = _cairo_stroke_style_dash_stroked (style) / _cairo_stroke_style_dash_period (style); + coverage = MIN (coverage, 1.0); + scale = tolerance / _cairo_matrix_transformed_circle_major_axis (ctm, 1.0); + + /* We stop searching for a starting point as soon as the + offset reaches zero. Otherwise when an initial dash + segment shrinks to zero it will be skipped over. */ + offset = style->dash_offset; + while (offset > 0.0 && offset >= style->dash[i]) { + offset -= style->dash[i]; + on = !on; + if (++i == style->num_dashes) + i = 0; + } + + *num_dashes = 2; + + dashes[0] = scale * coverage; + dashes[1] = scale * (1.0 - coverage); + + *dash_offset = on ? 0.0 : dashes[0]; } diff --git a/gfx/cairo/cairo/src/cairo-supported-features.h b/gfx/cairo/cairo/src/cairo-supported-features.h new file mode 100644 index 000000000000..676b61e19cf3 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-supported-features.h @@ -0,0 +1,29 @@ +/* Generated by configure. Do not edit. */ +#ifndef CAIRO_SUPPORTED_FEATURES_H +#define CAIRO_SUPPORTED_FEATURES_H + +/* This is a dummy header, to trick gtk-doc only */ + +#define CAIRO_HAS_XLIB_SURFACE 1 +#define CAIRO_HAS_XLIB_XRENDER_SURFACE 1 +#define CAIRO_HAS_XCB_SHM_FUNCTIONS 1 +#define CAIRO_HAS_QUARTZ_SURFACE 1 +#define CAIRO_HAS_QUARTZ_FONT 1 +#define CAIRO_HAS_WIN32_SURFACE 1 +#define CAIRO_HAS_WIN32_FONT 1 +#define CAIRO_HAS_PNG_FUNCTIONS 1 +#define CAIRO_HAS_EAGLE_FUNCTIONS 1 +#define CAIRO_HAS_EGL_FUNCTIONS 1 +#define CAIRO_HAS_GLX_FUNCTIONS 1 +#define CAIRO_HAS_FT_FONT 1 +#define CAIRO_HAS_FC_FONT 1 +#define CAIRO_HAS_PS_SURFACE 1 +#define CAIRO_HAS_PDF_SURFACE 1 +#define CAIRO_HAS_SVG_SURFACE 1 +#define CAIRO_HAS_IMAGE_SURFACE 1 +#define CAIRO_HAS_RECORDING_SURFACE 1 +#define CAIRO_HAS_TEE_SURFACE 1 +#define CAIRO_HAS_XML_SURFACE 1 +#define CAIRO_HAS_USER_FONT 1 + +#endif diff --git a/gfx/cairo/cairo/src/cairo-surface-clipper-private.h b/gfx/cairo/cairo/src/cairo-surface-clipper-private.h new file mode 100644 index 000000000000..f3d9de189391 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-clipper-private.h @@ -0,0 +1,72 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_SURFACE_CLIPPER_PRIVATE_H +#define CAIRO_SURFACE_CLIPPER_PRIVATE_H + +#include "cairo-types-private.h" +#include "cairo-clip-private.h" + +CAIRO_BEGIN_DECLS + +typedef struct _cairo_surface_clipper cairo_surface_clipper_t; + +typedef cairo_status_t +(*cairo_surface_clipper_intersect_clip_path_func_t) (cairo_surface_clipper_t *, + cairo_path_fixed_t *, + cairo_fill_rule_t, + double, + cairo_antialias_t); +struct _cairo_surface_clipper { + cairo_clip_t clip; + cairo_bool_t is_clipped; + cairo_surface_clipper_intersect_clip_path_func_t intersect_clip_path; +}; + +cairo_private cairo_status_t +_cairo_surface_clipper_set_clip (cairo_surface_clipper_t *clipper, + cairo_clip_t *clip); + +cairo_private void +_cairo_surface_clipper_init (cairo_surface_clipper_t *clipper, + cairo_surface_clipper_intersect_clip_path_func_t intersect); + +cairo_private void +_cairo_surface_clipper_reset (cairo_surface_clipper_t *clipper); + +CAIRO_END_DECLS + +#endif /* CAIRO_SURFACE_CLIPPER_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-surface-clipper.c b/gfx/cairo/cairo/src/cairo-surface-clipper.c new file mode 100644 index 000000000000..03610d165583 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-clipper.c @@ -0,0 +1,132 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-surface-clipper-private.h" + +/* A collection of routines to facilitate vector surface clipping */ + +static cairo_status_t +_cairo_surface_clipper_intersect_clip_path_recursive (cairo_surface_clipper_t *clipper, + cairo_clip_path_t *clip_path) +{ + cairo_status_t status; + + if (clip_path->prev != NULL) { + status = + _cairo_surface_clipper_intersect_clip_path_recursive (clipper, + clip_path->prev); + if (unlikely (status)) + return status; + } + + return clipper->intersect_clip_path (clipper, + &clip_path->path, + clip_path->fill_rule, + clip_path->tolerance, + clip_path->antialias); +} + +cairo_status_t +_cairo_surface_clipper_set_clip (cairo_surface_clipper_t *clipper, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_bool_t clear; + + /* XXX as we cache a reference to the path, and compare every time, + * we may in future need to install a notification if the clip->path + * is every modified (e.g. cairo_clip_translate). + */ + + if (clip == NULL && clipper->clip.path == NULL) + return CAIRO_STATUS_SUCCESS; + + if (clip != NULL && clip->path == clipper->clip.path) + return CAIRO_STATUS_SUCCESS; + + /* all clipped out state should never propagate this far */ + assert (clip == NULL || clip->path != NULL); + + /* Check whether this clip is a continuation of the previous. + * If not, we have to remove the current clip and rebuild. + */ + clear = clip == NULL || clip->path->prev != clipper->clip.path; + + _cairo_clip_reset (&clipper->clip); + _cairo_clip_init_copy (&clipper->clip, clip); + + if (clear) { + clipper->is_clipped = FALSE; + status = clipper->intersect_clip_path (clipper, NULL, 0, 0, 0); + if (unlikely (status)) + return status; + + if (clip != NULL && clip->path != NULL) { + status = + _cairo_surface_clipper_intersect_clip_path_recursive (clipper, + clip->path); + clipper->is_clipped = TRUE; + } + } else { + cairo_clip_path_t *path = clip->path; + + clipper->is_clipped = TRUE; + status = clipper->intersect_clip_path (clipper, + &path->path, + path->fill_rule, + path->tolerance, + path->antialias); + } + + return status; +} + +void +_cairo_surface_clipper_init (cairo_surface_clipper_t *clipper, + cairo_surface_clipper_intersect_clip_path_func_t func) +{ + _cairo_clip_init (&clipper->clip); + clipper->is_clipped = FALSE; + clipper->intersect_clip_path = func; +} + +void +_cairo_surface_clipper_reset (cairo_surface_clipper_t *clipper) +{ + _cairo_clip_reset (&clipper->clip); + clipper->is_clipped = FALSE; +} diff --git a/gfx/cairo/cairo/src/cairo-surface-fallback-private.h b/gfx/cairo/cairo/src/cairo-surface-fallback-private.h index cd1817809b49..c63c36e74957 100644 --- a/gfx/cairo/cairo/src/cairo-surface-fallback-private.h +++ b/gfx/cairo/cairo/src/cairo-surface-fallback-private.h @@ -44,13 +44,15 @@ cairo_private cairo_status_t _cairo_surface_fallback_paint (cairo_surface_t *surface, cairo_operator_t op, - const cairo_pattern_t *source); + const cairo_pattern_t *source, + cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_fallback_mask (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, - const cairo_pattern_t *mask); + const cairo_pattern_t *mask, + cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_fallback_stroke (cairo_surface_t *surface, @@ -61,7 +63,8 @@ _cairo_surface_fallback_stroke (cairo_surface_t *surface, cairo_matrix_t *ctm, cairo_matrix_t *ctm_inverse, double tolerance, - cairo_antialias_t antialias); + cairo_antialias_t antialias, + cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_fallback_fill (cairo_surface_t *surface, @@ -70,7 +73,8 @@ _cairo_surface_fallback_fill (cairo_surface_t *surface, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, - cairo_antialias_t antialias); + cairo_antialias_t antialias, + cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_fallback_show_glyphs (cairo_surface_t *surface, @@ -78,7 +82,8 @@ _cairo_surface_fallback_show_glyphs (cairo_surface_t *surface, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, - cairo_scaled_font_t *scaled_font); + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip); cairo_private cairo_surface_t * _cairo_surface_fallback_snapshot (cairo_surface_t *surface); @@ -95,7 +100,8 @@ _cairo_surface_fallback_composite (cairo_operator_t op, int dst_x, int dst_y, unsigned int width, - unsigned int height); + unsigned int height, + cairo_region_t *clip_region); cairo_private cairo_status_t _cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface, @@ -116,12 +122,12 @@ _cairo_surface_fallback_composite_trapezoids (cairo_operator_t op, unsigned int width, unsigned int height, cairo_trapezoid_t *traps, - int num_traps); + int num_traps, + cairo_region_t *clip_region); cairo_private cairo_status_t _cairo_surface_fallback_clone_similar (cairo_surface_t *surface, cairo_surface_t *src, - cairo_content_t content, int src_x, int src_y, int width, diff --git a/gfx/cairo/cairo/src/cairo-surface-fallback.c b/gfx/cairo/cairo/src/cairo-surface-fallback.c index 830c1b3de9f2..316558d0be53 100644 --- a/gfx/cairo/cairo/src/cairo-surface-fallback.c +++ b/gfx/cairo/cairo/src/cairo-surface-fallback.c @@ -34,12 +34,16 @@ * * Contributor(s): * Carl D. Worth + * Joonas Pihlaja + * Chris Wilson */ #include "cairoint.h" #include "cairo-surface-fallback-private.h" #include "cairo-clip-private.h" +#include "cairo-region-private.h" +#include "cairo-spans-private.h" typedef struct { cairo_surface_t *dst; @@ -82,13 +86,13 @@ _fallback_init (fallback_state_t *state, if (unlikely (status)) return status; + /* XXX: This NULL value tucked away in state->image is a rather * ugly interface. Cleaner would be to push the * CAIRO_INT_STATUS_NOTHING_TO_DO value down into * _cairo_surface_acquire_dest_image and its backend * counterparts. */ - if (state->image == NULL) - return CAIRO_INT_STATUS_NOTHING_TO_DO; + assert (state->image != NULL); return CAIRO_STATUS_SUCCESS; } @@ -101,46 +105,66 @@ _fallback_fini (fallback_state_t *state) state->image_extra); } -typedef cairo_status_t (*cairo_draw_func_t) (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents); +typedef cairo_status_t +(*cairo_draw_func_t) (void *closure, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_surface_t *dst, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region); static cairo_status_t _create_composite_mask_pattern (cairo_surface_pattern_t *mask_pattern, cairo_clip_t *clip, - cairo_draw_func_t draw_func, + cairo_draw_func_t draw_func, void *draw_closure, cairo_surface_t *dst, const cairo_rectangle_int_t *extents) { cairo_surface_t *mask; + cairo_region_t *clip_region = NULL; + cairo_solid_pattern_t solid; cairo_status_t status; + cairo_bool_t clip_surface = FALSE; - mask = cairo_surface_create_similar (dst, - CAIRO_CONTENT_ALPHA, - extents->width, - extents->height); - if (mask->status) + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (! _cairo_status_is_error (status)); + + /* The all-clipped state should never propagate this far. */ + assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); + + clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + + if (clip_region && cairo_region_num_rectangles (clip_region) == 1) + clip_region = NULL; + } + + /* We need to use solid here, because to use CAIRO_OPERATOR_SOURCE with + * a mask (as called via _cairo_surface_mask) triggers assertion failures. + */ + mask = _cairo_surface_create_similar_solid (dst, + CAIRO_CONTENT_ALPHA, + extents->width, + extents->height, + CAIRO_COLOR_TRANSPARENT, + TRUE); + if (unlikely (mask->status)) return mask->status; - status = (*draw_func) (draw_closure, CAIRO_OPERATOR_ADD, - NULL, mask, - extents->x, extents->y, - extents); + _cairo_pattern_init_solid (&solid, CAIRO_COLOR_WHITE, CAIRO_CONTENT_ALPHA); + status = draw_func (draw_closure, CAIRO_OPERATOR_ADD, + &solid.base, mask, + extents->x, extents->y, + extents, + clip_region); if (unlikely (status)) goto CLEANUP_SURFACE; - if (clip && clip->surface) - status = _cairo_clip_combine_to_surface (clip, CAIRO_OPERATOR_IN, - mask, - extents->x, extents->y, - extents); - if (unlikely (status)) - goto CLEANUP_SURFACE; + if (clip_surface) + status = _cairo_clip_combine_with_surface (clip, mask, extents); _cairo_pattern_init_for_surface (mask_pattern, mask); @@ -169,17 +193,17 @@ _clip_and_composite_with_mask (cairo_clip_t *clip, clip, draw_func, draw_closure, dst, extents); - if (unlikely (status)) - return status; + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _cairo_surface_composite (op, + src, &mask_pattern.base, dst, + extents->x, extents->y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height, + NULL); - status = _cairo_surface_composite (op, - src, &mask_pattern.base, dst, - extents->x, extents->y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height); - - _cairo_pattern_fini (&mask_pattern.base); + _cairo_pattern_fini (&mask_pattern.base); + } return status; } @@ -197,77 +221,97 @@ _clip_and_composite_combine (cairo_clip_t *clip, const cairo_rectangle_int_t *extents) { cairo_surface_t *intermediate; - cairo_surface_pattern_t dst_pattern; - cairo_surface_pattern_t intermediate_pattern; + cairo_surface_pattern_t pattern; + cairo_surface_pattern_t clip_pattern; + cairo_surface_t *clip_surface; cairo_status_t status; /* We'd be better off here creating a surface identical in format - * to dst, but we have no way of getting that information. - * A CAIRO_CONTENT_CLONE or something might be useful. - * cairo_surface_create_similar() also unnecessarily clears the surface. + * to dst, but we have no way of getting that information. Instead + * we ask the backend to create a similar surface of identical content, + * in the belief that the backend will do something useful - like use + * an identical format. For example, the xlib backend will endeavor to + * use a compatible depth to enable core protocol routines. */ - intermediate = cairo_surface_create_similar (dst, - CAIRO_CONTENT_COLOR_ALPHA, - extents->width, - extents->height); - if (intermediate->status) + intermediate = + _cairo_surface_create_similar_scratch (dst, dst->content, + extents->width, + extents->height); + if (intermediate == NULL) { + intermediate = + _cairo_image_surface_create_with_content (dst->content, + extents->width, + extents->width); + } + if (unlikely (intermediate->status)) return intermediate->status; - /* Initialize the intermediate surface from the destination surface - */ - _cairo_pattern_init_for_surface (&dst_pattern, dst); - + /* Initialize the intermediate surface from the destination surface */ + _cairo_pattern_init_for_surface (&pattern, dst); status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, - &dst_pattern.base, NULL, intermediate, + &pattern.base, NULL, intermediate, extents->x, extents->y, 0, 0, 0, 0, - extents->width, extents->height); - - _cairo_pattern_fini (&dst_pattern.base); - + extents->width, extents->height, + NULL); + _cairo_pattern_fini (&pattern.base); if (unlikely (status)) goto CLEANUP_SURFACE; status = (*draw_func) (draw_closure, op, src, intermediate, extents->x, extents->y, - extents); + extents, + NULL); if (unlikely (status)) goto CLEANUP_SURFACE; - /* Combine that with the clip - */ - status = _cairo_clip_combine_to_surface (clip, CAIRO_OPERATOR_DEST_IN, - intermediate, - extents->x, extents->y, - extents); + assert (clip->path != NULL); + clip_surface = _cairo_clip_get_surface (clip, dst); + if (unlikely (clip_surface->status)) + goto CLEANUP_SURFACE; + + _cairo_pattern_init_for_surface (&clip_pattern, clip_surface); + cairo_surface_destroy (clip_surface); + + /* Combine that with the clip */ + status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_IN, + &clip_pattern.base, NULL, intermediate, + extents->x - clip->path->extents.x, + extents->y - clip->path->extents.y, + 0, 0, + 0, 0, + extents->width, extents->height, + NULL); if (unlikely (status)) goto CLEANUP_SURFACE; - /* Punch the clip out of the destination - */ - status = _cairo_clip_combine_to_surface (clip, CAIRO_OPERATOR_DEST_OUT, - dst, - 0, 0, - extents); + /* Punch the clip out of the destination */ + status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT, + &clip_pattern.base, NULL, dst, + extents->x - clip->path->extents.x, + extents->y - clip->path->extents.y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height, + NULL); if (unlikely (status)) goto CLEANUP_SURFACE; - /* Now add the two results together - */ - _cairo_pattern_init_for_surface (&intermediate_pattern, intermediate); - + /* Now add the two results together */ + _cairo_pattern_init_for_surface (&pattern, intermediate); status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, - &intermediate_pattern.base, NULL, dst, + &pattern.base, NULL, dst, 0, 0, 0, 0, extents->x, extents->y, - extents->width, extents->height); - - _cairo_pattern_fini (&intermediate_pattern.base); + extents->width, extents->height, + NULL); + _cairo_pattern_fini (&pattern.base); CLEANUP_SURFACE: + _cairo_pattern_fini (&clip_pattern.base); cairo_surface_destroy (intermediate); return status; @@ -285,10 +329,19 @@ _clip_and_composite_source (cairo_clip_t *clip, const cairo_rectangle_int_t *extents) { cairo_surface_pattern_t mask_pattern; + cairo_region_t *clip_region = NULL; cairo_status_t status; - /* Create a surface that is mask IN clip - */ + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (! _cairo_status_is_error (status)); + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + return CAIRO_STATUS_SUCCESS; + + } + + + /* Create a surface that is mask IN clip */ status = _create_composite_mask_pattern (&mask_pattern, clip, draw_func, draw_closure, @@ -296,26 +349,26 @@ _clip_and_composite_source (cairo_clip_t *clip, if (unlikely (status)) return status; - /* Compute dest' = dest OUT (mask IN clip) - */ + /* Compute dest' = dest OUT (mask IN clip) */ status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT, &mask_pattern.base, NULL, dst, 0, 0, 0, 0, extents->x, extents->y, - extents->width, extents->height); + extents->width, extents->height, + clip_region); if (unlikely (status)) goto CLEANUP_MASK_PATTERN; - /* Now compute (src IN (mask IN clip)) ADD dest' - */ + /* Now compute (src IN (mask IN clip)) ADD dest' */ status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, src, &mask_pattern.base, dst, extents->x, extents->y, 0, 0, extents->x, extents->y, - extents->width, extents->height); + extents->width, extents->height, + clip_region); CLEANUP_MASK_PATTERN: _cairo_pattern_fini (&mask_pattern.base); @@ -372,30 +425,44 @@ _clip_and_composite (cairo_clip_t *clip, op = CAIRO_OPERATOR_DEST_OUT; } - if ((clip && clip->surface) || op == CAIRO_OPERATOR_SOURCE) - { - if (op == CAIRO_OPERATOR_SOURCE) - status = _clip_and_composite_source (clip, - src, - draw_func, draw_closure, - dst, extents); - else if (_cairo_operator_bounded_by_mask (op)) - status = _clip_and_composite_with_mask (clip, op, - src, - draw_func, draw_closure, - dst, extents); - else - status = _clip_and_composite_combine (clip, op, - src, - draw_func, draw_closure, - dst, extents); - } - else - { - status = (*draw_func) (draw_closure, op, - src, dst, - 0, 0, - extents); + if (op == CAIRO_OPERATOR_SOURCE) { + status = _clip_and_composite_source (clip, + src, + draw_func, draw_closure, + dst, extents); + } else { + cairo_bool_t clip_surface = FALSE; + cairo_region_t *clip_region = NULL; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (! _cairo_status_is_error (status)); + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + return CAIRO_STATUS_SUCCESS; + + clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (clip_surface) { + if (_cairo_operator_bounded_by_mask (op)) { + status = _clip_and_composite_with_mask (clip, op, + src, + draw_func, draw_closure, + dst, extents); + } else { + status = _clip_and_composite_combine (clip, op, + src, + draw_func, draw_closure, + dst, extents); + } + } else { + + status = draw_func (draw_closure, op, + src, dst, + 0, 0, + extents, + clip_region); + } } return status; @@ -409,59 +476,47 @@ _composite_trap_region (cairo_clip_t *clip, cairo_operator_t op, cairo_surface_t *dst, cairo_region_t *trap_region, - cairo_rectangle_int_t *extents) + const cairo_rectangle_int_t *extents) { cairo_status_t status; cairo_solid_pattern_t solid_pattern; - cairo_surface_pattern_t mask; - int num_rects = cairo_region_num_rectangles (trap_region); - unsigned int clip_serial; - cairo_surface_t *clip_surface = clip ? clip->surface : NULL; + cairo_surface_pattern_t mask_pattern; + cairo_pattern_t *mask = NULL; + int mask_x = 0, mask_y =0; - if (num_rects == 0) - return CAIRO_STATUS_SUCCESS; + if (clip != NULL) { + cairo_surface_t *clip_surface = NULL; + const cairo_rectangle_int_t *clip_extents; - if (clip_surface && op == CAIRO_OPERATOR_CLEAR) { - _cairo_pattern_init_solid (&solid_pattern, CAIRO_COLOR_WHITE, - CAIRO_CONTENT_COLOR); - src = &solid_pattern.base; - op = CAIRO_OPERATOR_DEST_OUT; + clip_surface = _cairo_clip_get_surface (clip, dst); + if (unlikely (clip_surface->status)) + return clip_surface->status; + + if (op == CAIRO_OPERATOR_CLEAR) { + _cairo_pattern_init_solid (&solid_pattern, CAIRO_COLOR_WHITE, + CAIRO_CONTENT_COLOR); + src = &solid_pattern.base; + op = CAIRO_OPERATOR_DEST_OUT; + } + + _cairo_pattern_init_for_surface (&mask_pattern, clip_surface); + cairo_surface_destroy (clip_surface); + + clip_extents = _cairo_clip_get_extents (clip); + mask_x = extents->x - clip_extents->x; + mask_y = extents->y - clip_extents->y; + mask = &mask_pattern.base; } - if (num_rects > 1) { - if (_cairo_surface_get_clip_mode (dst) != CAIRO_CLIP_MODE_REGION) - return CAIRO_INT_STATUS_UNSUPPORTED; - - clip_serial = _cairo_surface_allocate_clip_serial (dst); - status = _cairo_surface_set_clip_region (dst, - trap_region, - clip_serial); - if (unlikely (status)) - return status; - } - - if (clip_surface) - _cairo_pattern_init_for_surface (&mask, clip_surface); - - status = _cairo_surface_composite (op, - src, - clip_surface ? &mask.base : NULL, - dst, + status = _cairo_surface_composite (op, src, mask, dst, extents->x, extents->y, - extents->x - (clip_surface ? clip->surface_rect.x : 0), - extents->y - (clip_surface ? clip->surface_rect.y : 0), + mask_x, mask_y, extents->x, extents->y, - extents->width, extents->height); + extents->width, extents->height, + trap_region); - /* Restore the original clip if we modified it temporarily. */ - if (num_rects > 1) { - cairo_status_t status2 = _cairo_surface_set_clip (dst, clip); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } - - if (clip_surface) - _cairo_pattern_fini (&mask.base); + if (mask != NULL) + _cairo_pattern_fini (mask); return status; } @@ -478,27 +533,216 @@ _composite_traps_draw_func (void *closure, cairo_surface_t *dst, int dst_x, int dst_y, - const cairo_rectangle_int_t *extents) + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) { cairo_composite_traps_info_t *info = closure; - cairo_solid_pattern_t pattern; if (dst_x != 0 || dst_y != 0) _cairo_traps_translate (info->traps, - dst_x, - dst_y); - if (src == NULL) { - _cairo_pattern_init_solid (&pattern, CAIRO_COLOR_WHITE, - CAIRO_CONTENT_COLOR); - src = &pattern.base; - } - return _cairo_surface_composite_trapezoids (op, src, dst, info->antialias, extents->x, extents->y, extents->x - dst_x, extents->y - dst_y, extents->width, extents->height, info->traps->traps, - info->traps->num_traps); + info->traps->num_traps, + clip_region); +} + +enum { + HAS_CLEAR_REGION = 0x1, +}; + +static cairo_status_t +_clip_and_composite_region (const cairo_pattern_t *src, + cairo_operator_t op, + cairo_surface_t *dst, + cairo_region_t *trap_region, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + cairo_region_t clear_region; + unsigned int has_region = 0; + cairo_status_t status; + + if (! _cairo_operator_bounded_by_mask (op) && clip == NULL) { + /* If we optimize drawing with an unbounded operator to + * _cairo_surface_fill_rectangles() or to drawing with a + * clip region, then we have an additional region to clear. + */ + _cairo_region_init_rectangle (&clear_region, extents); + status = cairo_region_subtract (&clear_region, trap_region); + if (unlikely (status)) + return status; + + if (! cairo_region_is_empty (&clear_region)) + has_region |= HAS_CLEAR_REGION; + } + + if ((src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR) && + clip == NULL) + { + const cairo_color_t *color; + + if (op == CAIRO_OPERATOR_CLEAR) + color = CAIRO_COLOR_TRANSPARENT; + else + color = &((cairo_solid_pattern_t *)src)->color; + + /* Solid rectangles special case */ + status = _cairo_surface_fill_region (dst, op, color, trap_region); + } else { + /* For a simple rectangle, we can just use composite(), for more + * rectangles, we have to set a clip region. The cost of rasterizing + * trapezoids is pretty high for most backends currently, so it's + * worthwhile even if a region is needed. + * + * If we have a clip surface, we set it as the mask; this only works + * for bounded operators other than SOURCE; for unbounded operators, + * clip and mask cannot be interchanged. For SOURCE, the operator + * as implemented by the backends is different in its handling + * of the mask then what we want. + * + * CAIRO_INT_STATUS_UNSUPPORTED will be returned if the region has + * more than rectangle and the destination doesn't support clip + * regions. In that case, we fall through. + */ + status = _composite_trap_region (clip, src, op, dst, + trap_region, extents); + } + + if (has_region & HAS_CLEAR_REGION) { + if (status == CAIRO_STATUS_SUCCESS) { + status = _cairo_surface_fill_region (dst, + CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + &clear_region); + } + _cairo_region_fini (&clear_region); + } + + return status; +} + +/* avoid using region code to re-validate boxes */ +static cairo_status_t +_fill_rectangles (cairo_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_traps_t *traps, + cairo_clip_t *clip) +{ + const cairo_color_t *color; + cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; + cairo_rectangle_int_t *rects = stack_rects; + cairo_status_t status; + int i; + + if (! traps->is_rectilinear || ! traps->maybe_region) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* XXX: convert clip region to geometric boxes? */ + if (clip != NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* XXX: fallback for the region_subtract() operation */ + if (! _cairo_operator_bounded_by_mask (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! (src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (traps->has_intersections) { + if (traps->is_rectangular) { + status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING); + } else { + status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING); + } + if (unlikely (status)) + return status; + } + + for (i = 0; i < traps->num_traps; i++) { + if (! _cairo_fixed_is_integer (traps->traps[i].top) || + ! _cairo_fixed_is_integer (traps->traps[i].bottom) || + ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || + ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) + { + traps->maybe_region = FALSE; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + if (traps->num_traps > ARRAY_LENGTH (stack_rects)) { + rects = _cairo_malloc_ab (traps->num_traps, + sizeof (cairo_rectangle_int_t)); + if (unlikely (rects == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + for (i = 0; i < traps->num_traps; i++) { + int x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x); + int y1 = _cairo_fixed_integer_part (traps->traps[i].top); + int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x); + int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom); + + rects[i].x = x1; + rects[i].y = y1; + rects[i].width = x2 - x1; + rects[i].height = y2 - y1; + } + + if (op == CAIRO_OPERATOR_CLEAR) + color = CAIRO_COLOR_TRANSPARENT; + else + color = &((cairo_solid_pattern_t *)src)->color; + + status = _cairo_surface_fill_rectangles (dst, op, color, rects, i); + + if (rects != stack_rects) + free (rects); + + return status; +} + +/* fast-path for very common composite of a single rectangle */ +static cairo_status_t +_composite_rectangle (cairo_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_traps_t *traps, + cairo_clip_t *clip) +{ + cairo_rectangle_int_t rect; + + if (clip != NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (traps->num_traps > 1 || ! traps->is_rectilinear || ! traps->maybe_region) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_fixed_is_integer (traps->traps[0].top) || + ! _cairo_fixed_is_integer (traps->traps[0].bottom) || + ! _cairo_fixed_is_integer (traps->traps[0].left.p1.x) || + ! _cairo_fixed_is_integer (traps->traps[0].right.p1.x)) + { + traps->maybe_region = FALSE; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + rect.x = _cairo_fixed_integer_part (traps->traps[0].left.p1.x); + rect.y = _cairo_fixed_integer_part (traps->traps[0].top); + rect.width = _cairo_fixed_integer_part (traps->traps[0].right.p1.x) - rect.x; + rect.height = _cairo_fixed_integer_part (traps->traps[0].bottom) - rect.y; + + return _cairo_surface_composite (op, src, NULL, dst, + rect.x, rect.y, + 0, 0, + rect.x, rect.y, + rect.width, rect.height, + NULL); } /* Warning: This call modifies the coordinates of traps */ @@ -507,179 +751,130 @@ _clip_and_composite_trapezoids (const cairo_pattern_t *src, cairo_operator_t op, cairo_surface_t *dst, cairo_traps_t *traps, + cairo_antialias_t antialias, cairo_clip_t *clip, - cairo_antialias_t antialias) + cairo_rectangle_int_t *extents) { - cairo_status_t status; - cairo_region_t *trap_region = NULL; - cairo_region_t *clear_region = NULL; - cairo_rectangle_int_t extents; cairo_composite_traps_info_t traps_info; + cairo_region_t *clip_region = NULL; + cairo_bool_t clip_surface = FALSE; + cairo_status_t status; - if (_cairo_operator_bounded_by_mask (op) && traps->num_traps == 0) - return CAIRO_STATUS_SUCCESS; + if (traps->num_traps == 0 && _cairo_operator_bounded_by_mask (op)) + return CAIRO_STATUS_SUCCESS; - status = _cairo_surface_get_extents (dst, &extents); - if (unlikely (status)) - return status; + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + if (unlikely (_cairo_status_is_error (status))) + return status; + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + return CAIRO_STATUS_SUCCESS; - status = _cairo_traps_extract_region (traps, &trap_region); - if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - if (_cairo_operator_bounded_by_mask (op)) { - cairo_rectangle_int_t trap_extents; - - if (trap_region) { - status = _cairo_clip_intersect_to_region (clip, trap_region); - if (unlikely (status)) - goto out; - - cairo_region_get_extents (trap_region, &trap_extents); - } else { - cairo_box_t trap_box; - _cairo_traps_extents (traps, &trap_box); - _cairo_box_round_to_rectangle (&trap_box, &trap_extents); - } - - if (! _cairo_rectangle_intersect (&extents, &trap_extents)) { - status = CAIRO_STATUS_SUCCESS; - goto out; - } - - status = _cairo_clip_intersect_to_rectangle (clip, &extents); - if (unlikely (status)) - goto out; - } else { - cairo_surface_t *clip_surface = clip ? clip->surface : NULL; - - if (trap_region && !clip_surface) { - /* If we optimize drawing with an unbounded operator to - * _cairo_surface_fill_rectangles() or to drawing with a - * clip region, then we have an additional region to clear. - */ - clear_region = cairo_region_create_rectangle (&extents); - - status = cairo_region_status (clear_region); - if (unlikely (status)) - goto out; - - status = _cairo_clip_intersect_to_region (clip, clear_region); - if (unlikely (status)) - goto out; - - cairo_region_get_extents (clear_region, &extents); - - status = cairo_region_subtract (clear_region, trap_region); - if (unlikely (status)) - goto out; - - if (cairo_region_is_empty (clear_region)) { - cairo_region_destroy (clear_region); - clear_region = NULL; - } - } else { - status = _cairo_clip_intersect_to_rectangle (clip, &extents); - } + clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; } - if (unlikely (status)) - goto out; + /* Use a fast path if the trapezoids consist of a simple region, + * but we can only do this if we do not have a clip surface, or can + * substitute the mask with the clip. + */ + if (! clip_surface || + (_cairo_operator_bounded_by_mask (op) && op != CAIRO_OPERATOR_SOURCE)) + { + cairo_region_t *trap_region = NULL; - if (trap_region) { - cairo_surface_t *clip_surface = clip ? clip->surface : NULL; + status = _fill_rectangles (dst, op, src, traps, clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; - if ((src->type == CAIRO_PATTERN_TYPE_SOLID || - op == CAIRO_OPERATOR_CLEAR) && !clip_surface) { - const cairo_color_t *color; + status = _composite_rectangle (dst, op, src, traps, clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; - if (op == CAIRO_OPERATOR_CLEAR) { - color = CAIRO_COLOR_TRANSPARENT; - } else { - color = &((cairo_solid_pattern_t *)src)->color; - } + status = _cairo_traps_extract_region (traps, &trap_region); + if (unlikely (_cairo_status_is_error (status))) + return status; - /* Solid rectangles special case */ - status = _cairo_surface_fill_region (dst, op, color, trap_region); - - if (!status && clear_region) { - status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_CLEAR, - CAIRO_COLOR_TRANSPARENT, - clear_region); + if (trap_region != NULL) { + status = cairo_region_intersect_rectangle (trap_region, extents); + if (unlikely (status)) { + cairo_region_destroy (trap_region); + return status; } - goto out; - } + if (clip_region != NULL) { + status = cairo_region_intersect (trap_region, clip_region); + if (unlikely (status)) { + cairo_region_destroy (trap_region); + return status; + } + } - if ((_cairo_operator_bounded_by_mask (op) && - op != CAIRO_OPERATOR_SOURCE) || !clip_surface) { - /* For a simple rectangle, we can just use composite(), for more - * rectangles, we have to set a clip region. The cost of rasterizing - * trapezoids is pretty high for most backends currently, so it's - * worthwhile even if a region is needed. - * - * If we have a clip surface, we set it as the mask; this only works - * for bounded operators other than SOURCE; for unbounded operators, - * clip and mask cannot be interchanged. For SOURCE, the operator - * as implemented by the backends is different in its handling - * of the mask then what we want. - * - * CAIRO_INT_STATUS_UNSUPPORTED will be returned if the region has - * more than rectangle and the destination doesn't support clip - * regions. In that case, we fall through. - */ - status = _composite_trap_region (clip, src, op, dst, - trap_region, &extents); + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t trap_extents; - if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - if (!status && clear_region) - status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_CLEAR, - CAIRO_COLOR_TRANSPARENT, - clear_region); - goto out; - } - } + cairo_region_get_extents (trap_region, &trap_extents); + if (! _cairo_rectangle_intersect (extents, &trap_extents)) { + cairo_region_destroy (trap_region); + return CAIRO_STATUS_SUCCESS; + } + } + + status = _clip_and_composite_region (src, op, dst, + trap_region, + clip_surface ? clip : NULL, + extents); + cairo_region_destroy (trap_region); + + if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED)) + return status; + } } + /* No fast path, exclude self-intersections and clip trapezoids. */ + if (traps->has_intersections) { + if (traps->is_rectangular) + status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING); + else if (traps->is_rectilinear) + status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING); + else + status = _cairo_bentley_ottmann_tessellate_traps (traps, CAIRO_FILL_RULE_WINDING); + if (unlikely (status)) + return status; + } + + /* Otherwise render the trapezoids to a mask and composite in the usual + * fashion. + */ traps_info.traps = traps; traps_info.antialias = antialias; - status = _clip_and_composite (clip, op, src, - _composite_traps_draw_func, - &traps_info, dst, &extents); - -out: - if (trap_region) - cairo_region_destroy (trap_region); - if (clear_region) - cairo_region_destroy (clear_region); - - return status; + return _clip_and_composite (clip, op, src, + _composite_traps_draw_func, + &traps_info, dst, extents); } typedef struct { - cairo_path_fixed_t *path; + cairo_polygon_t *polygon; cairo_fill_rule_t fill_rule; - double tolerance; cairo_antialias_t antialias; -} cairo_composite_spans_fill_info_t; +} cairo_composite_spans_info_t; static cairo_status_t -_composite_spans_fill_func (void *closure, +_composite_spans_draw_func (void *closure, cairo_operator_t op, const cairo_pattern_t *src, cairo_surface_t *dst, int dst_x, int dst_y, - const cairo_rectangle_int_t *extents) + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) { cairo_composite_rectangles_t rects; - cairo_composite_spans_fill_info_t *info = closure; - cairo_solid_pattern_t pattern; + cairo_composite_spans_info_t *info = closure; - _cairo_composite_rectangles_init ( - &rects, extents->x, extents->y, - extents->width, extents->height); + _cairo_composite_rectangles_init (&rects, + extents->x, extents->y, + extents->width, extents->height); /* The incoming dst_x/y are where we're pretending the origin of * the dst surface is -- *not* the offset of a rectangle where @@ -687,62 +882,172 @@ _composite_spans_fill_func (void *closure, rects.dst.x -= dst_x; rects.dst.y -= dst_y; - /* We're called without a source pattern from - * _create_composite_mask_pattern(). */ - if (src == NULL) { - _cairo_pattern_init_solid (&pattern, CAIRO_COLOR_WHITE, - CAIRO_CONTENT_COLOR); - src = &pattern.base; + return _cairo_surface_composite_polygon (dst, op, src, + info->fill_rule, + info->antialias, + &rects, + info->polygon, + clip_region); +} + +static cairo_status_t +_rectangle_intersect_clip (cairo_rectangle_int_t *extents, cairo_clip_t *clip) +{ + if (clip != NULL) { + if (! _cairo_rectangle_intersect (extents, + _cairo_clip_get_extents (clip))) + { + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + return _cairo_clip_rectangle (clip, extents); + } else if (_cairo_rectangle_empty (extents)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_clip_contains_rectangle (cairo_clip_t *clip, + const cairo_rectangle_int_t *rect) +{ + cairo_clip_path_t *clip_path; + + clip_path = clip->path; + + if (clip_path->extents.x > rect->x || + clip_path->extents.y > rect->y || + clip_path->extents.x + clip_path->extents.width < rect->x + rect->width || + clip_path->extents.y + clip_path->extents.height < rect->y + rect->height) + { + return FALSE; } - return _cairo_path_fixed_fill_using_spans ( - op, src, info->path, dst, - info->fill_rule, info->tolerance, info->antialias, - &rects); + do { + cairo_box_t box; + + if (! _cairo_path_fixed_is_box (&clip_path->path, &box)) + return FALSE; + + if (box.p1.x > _cairo_fixed_from_int (rect->x) || + box.p1.y > _cairo_fixed_from_int (rect->y) || + box.p2.x < _cairo_fixed_from_int (rect->x + rect->width) || + box.p2.y < _cairo_fixed_from_int (rect->y + rect->height)) + { + return FALSE; + } + } while ((clip_path = clip_path->prev) != NULL); + + return TRUE; +} + +static inline cairo_status_t +_clip_to_boxes (cairo_clip_t **clip, + const cairo_rectangle_int_t *extents, + cairo_bool_t is_bounded, + cairo_box_t **boxes, + int *num_boxes) +{ + cairo_status_t status; + + if (*clip == NULL) { + status = CAIRO_STATUS_SUCCESS; + goto EXTENTS; + } + + status = _cairo_clip_get_boxes (*clip, boxes, num_boxes); + switch ((int) status) { + case CAIRO_STATUS_SUCCESS: + if (is_bounded) + *clip = NULL; + goto DONE; + + case CAIRO_INT_STATUS_UNSUPPORTED: + status = CAIRO_STATUS_SUCCESS; + goto EXTENTS; + } + + EXTENTS: + _cairo_box_from_rectangle (&(*boxes)[0], extents); + *num_boxes = 1; + DONE: + return status; } cairo_status_t _cairo_surface_fallback_paint (cairo_surface_t *surface, cairo_operator_t op, - const cairo_pattern_t *source) + const cairo_pattern_t *source, + cairo_clip_t *clip) { cairo_status_t status; cairo_rectangle_int_t extents; - cairo_box_t box; + cairo_bool_t is_bounded; + cairo_clip_path_t *clip_path = clip ? clip->path : NULL; + cairo_box_t boxes_stack[32], *boxes = boxes_stack; + int num_boxes = ARRAY_LENGTH (boxes_stack); cairo_traps_t traps; - status = _cairo_surface_get_extents (surface, &extents); - if (unlikely (status)) - return status; + is_bounded = _cairo_surface_get_extents (surface, &extents); + assert (is_bounded || clip); + is_bounded = FALSE; if (_cairo_operator_bounded_by_source (op)) { cairo_rectangle_int_t source_extents; - status = _cairo_pattern_get_extents (source, &source_extents); - if (unlikely (status)) - return status; - + _cairo_pattern_get_extents (source, &source_extents); if (! _cairo_rectangle_intersect (&extents, &source_extents)) return CAIRO_STATUS_SUCCESS; + + is_bounded = TRUE; } - status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents); - if (unlikely (status)) + if (is_bounded && clip != NULL && _clip_contains_rectangle (clip, &extents)) + clip = NULL; + + status = _rectangle_intersect_clip (&extents, clip); + if (unlikely (status)) { + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + status = CAIRO_STATUS_SUCCESS; return status; + } - _cairo_box_from_rectangle (&box, &extents); + status = _clip_to_boxes (&clip, &extents, is_bounded, &boxes, &num_boxes); + if (unlikely (status)) { + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + status = CAIRO_STATUS_SUCCESS; + return status; + } - _cairo_traps_init_box (&traps, &box); + /* If the clip cannot be reduced to a set of boxes, we will need to + * use a clipmask. Paint is special as it is the only operation that + * does not implicitly use a mask, so we may be able to reduce this + * operation to a fill... + */ + if (clip != NULL && clip_path->prev == NULL && + _cairo_operator_bounded_by_mask (op)) + { + return _cairo_surface_fill (surface, op, source, + &clip_path->path, + clip_path->fill_rule, + clip_path->tolerance, + clip_path->antialias, + NULL); + } - status = _clip_and_composite_trapezoids (source, - op, - surface, - &traps, - surface->clip, - CAIRO_ANTIALIAS_NONE); + status = _cairo_traps_init_boxes (&traps, boxes, num_boxes); + if (unlikely (status)) + goto CLEANUP_BOXES; + status = _clip_and_composite_trapezoids (source, op, surface, + &traps, CAIRO_ANTIALIAS_DEFAULT, + clip, &extents); _cairo_traps_fini (&traps); +CLEANUP_BOXES: + if (boxes != boxes_stack) + free (boxes); + return status; } @@ -753,69 +1058,79 @@ _cairo_surface_mask_draw_func (void *closure, cairo_surface_t *dst, int dst_x, int dst_y, - const cairo_rectangle_int_t *extents) + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) { cairo_pattern_t *mask = closure; - if (src) + if (src) { return _cairo_surface_composite (op, src, mask, dst, extents->x, extents->y, extents->x, extents->y, extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height); - else + extents->width, extents->height, + clip_region); + } else { return _cairo_surface_composite (op, mask, NULL, dst, extents->x, extents->y, 0, 0, /* unused */ extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height); + extents->width, extents->height, + clip_region); + } } cairo_status_t _cairo_surface_fallback_mask (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, - const cairo_pattern_t *mask) + const cairo_pattern_t *mask, + cairo_clip_t *clip) { + cairo_rectangle_int_t extents; + cairo_bool_t is_bounded; cairo_status_t status; - cairo_rectangle_int_t extents, source_extents, mask_extents; - status = _cairo_surface_get_extents (surface, &extents); - if (unlikely (status)) - return status; + is_bounded = _cairo_surface_get_extents (surface, &extents); + assert (is_bounded || clip); + is_bounded = FALSE; if (_cairo_operator_bounded_by_source (op)) { - status = _cairo_pattern_get_extents (source, &source_extents); - if (unlikely (status)) - return status; + cairo_rectangle_int_t source_extents; + _cairo_pattern_get_extents (source, &source_extents); if (! _cairo_rectangle_intersect (&extents, &source_extents)) return CAIRO_STATUS_SUCCESS; + + is_bounded = TRUE; } if (_cairo_operator_bounded_by_mask (op)) { - status = _cairo_pattern_get_extents (mask, &mask_extents); - if (unlikely (status)) - return status; + cairo_rectangle_int_t mask_extents; + _cairo_pattern_get_extents (mask, &mask_extents); if (! _cairo_rectangle_intersect (&extents, &mask_extents)) return CAIRO_STATUS_SUCCESS; + + is_bounded = TRUE; } - status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents); - if (unlikely (status)) + if (is_bounded && clip != NULL && _clip_contains_rectangle (clip, &extents)) + clip = NULL; + + status = _rectangle_intersect_clip (&extents, clip); + if (status) { + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + status = CAIRO_STATUS_SUCCESS; return status; + } - status = _clip_and_composite (surface->clip, op, - source, - _cairo_surface_mask_draw_func, - (void *) mask, - surface, - &extents); - - return status; + return _clip_and_composite (clip, op, source, + _cairo_surface_mask_draw_func, + (void *) mask, + surface, &extents); } cairo_status_t @@ -827,56 +1142,126 @@ _cairo_surface_fallback_stroke (cairo_surface_t *surface, cairo_matrix_t *ctm, cairo_matrix_t *ctm_inverse, double tolerance, - cairo_antialias_t antialias) + cairo_antialias_t antialias, + cairo_clip_t *clip) { - cairo_status_t status; + cairo_polygon_t polygon; cairo_traps_t traps; - cairo_box_t box; + cairo_box_t boxes_stack[32], *boxes = boxes_stack; + int num_boxes = ARRAY_LENGTH (boxes_stack); cairo_rectangle_int_t extents; + cairo_bool_t is_bounded; + cairo_status_t status; - status = _cairo_surface_get_extents (surface, &extents); - if (unlikely (status)) - return status; + is_bounded = _cairo_surface_get_extents (surface, &extents); + assert (is_bounded || clip); + is_bounded = FALSE; if (_cairo_operator_bounded_by_source (op)) { cairo_rectangle_int_t source_extents; - status = _cairo_pattern_get_extents (source, &source_extents); - if (unlikely (status)) - return status; + _cairo_pattern_get_extents (source, &source_extents); if (! _cairo_rectangle_intersect (&extents, &source_extents)) return CAIRO_STATUS_SUCCESS; + + is_bounded = TRUE; } - status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents); - if (unlikely (status)) - return status; + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t path_extents; - if (extents.width == 0 || extents.height == 0) - return CAIRO_STATUS_SUCCESS; + _cairo_path_fixed_approximate_stroke_extents (path, + stroke_style, ctm, + &path_extents); + if (! _cairo_rectangle_intersect (&extents, &path_extents)) + return CAIRO_STATUS_SUCCESS; - _cairo_box_from_rectangle (&box, &extents); + is_bounded = TRUE; + } + + if (is_bounded && clip != NULL && _clip_contains_rectangle (clip, &extents)) + clip = NULL; + + status = _rectangle_intersect_clip (&extents, clip); + if (unlikely (status)) { + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + status = CAIRO_STATUS_SUCCESS; + return status; + } + + status = _clip_to_boxes (&clip, &extents, is_bounded, &boxes, &num_boxes); + if (unlikely (status)) { + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + status = CAIRO_STATUS_SUCCESS; + return status; + } + + _cairo_polygon_init (&polygon); + _cairo_polygon_limit (&polygon, boxes, num_boxes); _cairo_traps_init (&traps); - _cairo_traps_limit (&traps, &box); + _cairo_traps_limit (&traps, boxes, num_boxes); - status = _cairo_path_fixed_stroke_to_traps (path, - stroke_style, - ctm, ctm_inverse, - tolerance, - &traps); + if (path->is_rectilinear) { + status = _cairo_path_fixed_stroke_rectilinear_to_traps (path, + stroke_style, + ctm, + &traps); + if (likely (status == CAIRO_STATUS_SUCCESS)) + goto DO_TRAPS; + + if (_cairo_status_is_error (status)) + goto CLEANUP; + } + + status = _cairo_path_fixed_stroke_to_polygon (path, + stroke_style, + ctm, ctm_inverse, + tolerance, + &polygon); if (unlikely (status)) - goto FAIL; + goto CLEANUP; - status = _clip_and_composite_trapezoids (source, - op, - surface, - &traps, - surface->clip, - antialias); + if (polygon.num_edges == 0) + goto DO_TRAPS; -FAIL: + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t polygon_extents; + + _cairo_box_round_to_rectangle (&polygon.extents, &polygon_extents); + if (! _cairo_rectangle_intersect (&extents, &polygon_extents)) + goto CLEANUP; + } + + if (_cairo_surface_check_span_renderer (op, source, surface, antialias)) { + cairo_composite_spans_info_t info; + + info.polygon = &polygon; + info.fill_rule = CAIRO_FILL_RULE_WINDING; + info.antialias = antialias; + + status = _clip_and_composite (clip, op, source, + _composite_spans_draw_func, + &info, surface, &extents); + goto CLEANUP; + } + + /* Fall back to trapezoid fills. */ + status = _cairo_bentley_ottmann_tessellate_polygon (&traps, + &polygon, + CAIRO_FILL_RULE_WINDING); + if (unlikely (status)) + goto CLEANUP; + + DO_TRAPS: + status = _clip_and_composite_trapezoids (source, op, surface, + &traps, antialias, + clip, &extents); + CLEANUP: _cairo_traps_fini (&traps); + _cairo_polygon_fini (&polygon); + if (boxes != boxes_stack) + free (boxes); return status; } @@ -888,96 +1273,144 @@ _cairo_surface_fallback_fill (cairo_surface_t *surface, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, - cairo_antialias_t antialias) + cairo_antialias_t antialias, + cairo_clip_t *clip) { - cairo_status_t status; + cairo_polygon_t polygon; cairo_traps_t traps; - cairo_box_t box; + cairo_box_t boxes_stack[32], *boxes = boxes_stack; + int num_boxes = ARRAY_LENGTH (boxes_stack); cairo_rectangle_int_t extents; + cairo_bool_t is_bounded; + cairo_bool_t is_rectilinear; + cairo_status_t status; - status = _cairo_surface_get_extents (surface, &extents); - if (unlikely (status)) - return status; + is_bounded = _cairo_surface_get_extents (surface, &extents); + assert (is_bounded || clip); + is_bounded = FALSE; if (_cairo_operator_bounded_by_source (op)) { cairo_rectangle_int_t source_extents; - status = _cairo_pattern_get_extents (source, &source_extents); - if (unlikely (status)) - return status; - + _cairo_pattern_get_extents (source, &source_extents); if (! _cairo_rectangle_intersect (&extents, &source_extents)) return CAIRO_STATUS_SUCCESS; + + is_bounded = TRUE; } - status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents); - if (unlikely (status)) - return status; + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t path_extents; - if (extents.width == 0 || extents.height == 0) - return CAIRO_STATUS_SUCCESS; + _cairo_path_fixed_approximate_fill_extents (path, &path_extents); + if (! _cairo_rectangle_intersect (&extents, &path_extents)) + return CAIRO_STATUS_SUCCESS; - /* Ask if the surface would like to render this combination of - * op/source/dst/antialias with spans or not, but don't actually - * make a renderer yet. We'll try to hit the region optimisations - * in _clip_and_composite_trapezoids() if it looks like the path - * is a region. */ - /* TODO: Until we have a mono scan converter we won't even try - * to use spans for CAIRO_ANTIALIAS_NONE. */ - /* TODO: The region filling code should be lifted from - * _clip_and_composite_trapezoids() and given first priority - * explicitly before deciding between spans and trapezoids. */ - if (antialias != CAIRO_ANTIALIAS_NONE && - !_cairo_path_fixed_is_box (path, &box) && - !_cairo_path_fixed_is_region (path) && - _cairo_surface_check_span_renderer ( - op, source, surface, antialias, NULL)) - { - cairo_composite_spans_fill_info_t info; - info.path = path; - info.fill_rule = fill_rule; - info.tolerance = tolerance; - info.antialias = antialias; + is_bounded = TRUE; + } - if (_cairo_operator_bounded_by_mask (op)) { - cairo_rectangle_int_t path_extents; + if (is_bounded) { + if (clip != NULL && _clip_contains_rectangle (clip, &extents)) + clip = NULL; - _cairo_path_fixed_approximate_clip_extents (path, - &path_extents); - if (! _cairo_rectangle_intersect (&extents, &path_extents)) - return CAIRO_STATUS_SUCCESS; + if (clip != NULL && clip->path->prev == NULL && + _cairo_path_fixed_equal (&clip->path->path, path)) + { + clip = NULL; } - - return _clip_and_composite ( - surface->clip, op, source, - _composite_spans_fill_func, - &info, - surface, - &extents); } - /* Fall back to trapezoid fills. */ - _cairo_box_from_rectangle (&box, &extents); - _cairo_traps_init (&traps); - _cairo_traps_limit (&traps, &box); - - status = _cairo_path_fixed_fill_to_traps (path, - fill_rule, - tolerance, - &traps); + status = _rectangle_intersect_clip (&extents, clip); if (unlikely (status)) { - _cairo_traps_fini (&traps); + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + status = CAIRO_STATUS_SUCCESS; return status; } - status = _clip_and_composite_trapezoids (source, - op, - surface, - &traps, - surface->clip, - antialias); + status = _clip_to_boxes (&clip, &extents, is_bounded, &boxes, &num_boxes); + if (unlikely (status)) { + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + status = CAIRO_STATUS_SUCCESS; + return status; + } + _cairo_traps_init (&traps); + _cairo_traps_limit (&traps, boxes, num_boxes); + + _cairo_polygon_init (&polygon); + _cairo_polygon_limit (&polygon, boxes, num_boxes); + + if (path->is_empty_fill) + goto DO_TRAPS; + + is_rectilinear = _cairo_path_fixed_is_rectilinear_fill (path); + if (is_rectilinear) { + status = _cairo_path_fixed_fill_rectilinear_to_traps (path, + fill_rule, + &traps); + if (likely (status == CAIRO_STATUS_SUCCESS)) + goto DO_TRAPS; + + if (_cairo_status_is_error (status)) + goto CLEANUP; + } + + status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); + if (unlikely (status)) + goto CLEANUP; + + if (polygon.num_edges == 0) + goto DO_TRAPS; + + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t polygon_extents; + + _cairo_box_round_to_rectangle (&polygon.extents, &polygon_extents); + if (! _cairo_rectangle_intersect (&extents, &polygon_extents)) + goto CLEANUP; + } + + if (is_rectilinear) { + status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps, + &polygon, + fill_rule); + if (likely (status == CAIRO_STATUS_SUCCESS)) + goto DO_TRAPS; + + if (unlikely (_cairo_status_is_error (status))) + goto CLEANUP; + } + + + if (_cairo_surface_check_span_renderer (op, source, surface, antialias)) { + cairo_composite_spans_info_t info; + + info.polygon = &polygon; + info.fill_rule = fill_rule; + info.antialias = antialias; + + status = _clip_and_composite (clip, op, source, + _composite_spans_draw_func, + &info, surface, &extents); + goto CLEANUP; + } + + /* Fall back to trapezoid fills. */ + status = _cairo_bentley_ottmann_tessellate_polygon (&traps, + &polygon, + fill_rule); + if (unlikely (status)) + goto CLEANUP; + + DO_TRAPS: + status = _clip_and_composite_trapezoids (source, op, surface, + &traps, antialias, + clip, &extents); + CLEANUP: _cairo_traps_fini (&traps); + _cairo_polygon_fini (&polygon); + if (boxes != boxes_stack) + free (boxes); return status; } @@ -995,10 +1428,10 @@ _cairo_surface_old_show_glyphs_draw_func (void *closure cairo_surface_t *dst, int dst_x, int dst_y, - const cairo_rectangle_int_t *extents) + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) { cairo_show_glyphs_info_t *glyph_info = closure; - cairo_solid_pattern_t pattern; cairo_status_t status; /* Modifying the glyph array is fine because we know that this function @@ -1008,19 +1441,12 @@ _cairo_surface_old_show_glyphs_draw_func (void *closure if (dst_x != 0 || dst_y != 0) { int i; - for (i = 0; i < glyph_info->num_glyphs; ++i) - { + for (i = 0; i < glyph_info->num_glyphs; ++i) { ((cairo_glyph_t *) glyph_info->glyphs)[i].x -= dst_x; ((cairo_glyph_t *) glyph_info->glyphs)[i].y -= dst_y; } } - if (src == NULL) { - _cairo_pattern_init_solid (&pattern, CAIRO_COLOR_WHITE, - CAIRO_CONTENT_COLOR); - src = &pattern.base; - } - status = _cairo_surface_old_show_glyphs (glyph_info->font, op, src, dst, extents->x, extents->y, @@ -1029,7 +1455,8 @@ _cairo_surface_old_show_glyphs_draw_func (void *closure extents->width, extents->height, glyph_info->glyphs, - glyph_info->num_glyphs); + glyph_info->num_glyphs, + clip_region); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; @@ -1041,7 +1468,8 @@ _cairo_surface_old_show_glyphs_draw_func (void *closure extents->y - dst_y, extents->width, extents->height, glyph_info->glyphs, - glyph_info->num_glyphs); + glyph_info->num_glyphs, + clip_region); } cairo_status_t @@ -1050,15 +1478,27 @@ _cairo_surface_fallback_show_glyphs (cairo_surface_t *surface, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, - cairo_scaled_font_t *scaled_font) + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip) { cairo_status_t status; cairo_rectangle_int_t extents; cairo_show_glyphs_info_t glyph_info; + cairo_bool_t is_bounded; - status = _cairo_surface_get_extents (surface, &extents); - if (unlikely (status)) - return status; + is_bounded = _cairo_surface_get_extents (surface, &extents); + assert (is_bounded || clip); + + is_bounded = FALSE; + if (_cairo_operator_bounded_by_source (op)) { + cairo_rectangle_int_t source_extents; + + _cairo_pattern_get_extents (source, &source_extents); + if (! _cairo_rectangle_intersect (&extents, &source_extents)) + return CAIRO_STATUS_SUCCESS; + + is_bounded = TRUE; + } if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t glyph_extents; @@ -1066,31 +1506,36 @@ _cairo_surface_fallback_show_glyphs (cairo_surface_t *surface, status = _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs, - &glyph_extents); + &glyph_extents, + NULL); if (unlikely (status)) return status; if (! _cairo_rectangle_intersect (&extents, &glyph_extents)) return CAIRO_STATUS_SUCCESS; + + is_bounded = TRUE; } - status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents); - if (unlikely (status)) + if (is_bounded && clip != NULL && _clip_contains_rectangle (clip, &extents)) + clip = NULL; + + status = _rectangle_intersect_clip (&extents, clip); + if (status) { + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + status = CAIRO_STATUS_SUCCESS; return status; + } glyph_info.font = scaled_font; glyph_info.glyphs = glyphs; glyph_info.num_glyphs = num_glyphs; - status = _clip_and_composite (surface->clip, - op, - source, - _cairo_surface_old_show_glyphs_draw_func, - &glyph_info, - surface, - &extents); - - return status; + return _clip_and_composite (clip, op, source, + _cairo_surface_old_show_glyphs_draw_func, + &glyph_info, + surface, + &extents); } cairo_surface_t * @@ -1124,15 +1569,10 @@ _cairo_surface_fallback_snapshot (cairo_surface_t *surface) } _cairo_pattern_init_for_surface (&pattern, &image->base); - status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, - &pattern.base, - NULL, - snapshot, - 0, 0, - 0, 0, - 0, 0, - image->width, - image->height); + status = _cairo_surface_paint (snapshot, + CAIRO_OPERATOR_SOURCE, + &pattern.base, + NULL); _cairo_pattern_fini (&pattern.base); _cairo_surface_release_source_image (surface, image, image_extra); if (unlikely (status)) { @@ -1155,15 +1595,17 @@ _cairo_surface_fallback_composite (cairo_operator_t op, int dst_x, int dst_y, unsigned int width, - unsigned int height) + unsigned int height, + cairo_region_t *clip_region) { fallback_state_t state; + cairo_region_t *fallback_region = NULL; cairo_status_t status; status = _fallback_init (&state, dst, dst_x, dst_y, width, height); if (unlikely (status)) { if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - return CAIRO_STATUS_SUCCESS; + status = CAIRO_STATUS_SUCCESS; return status; } @@ -1172,12 +1614,29 @@ _cairo_surface_fallback_composite (cairo_operator_t op, * _cairo_surface_composite so that we get the correct device * offset handling. */ + + if (clip_region != NULL && (state.image_rect.x || state.image_rect.y)) { + fallback_region = cairo_region_copy (clip_region); + status = fallback_region->status; + if (unlikely (status)) + goto FAIL; + + cairo_region_translate (fallback_region, + -state.image_rect.x, + -state.image_rect.y); + clip_region = fallback_region; + } + status = _cairo_surface_composite (op, src, mask, &state.image->base, src_x, src_y, mask_x, mask_y, dst_x - state.image_rect.x, dst_y - state.image_rect.y, - width, height); + width, height, + clip_region); + FAIL: + if (fallback_region != NULL) + cairo_region_destroy (fallback_region); _fallback_fini (&state); return status; @@ -1224,7 +1683,7 @@ _cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface, status = _fallback_init (&state, surface, x1, y1, x2 - x1, y2 - y1); if (unlikely (status)) { if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - return CAIRO_STATUS_SUCCESS; + status = CAIRO_STATUS_SUCCESS; return status; } @@ -1271,16 +1730,18 @@ _cairo_surface_fallback_composite_trapezoids (cairo_operator_t op, unsigned int width, unsigned int height, cairo_trapezoid_t *traps, - int num_traps) + int num_traps, + cairo_region_t *clip_region) { fallback_state_t state; + cairo_region_t *fallback_region = NULL; cairo_trapezoid_t *offset_traps = NULL; cairo_status_t status; status = _fallback_init (&state, dst, dst_x, dst_y, width, height); if (unlikely (status)) { if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - return CAIRO_STATUS_SUCCESS; + status = CAIRO_STATUS_SUCCESS; return status; } @@ -1288,15 +1749,28 @@ _cairo_surface_fallback_composite_trapezoids (cairo_operator_t op, if (state.image_rect.x != 0 || state.image_rect.y != 0) { offset_traps = _cairo_malloc_ab (num_traps, sizeof (cairo_trapezoid_t)); - if (!offset_traps) { + if (offset_traps == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto DONE; + goto FAIL; } _cairo_trapezoid_array_translate_and_scale (offset_traps, traps, num_traps, - state.image_rect.x, - state.image_rect.y, 1.0, 1.0); traps = offset_traps; + + /* similarly we need to adjust the region */ + if (clip_region != NULL) { + fallback_region = cairo_region_copy (clip_region); + status = fallback_region->status; + if (unlikely (status)) + goto FAIL; + + cairo_region_translate (fallback_region, + -state.image_rect.x, + -state.image_rect.y); + clip_region = fallback_region; + } } status = _cairo_surface_composite_trapezoids (op, pattern, @@ -1306,11 +1780,14 @@ _cairo_surface_fallback_composite_trapezoids (cairo_operator_t op, dst_x - state.image_rect.x, dst_y - state.image_rect.y, width, height, - traps, num_traps); - if (offset_traps) + traps, num_traps, + clip_region); + if (offset_traps != NULL) free (offset_traps); - DONE: + FAIL: + if (fallback_region != NULL) + cairo_region_destroy (fallback_region); _fallback_fini (&state); return status; @@ -1319,7 +1796,6 @@ _cairo_surface_fallback_composite_trapezoids (cairo_operator_t op, cairo_status_t _cairo_surface_fallback_clone_similar (cairo_surface_t *surface, cairo_surface_t *src, - cairo_content_t content, int src_x, int src_y, int width, @@ -1333,9 +1809,11 @@ _cairo_surface_fallback_clone_similar (cairo_surface_t *surface, cairo_status_t status; new_surface = _cairo_surface_create_similar_scratch (surface, - src->content & content, + src->content, width, height); - if (new_surface->status) + if (new_surface == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (unlikely (new_surface->status)) return new_surface->status; /* We have to copy these here, so that the coordinate spaces are correct */ @@ -1348,7 +1826,8 @@ _cairo_surface_fallback_clone_similar (cairo_surface_t *surface, status = _cairo_surface_paint (new_surface, CAIRO_OPERATOR_SOURCE, - &pattern.base, NULL); + &pattern.base, + NULL); _cairo_pattern_fini (&pattern.base); if (unlikely (status)) { diff --git a/gfx/cairo/cairo/src/cairo-surface-private.h b/gfx/cairo/cairo/src/cairo-surface-private.h index c25b6dc8a7de..994df0e595c7 100644 --- a/gfx/cairo/cairo/src/cairo-surface-private.h +++ b/gfx/cairo/cairo/src/cairo-surface-private.h @@ -42,6 +42,7 @@ #include "cairo-types-private.h" #include "cairo-reference-count-private.h" +#include "cairo-clip-private.h" typedef void (*cairo_surface_func_t) (cairo_surface_t *); @@ -57,9 +58,12 @@ struct _cairo_surface { cairo_reference_count_t ref_count; cairo_status_t status; - cairo_bool_t finished; unsigned int unique_id; + unsigned finished : 1; + unsigned is_clear : 1; + unsigned has_font_options : 1; + cairo_user_data_array_t user_data; cairo_user_data_array_t mime_data; @@ -77,24 +81,6 @@ struct _cairo_surface { double x_fallback_resolution; double y_fallback_resolution; - cairo_clip_t *clip; - - /* - * Each time a clip region is modified, it gets the next value in this - * sequence. This means that clip regions for this surface are uniquely - * identified and updates to the clip can be readily identified - */ - unsigned int next_clip_serial; - /* - * The serial number of the current clip. This is set when - * the surface clipping is set. The gstate can then cheaply - * check whether the surface clipping is already correct before - * performing a rendering operation. - * - * The special value '0' is reserved for the unclipped case. - */ - unsigned int current_clip_serial; - /* A "snapshot" surface is immutable. See _cairo_surface_snapshot. */ cairo_surface_t *snapshot_of; cairo_surface_func_t snapshot_detach; @@ -106,7 +92,6 @@ struct _cairo_surface { * and set using _cairo_surface_set_font_options(), and propagated by * cairo_surface_create_similar(). */ - cairo_bool_t has_font_options; cairo_font_options_t font_options; }; diff --git a/gfx/cairo/cairo/src/cairo-surface-wrapper-private.h b/gfx/cairo/cairo/src/cairo-surface-wrapper-private.h new file mode 100644 index 000000000000..7da4cdaad5cc --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-wrapper-private.h @@ -0,0 +1,163 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2005 Red Hat, Inc. + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_SURFACE_WRAPPER_PRIVATE_H +#define CAIRO_SURFACE_WRAPPER_PRIVATE_H + +#include "cairo-types-private.h" + +CAIRO_BEGIN_DECLS + +struct _cairo_surface_wrapper { + cairo_surface_t *target; + + /* any other information? */ +}; + +cairo_private void +_cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper, + cairo_surface_t *target); + +cairo_private void +_cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper); + +cairo_private cairo_status_t +_cairo_surface_wrapper_acquire_source_image (cairo_surface_wrapper_t *wrapper, + cairo_image_surface_t **image_out, + void **image_extra); + +cairo_private void +_cairo_surface_wrapper_release_source_image (cairo_surface_wrapper_t *wrapper, + cairo_image_surface_t *image, + void *image_extra); + + +cairo_private cairo_status_t +_cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_stroke_style_t *stroke_style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, + cairo_operator_t fill_op, + const cairo_pattern_t *fill_source, + cairo_fill_rule_t fill_rule, + double fill_tolerance, + cairo_antialias_t fill_antialias, + cairo_path_fixed_t *path, + cairo_operator_t stroke_op, + const cairo_pattern_t *stroke_source, + cairo_stroke_style_t *stroke_style, + cairo_matrix_t *stroke_ctm, + cairo_matrix_t *stroke_ctm_inverse, + double stroke_tolerance, + cairo_antialias_t stroke_antialias, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip); + +cairo_private cairo_surface_t * +_cairo_surface_wrapper_create_similar (cairo_surface_wrapper_t *wrapper, + cairo_content_t content, + int width, + int height); +cairo_private cairo_bool_t +_cairo_surface_wrapper_get_extents (cairo_surface_wrapper_t *wrapper, + cairo_rectangle_int_t *extents); + +cairo_private void +_cairo_surface_wrapper_get_font_options (cairo_surface_wrapper_t *wrapper, + cairo_font_options_t *options); + +cairo_private cairo_surface_t * +_cairo_surface_wrapper_snapshot (cairo_surface_wrapper_t *wrapper); + +cairo_private cairo_bool_t +_cairo_surface_wrapper_has_show_text_glyphs (cairo_surface_wrapper_t *wrapper); + +static inline cairo_bool_t +_cairo_surface_wrapper_is_active (cairo_surface_wrapper_t *wrapper) +{ + return wrapper->target != (cairo_surface_t *) 0; +} + +CAIRO_END_DECLS + +#endif /* CAIRO_SURFACE_WRAPPER_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-surface-wrapper.c b/gfx/cairo/cairo/src/cairo-surface-wrapper.c new file mode 100644 index 000000000000..af1ef9c2cb4e --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-wrapper.c @@ -0,0 +1,526 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * Copyright © 2007 Adrian Johnson + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-surface-wrapper-private.h" + +/* A collection of routines to facilitate surface wrapping */ + +static cairo_bool_t +_cairo_surface_wrapper_needs_device_transform (cairo_surface_wrapper_t *wrapper, + cairo_matrix_t *matrix) +{ + if (_cairo_matrix_is_identity (&wrapper->target->device_transform)) + return FALSE; + + *matrix = wrapper->target->device_transform; + return TRUE; +} + +cairo_status_t +_cairo_surface_wrapper_acquire_source_image (cairo_surface_wrapper_t *wrapper, + cairo_image_surface_t **image_out, + void **image_extra) +{ + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + return _cairo_surface_acquire_source_image (wrapper->target, + image_out, image_extra); +} + +void +_cairo_surface_wrapper_release_source_image (cairo_surface_wrapper_t *wrapper, + cairo_image_surface_t *image, + void *image_extra) +{ + _cairo_surface_release_source_image (wrapper->target, image, image_extra); +} + +static void +_cairo_surface_wrapper_transform_pattern (cairo_surface_wrapper_t *wrapper, + cairo_pattern_t *pattern, + const cairo_pattern_t *original) +{ + _cairo_pattern_init_static_copy (pattern, original); + + if (_cairo_surface_has_device_transform (wrapper->target)) + _cairo_pattern_transform (pattern, + &wrapper->target->device_transform_inverse); +} + +cairo_status_t +_cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_matrix_t device_transform; + cairo_clip_t clip_copy, *dev_clip = clip; + cairo_pattern_union_t source_pattern; + + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (clip != NULL) { + if (_cairo_surface_wrapper_needs_device_transform (wrapper, + &device_transform)) + { + status = _cairo_clip_init_copy_transformed (&clip_copy, clip, + &device_transform); + if (unlikely (status)) + goto FINISH; + + } else { + _cairo_clip_init_copy (&clip_copy, clip); + } + + dev_clip = &clip_copy; + } + + _cairo_surface_wrapper_transform_pattern (wrapper, &source_pattern.base, source); + + status = _cairo_surface_paint (wrapper->target, op, &source_pattern.base, dev_clip); + + FINISH: + if (dev_clip != clip) + _cairo_clip_reset (dev_clip); + return status; +} + +cairo_status_t +_cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_matrix_t device_transform; + cairo_clip_t clip_copy, *dev_clip = clip; + cairo_pattern_union_t source_pattern, mask_pattern; + + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (clip != NULL) { + if (_cairo_surface_wrapper_needs_device_transform (wrapper, + &device_transform)) + { + status = _cairo_clip_init_copy_transformed (&clip_copy, clip, + &device_transform); + if (unlikely (status)) + goto FINISH; + + } else { + _cairo_clip_init_copy (&clip_copy, clip); + } + + dev_clip = &clip_copy; + } + + _cairo_surface_wrapper_transform_pattern (wrapper, &source_pattern.base, source); + _cairo_surface_wrapper_transform_pattern (wrapper, &mask_pattern.base, mask); + + status = _cairo_surface_mask (wrapper->target, op, + &source_pattern.base, &mask_pattern.base, + dev_clip); + + FINISH: + if (dev_clip != clip) + _cairo_clip_reset (dev_clip); + return status; +} + +cairo_status_t +_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_stroke_style_t *stroke_style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_matrix_t device_transform; + cairo_path_fixed_t path_copy, *dev_path = path; + cairo_clip_t clip_copy, *dev_clip = clip; + cairo_matrix_t dev_ctm = *ctm; + cairo_matrix_t dev_ctm_inverse = *ctm_inverse; + cairo_pattern_union_t source_pattern; + + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (_cairo_surface_wrapper_needs_device_transform (wrapper, + &device_transform)) + { + status = _cairo_path_fixed_init_copy (&path_copy, dev_path); + if (unlikely (status)) + goto FINISH; + + _cairo_path_fixed_transform (&path_copy, &device_transform); + dev_path = &path_copy; + + if (clip != NULL) { + status = _cairo_clip_init_copy_transformed (&clip_copy, clip, + &device_transform); + if (unlikely (status)) + goto FINISH; + + dev_clip = &clip_copy; + } + + cairo_matrix_multiply (&dev_ctm, &dev_ctm, &device_transform); + status = cairo_matrix_invert (&device_transform); + assert (status == CAIRO_STATUS_SUCCESS); + cairo_matrix_multiply (&dev_ctm_inverse, + &device_transform, + &dev_ctm_inverse); + } else { + if (clip != NULL) { + dev_clip = &clip_copy; + _cairo_clip_init_copy (&clip_copy, clip); + } + } + + _cairo_surface_wrapper_transform_pattern (wrapper, &source_pattern.base, source); + + status = _cairo_surface_stroke (wrapper->target, op, &source_pattern.base, + dev_path, stroke_style, + &dev_ctm, &dev_ctm_inverse, + tolerance, antialias, + dev_clip); + + FINISH: + if (dev_path != path) + _cairo_path_fixed_fini (dev_path); + if (dev_clip != clip) + _cairo_clip_reset (dev_clip); + return status; +} + +cairo_status_t +_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, + cairo_operator_t fill_op, + const cairo_pattern_t *fill_source, + cairo_fill_rule_t fill_rule, + double fill_tolerance, + cairo_antialias_t fill_antialias, + cairo_path_fixed_t *path, + cairo_operator_t stroke_op, + const cairo_pattern_t *stroke_source, + cairo_stroke_style_t *stroke_style, + cairo_matrix_t *stroke_ctm, + cairo_matrix_t *stroke_ctm_inverse, + double stroke_tolerance, + cairo_antialias_t stroke_antialias, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_matrix_t device_transform; + cairo_path_fixed_t path_copy, *dev_path = path; + cairo_clip_t clip_copy, *dev_clip = clip; + cairo_matrix_t dev_ctm = *stroke_ctm; + cairo_matrix_t dev_ctm_inverse = *stroke_ctm_inverse; + cairo_pattern_union_t fill_pattern, stroke_pattern; + + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (_cairo_surface_wrapper_needs_device_transform (wrapper, + &device_transform)) + { + status = _cairo_path_fixed_init_copy (&path_copy, dev_path); + if (unlikely (status)) + goto FINISH; + + _cairo_path_fixed_transform (&path_copy, &device_transform); + dev_path = &path_copy; + + if (clip != NULL) { + status = _cairo_clip_init_copy_transformed (&clip_copy, clip, + &device_transform); + if (unlikely (status)) + goto FINISH; + + dev_clip = &clip_copy; + } + + cairo_matrix_multiply (&dev_ctm, &dev_ctm, &device_transform); + status = cairo_matrix_invert (&device_transform); + assert (status == CAIRO_STATUS_SUCCESS); + cairo_matrix_multiply (&dev_ctm_inverse, + &device_transform, + &dev_ctm_inverse); + } else { + if (clip != NULL) { + dev_clip = &clip_copy; + _cairo_clip_init_copy (&clip_copy, clip); + } + } + + _cairo_surface_wrapper_transform_pattern (wrapper, &fill_pattern.base, fill_source); + _cairo_surface_wrapper_transform_pattern (wrapper, &stroke_pattern.base, stroke_source); + + status = _cairo_surface_fill_stroke (wrapper->target, + fill_op, &fill_pattern.base, fill_rule, + fill_tolerance, fill_antialias, + dev_path, + stroke_op, &stroke_pattern.base, + stroke_style, + &dev_ctm, &dev_ctm_inverse, + stroke_tolerance, stroke_antialias, + dev_clip); + + FINISH: + if (dev_path != path) + _cairo_path_fixed_fini (dev_path); + if (dev_clip != clip) + _cairo_clip_reset (dev_clip); + return status; +} + +cairo_status_t +_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_matrix_t device_transform; + cairo_path_fixed_t path_copy, *dev_path = path; + cairo_clip_t clip_copy, *dev_clip = clip; + cairo_pattern_union_t source_pattern; + + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (_cairo_surface_wrapper_needs_device_transform (wrapper, + &device_transform)) + { + status = _cairo_path_fixed_init_copy (&path_copy, dev_path); + if (unlikely (status)) + goto FINISH; + + _cairo_path_fixed_transform (&path_copy, &device_transform); + dev_path = &path_copy; + + if (clip != NULL) { + status = _cairo_clip_init_copy_transformed (&clip_copy, clip, + &device_transform); + if (unlikely (status)) + goto FINISH; + + dev_clip = &clip_copy; + } + } else { + if (clip != NULL) { + dev_clip = &clip_copy; + _cairo_clip_init_copy (&clip_copy, clip); + } + } + + _cairo_surface_wrapper_transform_pattern (wrapper, &source_pattern.base, source); + + status = _cairo_surface_fill (wrapper->target, op, + &source_pattern.base, + dev_path, fill_rule, + tolerance, antialias, + dev_clip); + + FINISH: + if (dev_path != path) + _cairo_path_fixed_fini (dev_path); + if (dev_clip != clip) + _cairo_clip_reset (dev_clip); + return status; +} + +cairo_status_t +_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_matrix_t device_transform; + cairo_pattern_union_t source_pattern; + cairo_clip_t clip_copy, *dev_clip = clip; + cairo_glyph_t *dev_glyphs = glyphs; + + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + if (glyphs == NULL || num_glyphs == 0) + return CAIRO_STATUS_SUCCESS; + + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (_cairo_surface_wrapper_needs_device_transform (wrapper, + &device_transform)) + { + int i; + + if (clip != NULL) { + dev_clip = &clip_copy; + status = _cairo_clip_init_copy_transformed (&clip_copy, clip, + &device_transform); + if (unlikely (status)) + goto FINISH; + } + + dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); + if (dev_glyphs == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FINISH; + } + + for (i = 0; i < num_glyphs; i++) { + dev_glyphs[i] = glyphs[i]; + cairo_matrix_transform_point (&device_transform, + &dev_glyphs[i].x, + &dev_glyphs[i].y); + } + } else { + if (clip != NULL) { + dev_clip = &clip_copy; + _cairo_clip_init_copy (&clip_copy, clip); + } + } + + _cairo_surface_wrapper_transform_pattern (wrapper, &source_pattern.base, source); + + status = _cairo_surface_show_text_glyphs (wrapper->target, op, + &source_pattern.base, + utf8, utf8_len, + dev_glyphs, num_glyphs, + clusters, num_clusters, + cluster_flags, + scaled_font, + dev_clip); + FINISH: + if (dev_clip != clip) + _cairo_clip_reset (dev_clip); + if (dev_glyphs != glyphs) + free (dev_glyphs); + return status; +} + +cairo_surface_t * +_cairo_surface_wrapper_create_similar (cairo_surface_wrapper_t *wrapper, + cairo_content_t content, + int width, + int height) +{ + return _cairo_surface_create_similar_solid (wrapper->target, + content, width, height, + CAIRO_COLOR_TRANSPARENT, + TRUE); +} + +cairo_bool_t +_cairo_surface_wrapper_get_extents (cairo_surface_wrapper_t *wrapper, + cairo_rectangle_int_t *extents) +{ + return _cairo_surface_get_extents (wrapper->target, extents); +} + +void +_cairo_surface_wrapper_get_font_options (cairo_surface_wrapper_t *wrapper, + cairo_font_options_t *options) +{ + cairo_surface_get_font_options (wrapper->target, options); +} + +cairo_surface_t * +_cairo_surface_wrapper_snapshot (cairo_surface_wrapper_t *wrapper) +{ + return _cairo_surface_snapshot (wrapper->target); +} + +cairo_bool_t +_cairo_surface_wrapper_has_show_text_glyphs (cairo_surface_wrapper_t *wrapper) +{ + return cairo_surface_has_show_text_glyphs (wrapper->target); +} + +void +_cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper, + cairo_surface_t *target) +{ + wrapper->target = cairo_surface_reference (target); +} + +void +_cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper) +{ + cairo_surface_destroy (wrapper->target); +} diff --git a/gfx/cairo/cairo/src/cairo-surface.c b/gfx/cairo/cairo/src/cairo-surface.c index ed71351b07fc..4a1d6a0a7155 100644 --- a/gfx/cairo/cairo/src/cairo-surface.c +++ b/gfx/cairo/cairo/src/cairo-surface.c @@ -40,7 +40,9 @@ #include "cairo-surface-fallback-private.h" #include "cairo-clip-private.h" -#include "cairo-meta-surface-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-region-private.h" +#include "cairo-tee-surface-private.h" #define DEFINE_NIL_SURFACE(status, name) \ const cairo_surface_t name = { \ @@ -49,8 +51,10 @@ const cairo_surface_t name = { \ CAIRO_CONTENT_COLOR, /* content */ \ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ \ status, /* status */ \ - FALSE, /* finished */ \ 0, /* unique id */ \ + FALSE, /* finished */ \ + TRUE, /* is_clear */ \ + FALSE, /* has_font_options */ \ { 0, 0, 0, NULL, }, /* user_data */ \ { 0, 0, 0, NULL, }, /* mime_data */ \ { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, /* device_transform */ \ @@ -59,9 +63,6 @@ const cairo_surface_t name = { \ 0.0, /* y_resolution */ \ 0.0, /* x_fallback_resolution */ \ 0.0, /* y_fallback_resolution */ \ - NULL, /* clip */ \ - 0, /* next_clip_serial */ \ - 0, /* current_clip_serial */ \ NULL, /* snapshot_of */ \ NULL, /* snapshot_detach */ \ { 0, /* size */ \ @@ -69,7 +70,6 @@ const cairo_surface_t name = { \ 0, /* element_size */ \ NULL, /* elements */ \ }, /* snapshots */ \ - FALSE, /* has_font_options */ \ { CAIRO_ANTIALIAS_DEFAULT, /* antialias */ \ CAIRO_SUBPIXEL_ORDER_DEFAULT, /* subpixel_order */ \ CAIRO_HINT_STYLE_DEFAULT, /* hint_style */ \ @@ -77,8 +77,11 @@ const cairo_surface_t name = { \ } /* font_options */ \ } +/* XXX error object! */ + static DEFINE_NIL_SURFACE(CAIRO_STATUS_NO_MEMORY, _cairo_surface_nil); static DEFINE_NIL_SURFACE(CAIRO_STATUS_SURFACE_TYPE_MISMATCH, _cairo_surface_nil_surface_type_mismatch); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_STATUS, _cairo_surface_nil_invalid_status); static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_CONTENT, _cairo_surface_nil_invalid_content); static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_FORMAT, _cairo_surface_nil_invalid_format); static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_VISUAL, _cairo_surface_nil_invalid_visual); @@ -89,11 +92,6 @@ static DEFINE_NIL_SURFACE(CAIRO_STATUS_WRITE_ERROR, _cairo_surface_nil_write_err static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_STRIDE, _cairo_surface_nil_invalid_stride); static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_SIZE, _cairo_surface_nil_invalid_size); -static cairo_status_t -_cairo_surface_copy_pattern_for_destination (const cairo_pattern_t **pattern, - cairo_surface_t *destination, - cairo_pattern_t *pattern_copy); - /** * _cairo_surface_set_error: * @surface: a surface @@ -191,14 +189,14 @@ slim_hidden_def (cairo_surface_status); static unsigned int _cairo_surface_allocate_unique_id (void) { - static unsigned int unique_id; + static cairo_atomic_int_t unique_id; #if CAIRO_NO_MUTEX if (++unique_id == 0) unique_id = 1; return unique_id; #else - unsigned int old, id; + cairo_atomic_int_t old, id; do { old = _cairo_atomic_uint_get (&unique_id); @@ -346,8 +344,9 @@ _cairo_surface_init (cairo_surface_t *surface, CAIRO_REFERENCE_COUNT_INIT (&surface->ref_count, 1); surface->status = CAIRO_STATUS_SUCCESS; - surface->finished = FALSE; surface->unique_id = _cairo_surface_allocate_unique_id (); + surface->finished = FALSE; + surface->is_clear = FALSE; _cairo_user_data_array_init (&surface->user_data); _cairo_user_data_array_init (&surface->mime_data); @@ -361,43 +360,16 @@ _cairo_surface_init (cairo_surface_t *surface, surface->x_fallback_resolution = CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT; surface->y_fallback_resolution = CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT; - surface->clip = NULL; - surface->next_clip_serial = 0; - surface->current_clip_serial = 0; - _cairo_array_init (&surface->snapshots, sizeof (cairo_surface_t *)); surface->snapshot_of = NULL; surface->has_font_options = FALSE; } -cairo_surface_t * -_cairo_surface_create_similar_scratch (cairo_surface_t *other, - cairo_content_t content, - int width, - int height) +static void +_cairo_surface_copy_similar_properties (cairo_surface_t *surface, + cairo_surface_t *other) { - cairo_surface_t *surface = NULL; - - if (other->status) - return _cairo_surface_create_in_error (other->status); - - if (other->backend->create_similar) { - surface = other->backend->create_similar (other, content, width, height); - if (surface != NULL && surface->status) - return surface; - } - - if (surface == NULL) { - surface = - cairo_image_surface_create (_cairo_format_from_content (content), - width, height); - } - - /* If any error occurred, then return the nil surface we received. */ - if (unlikely (surface->status)) - return surface; - if (other->has_font_options || other->backend != surface->backend) { cairo_font_options_t options; @@ -408,6 +380,28 @@ _cairo_surface_create_similar_scratch (cairo_surface_t *other, cairo_surface_set_fallback_resolution (surface, other->x_fallback_resolution, other->y_fallback_resolution); +} + +cairo_surface_t * +_cairo_surface_create_similar_scratch (cairo_surface_t *other, + cairo_content_t content, + int width, + int height) +{ + cairo_surface_t *surface; + + if (unlikely (other->status)) + return _cairo_surface_create_in_error (other->status); + + if (other->backend->create_similar == NULL) + return NULL; + + surface = other->backend->create_similar (other, + content, width, height); + if (surface == NULL || surface->status) + return surface; + + _cairo_surface_copy_similar_properties (surface, other); return surface; } @@ -443,46 +437,46 @@ cairo_surface_create_similar (cairo_surface_t *other, int width, int height) { - if (other->status) + if (unlikely (other->status)) return _cairo_surface_create_in_error (other->status); - if (! CAIRO_CONTENT_VALID (content)) + if (unlikely (! CAIRO_CONTENT_VALID (content))) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); - return _cairo_surface_create_similar_solid (other, content, - width, height, - CAIRO_COLOR_TRANSPARENT); + return _cairo_surface_create_similar_solid (other, + content, width, height, + CAIRO_COLOR_TRANSPARENT, + TRUE); } -slim_hidden_def (cairo_surface_create_similar); cairo_surface_t * _cairo_surface_create_similar_solid (cairo_surface_t *other, cairo_content_t content, int width, int height, - const cairo_color_t *color) + const cairo_color_t *color, + cairo_bool_t allow_fallback) { cairo_status_t status; cairo_surface_t *surface; - cairo_solid_pattern_t solid_pattern; + cairo_solid_pattern_t pattern; surface = _cairo_surface_create_similar_scratch (other, content, width, height); - if (surface->status) + if (surface == NULL && allow_fallback) + surface = _cairo_image_surface_create_with_content (content, + width, height); + if (surface == NULL || surface->status) return surface; - _cairo_pattern_init_solid (&solid_pattern, color, content); - + _cairo_pattern_init_solid (&pattern, color, content); status = _cairo_surface_paint (surface, color == CAIRO_COLOR_TRANSPARENT ? CAIRO_OPERATOR_CLEAR : CAIRO_OPERATOR_SOURCE, - &solid_pattern.base, NULL); - - _cairo_pattern_fini (&solid_pattern.base); - + &pattern.base, NULL); if (unlikely (status)) { cairo_surface_destroy (surface); - return _cairo_surface_create_in_error (status); + surface = _cairo_surface_create_in_error (status); } return surface; @@ -504,7 +498,8 @@ _cairo_surface_create_solid_pattern_surface (cairo_surface_t *other, return _cairo_surface_create_similar_solid (other, solid_pattern->content, 1, 1, - &solid_pattern->color); + &solid_pattern->color, + FALSE); } cairo_int_status_t @@ -532,17 +527,6 @@ _cairo_surface_repaint_solid_pattern_surface (cairo_surface_t *other, NULL); } -cairo_clip_mode_t -_cairo_surface_get_clip_mode (cairo_surface_t *surface) -{ - if (surface->backend->intersect_clip_path != NULL) - return CAIRO_CLIP_MODE_PATH; - else if (surface->backend->set_clip_region != NULL) - return CAIRO_CLIP_MODE_REGION; - else - return CAIRO_CLIP_MODE_MASK; -} - /** * cairo_surface_reference: * @surface: a #cairo_surface_t @@ -604,36 +588,6 @@ cairo_surface_destroy (cairo_surface_t *surface) } slim_hidden_def(cairo_surface_destroy); -/** - * _cairo_surface_reset: - * @surface: a #cairo_surface_t - * - * Resets the surface back to defaults such that it may be reused in lieu - * of creating a new surface. - **/ -cairo_status_t -_cairo_surface_reset (cairo_surface_t *surface) -{ - if (surface == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) - return CAIRO_STATUS_SUCCESS; - - assert (CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count) == 1); - - _cairo_user_data_array_fini (&surface->user_data); - _cairo_user_data_array_fini (&surface->mime_data); - - if (surface->backend->reset != NULL) { - cairo_status_t status = surface->backend->reset (surface); - if (unlikely (status)) - return _cairo_surface_set_error (surface, status); - } - - _cairo_surface_init (surface, surface->backend, surface->content); - - return CAIRO_STATUS_SUCCESS; -} - /** * cairo_surface_get_reference_count: * @surface: a #cairo_surface_t @@ -1078,14 +1032,9 @@ cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, * call mark_dirty()). */ assert (! _cairo_surface_has_snapshots (surface)); - /* Always reset the clip here, to avoid having external calls to - * clip manipulation functions of the underlying device clip result - * in a desync between the cairo clip and the backend clip, due to - * the clip caching. - */ - surface->current_clip_serial = -1; + surface->is_clear = FALSE; - if (surface->backend->mark_dirty_rectangle) { + if (surface->backend->mark_dirty_rectangle != NULL) { /* XXX: FRAGILE: We're ignoring the scaling component of * device_transform here. I don't know what the right thing to * do would actually be if there were some scaling here, but @@ -1110,8 +1059,8 @@ slim_hidden_def (cairo_surface_mark_dirty_rectangle); * * Private function for setting an extra scale factor to affect all * drawing to a surface. This is used, for example, when replaying a - * meta surface to an image fallback intended for an eventual - * vector-oriented backend. Since the meta surface will record + * recording surface to an image fallback intended for an eventual + * vector-oriented backend. Since the recording surface will record * coordinates in one backend space, but the image fallback uses a * different backend space, (differing by the fallback resolution * scale factors), we need a scale factor correction. @@ -1269,6 +1218,14 @@ cairo_surface_set_fallback_resolution (cairo_surface_t *surface, return; } + if (x_pixels_per_inch <= 0 || y_pixels_per_inch <= 0) { + /* XXX Could delay raising the error until we fallback, but throwing + * the error here means that we can catch the real culprit. + */ + status = _cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_MATRIX); + return; + } + _cairo_surface_begin_modification (surface); surface->x_fallback_resolution = x_pixels_per_inch; @@ -1459,24 +1416,23 @@ _cairo_surface_release_dest_image (cairo_surface_t *surface, } static cairo_status_t -_cairo_meta_surface_clone_similar (cairo_surface_t *surface, - cairo_surface_t *src, - cairo_content_t content, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out) +_cairo_recording_surface_clone_similar (cairo_surface_t *surface, + cairo_surface_t *src, + int src_x, + int src_y, + int width, + int height, + int *clone_offset_x, + int *clone_offset_y, + cairo_surface_t **clone_out) { - cairo_meta_surface_t *meta = (cairo_meta_surface_t *) src; + cairo_recording_surface_t *recorder = (cairo_recording_surface_t *) src; cairo_surface_t *similar; cairo_status_t status; similar = _cairo_surface_has_snapshot (src, surface->backend, - src->content & content); + src->content); if (similar != NULL) { *clone_out = cairo_surface_reference (similar); *clone_offset_x = 0; @@ -1484,32 +1440,36 @@ _cairo_meta_surface_clone_similar (cairo_surface_t *surface, return CAIRO_STATUS_SUCCESS; } - if (width*height*8 < meta->extents.width*meta->extents.height) { - similar = cairo_surface_create_similar (surface, - src->content & content, - width, height); - status = similar->status; - if (unlikely (status)) - return status; + if (recorder->unbounded || + width*height*8 < recorder->extents.width*recorder->extents.height) + { + /* XXX use _solid to perform an initial CLEAR? */ + similar = _cairo_surface_create_similar_scratch (surface, + src->content, + width, height); + if (similar == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (unlikely (similar->status)) + return similar->status; cairo_surface_set_device_offset (similar, -src_x, -src_y); - status = cairo_meta_surface_replay (src, similar); + status = _cairo_recording_surface_replay (src, similar); if (unlikely (status)) { cairo_surface_destroy (similar); return status; } - } else { - similar = cairo_surface_create_similar (surface, - src->content & content, - meta->extents.width, - meta->extents.height); - status = similar->status; - if (unlikely (status)) - return status; + similar = _cairo_surface_create_similar_scratch (surface, + src->content, + recorder->extents.width, + recorder->extents.height); + if (similar == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (unlikely (similar->status)) + return similar->status; - status = cairo_meta_surface_replay (src, similar); + status = _cairo_recording_surface_replay (src, similar); if (unlikely (status)) { cairo_surface_destroy (similar); return status; @@ -1598,7 +1558,6 @@ _wrap_image (cairo_surface_t *src, * _cairo_surface_clone_similar: * @surface: a #cairo_surface_t * @src: the source image - * @content: target content mask * @src_x: extent for the rectangle in src we actually care about * @src_y: extent for the rectangle in src we actually care about * @width: extent for the rectangle in src we actually care about @@ -1618,7 +1577,6 @@ _wrap_image (cairo_surface_t *src, cairo_status_t _cairo_surface_clone_similar (cairo_surface_t *surface, cairo_surface_t *src, - cairo_content_t content, int src_x, int src_y, int width, @@ -1637,28 +1595,35 @@ _cairo_surface_clone_similar (cairo_surface_t *surface, if (unlikely (surface->finished)) return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); - if (surface->backend->clone_similar) { + if (src->type == CAIRO_SURFACE_TYPE_TEE) { + cairo_surface_t *match; + + match = _cairo_tee_surface_find_match (src, + surface->backend, + src->content); + if (match != NULL) + src = match; + } + + if (surface->backend->clone_similar != NULL) { status = surface->backend->clone_similar (surface, src, - content, src_x, src_y, width, height, clone_offset_x, clone_offset_y, clone_out); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { if (_cairo_surface_is_image (src)) return CAIRO_INT_STATUS_UNSUPPORTED; /* First check to see if we can replay to a similar surface */ - if (_cairo_surface_is_meta (src)) { - return _cairo_meta_surface_clone_similar (surface, src, - content, - src_x, src_y, - width, height, - clone_offset_x, - clone_offset_y, - clone_out); + if (_cairo_surface_is_recording (src)) { + return _cairo_recording_surface_clone_similar (surface, src, + src_x, src_y, + width, height, + clone_offset_x, + clone_offset_y, + clone_out); } /* If we failed, try again with an image surface */ @@ -1670,7 +1635,6 @@ _cairo_surface_clone_similar (cairo_surface_t *surface, } else { status = surface->backend->clone_similar (surface, &image->base, - content, src_x, src_y, width, height, clone_offset_x, @@ -1686,7 +1650,6 @@ _cairo_surface_clone_similar (cairo_surface_t *surface, if (status == CAIRO_INT_STATUS_UNSUPPORTED) { status = _cairo_surface_fallback_clone_similar (surface, src, - content, src_x, src_y, width, height, clone_offset_x, @@ -1694,7 +1657,6 @@ _cairo_surface_clone_similar (cairo_surface_t *surface, clone_out); } - /* We should never get UNSUPPORTED here, so if we have an error, bail. */ if (unlikely (status)) return status; @@ -1709,7 +1671,7 @@ _cairo_surface_clone_similar (cairo_surface_t *surface, } /* XXX: Shouldn't really need to do this here. */ -#include "cairo-meta-surface-private.h" +#include "cairo-recording-surface-private.h" /** * _cairo_surface_snapshot @@ -1748,19 +1710,21 @@ _cairo_surface_snapshot (cairo_surface_t *surface) if (surface->backend->snapshot != NULL) { snapshot = surface->backend->snapshot (surface); - if (unlikely (snapshot->status)) - return snapshot; + if (snapshot != NULL) { + if (unlikely (snapshot->status)) + return snapshot; - /* Is this surface just a proxy - e.g. paginated surfaces? */ - if (snapshot->backend != surface->backend) { - cairo_surface_t *previous; + /* Is this surface just a proxy - e.g. paginated surfaces? */ + if (snapshot->backend != surface->backend) { + cairo_surface_t *previous; - previous = _cairo_surface_has_snapshot (surface, - snapshot->backend, - snapshot->content); - if (previous != NULL) { - cairo_surface_destroy (snapshot); - return cairo_surface_reference (previous); + previous = _cairo_surface_has_snapshot (surface, + snapshot->backend, + snapshot->content); + if (previous != NULL) { + cairo_surface_destroy (snapshot); + return cairo_surface_reference (previous); + } } } } @@ -1836,11 +1800,12 @@ _cairo_surface_composite (cairo_operator_t op, int dst_x, int dst_y, unsigned int width, - unsigned int height) + unsigned int height, + cairo_region_t *clip_region) { cairo_int_status_t status; - if (dst->status) + if (unlikely (dst->status)) return dst->status; assert (_cairo_surface_is_writable (dst)); @@ -1858,7 +1823,8 @@ _cairo_surface_composite (cairo_operator_t op, src_x, src_y, mask_x, mask_y, dst_x, dst_y, - width, height); + width, height, + clip_region); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return _cairo_surface_set_error (dst, status); } @@ -1869,7 +1835,8 @@ _cairo_surface_composite (cairo_operator_t op, src_x, src_y, mask_x, mask_y, dst_x, dst_y, - width, height)); + width, height, + clip_region)); } /** @@ -1945,6 +1912,13 @@ _cairo_surface_fill_region (cairo_surface_t *surface, if (num_rects == 0) return CAIRO_STATUS_SUCCESS; + /* catch a common reduction of _cairo_clip_combine_with_surface() */ + if (op == CAIRO_OPERATOR_IN && + _cairo_color_equal (color, CAIRO_COLOR_WHITE)) + { + return CAIRO_STATUS_SUCCESS; + } + if (num_rects > ARRAY_LENGTH (stack_rects)) { rects = _cairo_malloc_ab (num_rects, sizeof (cairo_rectangle_int_t)); @@ -1957,8 +1931,8 @@ _cairo_surface_fill_region (cairo_surface_t *surface, for (i = 0; i < num_rects; i++) cairo_region_get_rectangle (region, i, &rects[i]); - status = _cairo_surface_fill_rectangles (surface, op, - color, rects, num_rects); + status = _cairo_surface_fill_rectangles (surface, + op, color, rects, num_rects); if (rects != stack_rects) free (rects); @@ -2000,14 +1974,16 @@ _cairo_surface_fill_rectangles (cairo_surface_t *surface, return CAIRO_STATUS_SUCCESS; if (surface->backend->fill_rectangles) { - status = surface->backend->fill_rectangles (surface, op, color, + status = surface->backend->fill_rectangles (surface, + op, color, rects, num_rects); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return _cairo_surface_set_error (surface, status); } return _cairo_surface_set_error (surface, - _cairo_surface_fallback_fill_rectangles (surface, op, color, + _cairo_surface_fallback_fill_rectangles (surface, + op, color, rects, num_rects)); } @@ -2015,33 +1991,36 @@ cairo_status_t _cairo_surface_paint (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_status_t status; - cairo_pattern_union_t dev_source; - if (surface->status) + if (unlikely (surface->status)) return surface->status; + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (op == CAIRO_OPERATOR_CLEAR) { + if (surface->is_clear) + return CAIRO_STATUS_SUCCESS; + + if (clip == NULL) + surface->is_clear = TRUE; + } + _cairo_surface_begin_modification (surface); - status = _cairo_surface_copy_pattern_for_destination (&source, - surface, - &dev_source.base); - if (unlikely (status)) - return _cairo_surface_set_error (surface, status); - - if (surface->backend->paint) { - status = surface->backend->paint (surface, op, source, extents); + if (surface->backend->paint != NULL) { + status = surface->backend->paint (surface, op, source, clip); if (status != CAIRO_INT_STATUS_UNSUPPORTED) goto FINISH; } - status = _cairo_surface_fallback_paint (surface, op, source); + status = _cairo_surface_fallback_paint (surface, op, source, clip); FINISH: - if (source == &dev_source.base) - _cairo_pattern_fini (&dev_source.base); + surface->is_clear &= op == CAIRO_OPERATOR_CLEAR; return _cairo_surface_set_error (surface, status); } @@ -2051,44 +2030,38 @@ _cairo_surface_mask (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_status_t status; - cairo_pattern_union_t dev_source; - cairo_pattern_union_t dev_mask; - if (surface->status) + if (unlikely (surface->status)) return surface->status; + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) + return CAIRO_STATUS_SUCCESS; + + /* If the mask is blank, this is just an expensive no-op */ + if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) { + const cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t *) mask; + if (spattern->surface->is_clear) + return CAIRO_STATUS_SUCCESS; + } + _cairo_surface_begin_modification (surface); - status = _cairo_surface_copy_pattern_for_destination (&source, - surface, - &dev_source.base); - if (unlikely (status)) - goto FINISH; - - status = _cairo_surface_copy_pattern_for_destination (&mask, - surface, - &dev_mask.base); - if (unlikely (status)) - goto CLEANUP_SOURCE; - - if (surface->backend->mask) { - status = surface->backend->mask (surface, op, source, mask, extents); + if (surface->backend->mask != NULL) { + status = surface->backend->mask (surface, op, source, mask, clip); if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto CLEANUP_MASK; + goto FINISH; } - status = _cairo_surface_fallback_mask (surface, op, source, mask); + status = _cairo_surface_fallback_mask (surface, op, source, mask, clip); - CLEANUP_MASK: - if (mask == &dev_mask.base) - _cairo_pattern_fini (&dev_mask.base); - CLEANUP_SOURCE: - if (source == &dev_source.base) - _cairo_pattern_fini (&dev_source.base); FINISH: + surface->is_clear &= op == CAIRO_OPERATOR_CLEAR; return _cairo_surface_set_error (surface, status); } @@ -2108,37 +2081,29 @@ _cairo_surface_fill_stroke (cairo_surface_t *surface, cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, cairo_antialias_t stroke_antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_status_t status; - if (surface->status) + if (unlikely (surface->status)) return surface->status; + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (surface->is_clear && + fill_op == CAIRO_OPERATOR_CLEAR && + stroke_op == CAIRO_OPERATOR_CLEAR) + { + return CAIRO_STATUS_SUCCESS; + } + _cairo_surface_begin_modification (surface); if (surface->backend->fill_stroke) { - cairo_pattern_union_t dev_stroke_source; - cairo_pattern_union_t dev_fill_source; cairo_matrix_t dev_ctm = *stroke_ctm; cairo_matrix_t dev_ctm_inverse = *stroke_ctm_inverse; - status = _cairo_surface_copy_pattern_for_destination (&stroke_source, - surface, - &dev_stroke_source.base); - if (unlikely (status)) - return _cairo_surface_set_error (surface, status); - - status = _cairo_surface_copy_pattern_for_destination (&fill_source, - surface, - &dev_fill_source.base); - if (unlikely (status)) { - if (stroke_source == &dev_stroke_source.base) - _cairo_pattern_fini (&dev_stroke_source.base); - - return _cairo_surface_set_error (surface, status); - } - status = surface->backend->fill_stroke (surface, fill_op, fill_source, fill_rule, fill_tolerance, fill_antialias, @@ -2147,30 +2112,30 @@ _cairo_surface_fill_stroke (cairo_surface_t *surface, stroke_style, &dev_ctm, &dev_ctm_inverse, stroke_tolerance, stroke_antialias, - extents); - - if (stroke_source == &dev_stroke_source.base) - _cairo_pattern_fini (&dev_stroke_source.base); - - if (fill_source == &dev_fill_source.base) - _cairo_pattern_fini (&dev_fill_source.base); + clip); if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return _cairo_surface_set_error (surface, status); + goto FINISH; } status = _cairo_surface_fill (surface, fill_op, fill_source, path, - fill_rule, fill_tolerance, fill_antialias, NULL); + fill_rule, fill_tolerance, fill_antialias, + clip); if (unlikely (status)) - return _cairo_surface_set_error (surface, status); + goto FINISH; status = _cairo_surface_stroke (surface, stroke_op, stroke_source, path, stroke_style, stroke_ctm, stroke_ctm_inverse, - stroke_tolerance, stroke_antialias, NULL); + stroke_tolerance, stroke_antialias, + clip); if (unlikely (status)) - return _cairo_surface_set_error (surface, status); + goto FINISH; - return CAIRO_STATUS_SUCCESS; + FINISH: + surface->is_clear &= fill_op == CAIRO_OPERATOR_CLEAR; + surface->is_clear &= stroke_op == CAIRO_OPERATOR_CLEAR; + + return _cairo_surface_set_error (surface, status); } cairo_status_t @@ -2183,31 +2148,27 @@ _cairo_surface_stroke (cairo_surface_t *surface, cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_status_t status; - cairo_pattern_union_t dev_source; - cairo_path_fixed_t *dev_path = path; - cairo_path_fixed_t real_dev_path; - cairo_matrix_t dev_ctm = *ctm; - cairo_matrix_t dev_ctm_inverse = *ctm_inverse; - if (surface->status) + if (unlikely (surface->status)) return surface->status; + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) + return CAIRO_STATUS_SUCCESS; + _cairo_surface_begin_modification (surface); - status = _cairo_surface_copy_pattern_for_destination (&source, - surface, - &dev_source.base); - if (unlikely (status)) - return _cairo_surface_set_error (surface, status); - - if (surface->backend->stroke) { + if (surface->backend->stroke != NULL) { status = surface->backend->stroke (surface, op, source, path, stroke_style, - &dev_ctm, &dev_ctm_inverse, - tolerance, antialias, extents); + ctm, ctm_inverse, + tolerance, antialias, + clip); if (status != CAIRO_INT_STATUS_UNSUPPORTED) goto FINISH; @@ -2215,15 +2176,12 @@ _cairo_surface_stroke (cairo_surface_t *surface, status = _cairo_surface_fallback_stroke (surface, op, source, path, stroke_style, - &dev_ctm, &dev_ctm_inverse, - tolerance, antialias); + ctm, ctm_inverse, + tolerance, antialias, + clip); FINISH: - if (dev_path == &real_dev_path) - _cairo_path_fixed_fini (&real_dev_path); - - if (source == &dev_source.base) - _cairo_pattern_fini (&dev_source.base); + surface->is_clear &= op == CAIRO_OPERATOR_CLEAR; return _cairo_surface_set_error (surface, status); } @@ -2236,26 +2194,26 @@ _cairo_surface_fill (cairo_surface_t *surface, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_status_t status; - cairo_pattern_union_t dev_source; - if (surface->status) + if (unlikely (surface->status)) return surface->status; + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) + return CAIRO_STATUS_SUCCESS; + _cairo_surface_begin_modification (surface); - status = _cairo_surface_copy_pattern_for_destination (&source, - surface, - &dev_source.base); - if (unlikely (status)) - return _cairo_surface_set_error (surface, status); - - if (surface->backend->fill) { + if (surface->backend->fill != NULL) { status = surface->backend->fill (surface, op, source, path, fill_rule, - tolerance, antialias, extents); + tolerance, antialias, + clip); if (status != CAIRO_INT_STATUS_UNSUPPORTED) goto FINISH; @@ -2263,11 +2221,11 @@ _cairo_surface_fill (cairo_surface_t *surface, status = _cairo_surface_fallback_fill (surface, op, source, path, fill_rule, - tolerance, antialias); + tolerance, antialias, + clip); FINISH: - if (source == &dev_source.base) - _cairo_pattern_fini (&dev_source.base); + surface->is_clear &= op == CAIRO_OPERATOR_CLEAR; return _cairo_surface_set_error (surface, status); } @@ -2284,7 +2242,8 @@ _cairo_surface_composite_trapezoids (cairo_operator_t op, unsigned int width, unsigned int height, cairo_trapezoid_t *traps, - int num_traps) + int num_traps, + cairo_region_t *clip_region) { cairo_int_status_t status; @@ -2305,7 +2264,8 @@ _cairo_surface_composite_trapezoids (cairo_operator_t op, src_x, src_y, dst_x, dst_y, width, height, - traps, num_traps); + traps, num_traps, + clip_region); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return _cairo_surface_set_error (dst, status); } @@ -2316,59 +2276,54 @@ _cairo_surface_composite_trapezoids (cairo_operator_t op, src_x, src_y, dst_x, dst_y, width, height, - traps, num_traps)); + traps, num_traps, + clip_region)); } cairo_span_renderer_t * -_cairo_surface_create_span_renderer (cairo_operator_t op, - const cairo_pattern_t *pattern, +_cairo_surface_create_span_renderer (cairo_operator_t op, + const cairo_pattern_t *pattern, cairo_surface_t *dst, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects) + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects, + cairo_region_t *clip_region) { assert (dst->snapshot_of == NULL); - if (dst->status) + if (unlikely (dst->status)) return _cairo_span_renderer_create_in_error (dst->status); - if (dst->finished) + if (unlikely (dst->finished)) return _cairo_span_renderer_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); if (dst->backend->create_span_renderer) { return dst->backend->create_span_renderer (op, pattern, dst, antialias, - rects); + rects, + clip_region); } ASSERT_NOT_REACHED; return _cairo_span_renderer_create_in_error (CAIRO_INT_STATUS_UNSUPPORTED); } cairo_bool_t -_cairo_surface_check_span_renderer (cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *dst, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects) +_cairo_surface_check_span_renderer (cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *dst, + cairo_antialias_t antialias) { - cairo_int_status_t status; - assert (dst->snapshot_of == NULL); + assert (dst->status == CAIRO_STATUS_SUCCESS); + assert (! dst->finished); - if (dst->status) + /* XXX: Currently we have no mono span renderer */ + if (antialias == CAIRO_ANTIALIAS_NONE) return FALSE; - if (dst->finished) { - status = _cairo_surface_set_error (dst, CAIRO_STATUS_SURFACE_FINISHED); - return FALSE; - } + if (dst->backend->check_span_renderer != NULL) + return dst->backend->check_span_renderer (op, pattern, dst, antialias); - if (dst->backend->check_span_renderer) { - return dst->backend->check_span_renderer (op, - pattern, dst, - antialias, - rects); - } return FALSE; } @@ -2448,329 +2403,6 @@ cairo_surface_show_page (cairo_surface_t *surface) } slim_hidden_def (cairo_surface_show_page); -/** - * _cairo_surface_get_current_clip_serial: - * @surface: the #cairo_surface_t to return the serial number for - * - * This space left intentionally blank. - * - * Returns: the serial number associated with the current - * clip in the surface. All gstate functions must - * verify that the correct clip is set in the surface before - * invoking any surface drawing function. - */ -unsigned int -_cairo_surface_get_current_clip_serial (cairo_surface_t *surface) -{ - return surface->current_clip_serial; -} - -/** - * _cairo_surface_allocate_clip_serial: - * @surface: the #cairo_surface_t to allocate a serial number from - * - * Each surface has a separate set of clipping serial numbers, and - * this function allocates one from the specified surface. As zero is - * reserved for the special no-clipping case, this function will not - * return that except for an in-error surface, (ie. surface->status != - * %CAIRO_STATUS_SUCCESS). - */ -unsigned int -_cairo_surface_allocate_clip_serial (cairo_surface_t *surface) -{ - unsigned int serial; - - if (surface->status) - return 0; - - if ((serial = ++(surface->next_clip_serial)) == 0) - serial = ++(surface->next_clip_serial); - return serial; -} - -/** - * _cairo_surface_reset_clip: - * @surface: the #cairo_surface_t to reset the clip on - * - * This function sets the clipping for the surface to - * None, which is to say that drawing is entirely - * unclipped. It also sets the clip serial number - * to zero. - */ -cairo_status_t -_cairo_surface_reset_clip (cairo_surface_t *surface) -{ - cairo_status_t status; - - if (surface->status) - return surface->status; - - if (surface->finished) - return _cairo_surface_set_error (surface,CAIRO_STATUS_SURFACE_FINISHED); - - surface->current_clip_serial = 0; - - if (surface->backend->intersect_clip_path) { - status = surface->backend->intersect_clip_path (surface, - NULL, - CAIRO_FILL_RULE_WINDING, - 0, - CAIRO_ANTIALIAS_DEFAULT); - if (unlikely (status)) - return _cairo_surface_set_error (surface, status); - } - - if (surface->backend->set_clip_region != NULL) { - status = surface->backend->set_clip_region (surface, NULL); - if (unlikely (status)) - return _cairo_surface_set_error (surface, status); - } - - return CAIRO_STATUS_SUCCESS; -} - -/** - * _cairo_surface_set_clip_region: - * @surface: the #cairo_surface_t to reset the clip on - * @region: the #cairo_region_t to use for clipping - * @serial: the clip serial number associated with the region - * - * This function sets the clipping for the surface to - * the specified region and sets the surface clipping - * serial number to the associated serial number. - */ -cairo_status_t -_cairo_surface_set_clip_region (cairo_surface_t *surface, - cairo_region_t *region, - unsigned int serial) -{ - cairo_status_t status; - - if (surface->status) - return surface->status; - - if (surface->finished) - return _cairo_surface_set_error (surface,CAIRO_STATUS_SURFACE_FINISHED); - - assert (surface->backend->set_clip_region != NULL); - - status = surface->backend->set_clip_region (surface, region); - if (unlikely (status)) - return _cairo_surface_set_error (surface, status); - - surface->current_clip_serial = serial; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_int_status_t -_cairo_surface_intersect_clip_path (cairo_surface_t *surface, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_path_fixed_t *dev_path = path; - cairo_status_t status; - - if (surface->status) - return surface->status; - - if (surface->finished) - return _cairo_surface_set_error (surface,CAIRO_STATUS_SURFACE_FINISHED); - - assert (surface->backend->intersect_clip_path != NULL); - - status = surface->backend->intersect_clip_path (surface, - dev_path, - fill_rule, - tolerance, - antialias); - - return _cairo_surface_set_error (surface, status); -} - -static cairo_status_t -_cairo_surface_set_clip_path_recursive (cairo_surface_t *surface, - cairo_clip_path_t *clip_path) -{ - cairo_status_t status; - - if (surface->status) - return surface->status; - - if (clip_path == NULL) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_surface_set_clip_path_recursive (surface, clip_path->prev); - if (unlikely (status)) - return status; - - return _cairo_surface_intersect_clip_path (surface, - &clip_path->path, - clip_path->fill_rule, - clip_path->tolerance, - clip_path->antialias); -} - -/** - * _cairo_surface_set_clip_path: - * @surface: the #cairo_surface_t to set the clip on - * @clip_path: the clip path to set - * @serial: the clip serial number associated with the clip path - * - * Sets the given clipping path for the surface and assigns the - * clipping serial to the surface. - **/ -static cairo_status_t -_cairo_surface_set_clip_path (cairo_surface_t *surface, - cairo_clip_path_t *clip_path, - unsigned int serial) -{ - cairo_status_t status; - - if (surface->status) - return surface->status; - - if (surface->finished) - return _cairo_surface_set_error (surface,CAIRO_STATUS_SURFACE_FINISHED); - - assert (surface->backend->intersect_clip_path != NULL); - - status = surface->backend->intersect_clip_path (surface, - NULL, - CAIRO_FILL_RULE_WINDING, - 0, - CAIRO_ANTIALIAS_DEFAULT); - if (unlikely (status)) - return _cairo_surface_set_error (surface, status); - - status = _cairo_surface_set_clip_path_recursive (surface, clip_path); - if (unlikely (status)) - return _cairo_surface_set_error (surface, status); - - surface->current_clip_serial = serial; - - return CAIRO_STATUS_SUCCESS; -} - - -/** - * _cairo_surface_set_empty_clip_path: - * @surface: the #cairo_surface_t to set the clip on - * @serial: the clip serial number associated with the clip path - * - * Create an empty clip path, one that represents the entire surface clipped - * out, and assigns the given clipping serial to the surface. - **/ -static cairo_status_t -_cairo_surface_set_empty_clip_path (cairo_surface_t *surface, - unsigned int serial) -{ - cairo_path_fixed_t path; - cairo_status_t status; - - if (surface->status) - return surface->status; - - _cairo_path_fixed_init (&path); - - status = surface->backend->intersect_clip_path (surface, - &path, - CAIRO_FILL_RULE_WINDING, - 0, - CAIRO_ANTIALIAS_DEFAULT); - - if (status == CAIRO_STATUS_SUCCESS) - surface->current_clip_serial = serial; - - _cairo_path_fixed_fini (&path); - - return _cairo_surface_set_error (surface, status); -} - -/** - * _cairo_surface_set_empty_clip_region: - * @surface: the #cairo_surface_t to set the clip on - * @serial: the clip serial number associated with the clip path - * - * Create an empty clip region, one that represents the entire surface clipped - * out, and assigns the given clipping serial to the surface. - **/ -static cairo_status_t -_cairo_surface_set_empty_clip_region (cairo_surface_t *surface, - unsigned int serial) -{ - cairo_region_t *region; - cairo_status_t status; - - if (surface->status) - return surface->status; - - region = cairo_region_create (); - status = region->status; - - if (status == CAIRO_STATUS_SUCCESS) - status = _cairo_surface_set_clip_region (surface, region, serial); - - cairo_region_destroy (region); - - return _cairo_surface_set_error (surface, status); -} - -cairo_clip_t * -_cairo_surface_get_clip (cairo_surface_t *surface) -{ - return surface->clip; -} - -cairo_status_t -_cairo_surface_set_clip (cairo_surface_t *surface, cairo_clip_t *clip) -{ - unsigned int serial = 0; - - if (surface->status) - return surface->status; - - if (surface->finished) - return _cairo_surface_set_error (surface,CAIRO_STATUS_SURFACE_FINISHED); - - if (clip) { - serial = clip->serial; - if (serial == 0) - clip = NULL; - } - - surface->clip = clip; - - if (serial == _cairo_surface_get_current_clip_serial (surface)) - return CAIRO_STATUS_SUCCESS; - - if (clip) { - if (clip->all_clipped) { - if (surface->backend->intersect_clip_path != NULL) - return _cairo_surface_set_empty_clip_path (surface, - clip->serial); - - if (surface->backend->set_clip_region != NULL) - return _cairo_surface_set_empty_clip_region (surface, - clip->serial); - } else { - if (clip->path) - return _cairo_surface_set_clip_path (surface, - clip->path, - clip->serial); - - if (clip->region) - return _cairo_surface_set_clip_region (surface, - clip->region, - clip->serial); - } - } - - return _cairo_surface_reset_clip (surface); -} - /** * _cairo_surface_get_extents: * @surface: the #cairo_surface_t to fetch extents for @@ -2780,7 +2412,7 @@ _cairo_surface_set_clip (cairo_surface_t *surface, cairo_clip_t *clip) * possibly be recorded, in other words, it is the maximum extent of * potentially usable coordinates. * - * For vector surfaces, (PDF, PS, SVG and meta-surfaces), the surface + * For vector surfaces, (PDF, PS, SVG and recording-surfaces), the surface * might be conceived as unbounded, but we force the user to provide a * maximum size at the time of surface_create. So get_extents uses * that size. @@ -2795,31 +2427,22 @@ _cairo_surface_set_clip (cairo_surface_t *surface, cairo_clip_t *clip) * This behavior would have to be changed is we ever exported a public * variant of this function. */ -cairo_int_status_t +cairo_bool_t _cairo_surface_get_extents (cairo_surface_t *surface, cairo_rectangle_int_t *extents) { - cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + cairo_bool_t bounded = FALSE; - if (surface->status) - return surface->status; + if (unlikely (surface->status || surface->finished)) + return TRUE; - if (surface->finished) - return _cairo_surface_set_error (surface,CAIRO_STATUS_SURFACE_FINISHED); + if (surface->backend->get_extents != NULL) + bounded = surface->backend->get_extents (surface, extents); - if (surface->backend->get_extents) { - status = _cairo_surface_set_error (surface, - surface->backend->get_extents (surface, extents)); - } + if (! bounded) + _cairo_unbounded_rectangle_init (extents); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - extents->x = CAIRO_RECT_INT_MIN; - extents->y = CAIRO_RECT_INT_MIN; - extents->width = CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN; - extents->height = CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN; - } - - return status; + return bounded; } /** @@ -2891,25 +2514,24 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_status_t status; cairo_scaled_font_t *dev_scaled_font = scaled_font; - cairo_pattern_union_t dev_source; - if (surface->status) + if (unlikely (surface->status)) return surface->status; if (num_glyphs == 0 && utf8_len == 0) return CAIRO_STATUS_SUCCESS; - _cairo_surface_begin_modification (surface); + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; - status = _cairo_surface_copy_pattern_for_destination (&source, - surface, - &dev_source.base); - if (unlikely (status)) - return _cairo_surface_set_error (surface, status); + if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) + return CAIRO_STATUS_SUCCESS; + + _cairo_surface_begin_modification (surface); if (_cairo_surface_has_device_transform (surface) && ! _cairo_matrix_is_integer_translation (&surface->device_transform, NULL, NULL)) @@ -2927,12 +2549,8 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, &font_options); } status = cairo_scaled_font_status (dev_scaled_font); - if (unlikely (status)) { - if (source == &dev_source.base) - _cairo_pattern_fini (&dev_source.base); - + if (unlikely (status)) return _cairo_surface_set_error (surface, status); - } status = CAIRO_INT_STATUS_UNSUPPORTED; @@ -2941,21 +2559,25 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, if (clusters) { /* A real show_text_glyphs call. Try show_text_glyphs backend * method first */ - if (surface->backend->show_text_glyphs) { + if (surface->backend->show_text_glyphs != NULL) { status = surface->backend->show_text_glyphs (surface, op, source, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags, - dev_scaled_font, extents); + dev_scaled_font, + clip); } - if (status == CAIRO_INT_STATUS_UNSUPPORTED && surface->backend->show_glyphs) { + if (status == CAIRO_INT_STATUS_UNSUPPORTED && + surface->backend->show_glyphs) + { int remaining_glyphs = num_glyphs; status = surface->backend->show_glyphs (surface, op, source, glyphs, num_glyphs, dev_scaled_font, - &remaining_glyphs, extents); + clip, + &remaining_glyphs); glyphs += num_glyphs - remaining_glyphs; num_glyphs = remaining_glyphs; if (status == CAIRO_INT_STATUS_UNSUPPORTED && remaining_glyphs == 0) @@ -2963,18 +2585,19 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, } } else { /* A mere show_glyphs call. Try show_glyphs backend method first */ - if (surface->backend->show_glyphs) { + if (surface->backend->show_glyphs != NULL) { int remaining_glyphs = num_glyphs; status = surface->backend->show_glyphs (surface, op, source, glyphs, num_glyphs, dev_scaled_font, - &remaining_glyphs, extents); + clip, + &remaining_glyphs); glyphs += num_glyphs - remaining_glyphs; num_glyphs = remaining_glyphs; if (status == CAIRO_INT_STATUS_UNSUPPORTED && remaining_glyphs == 0) status = CAIRO_STATUS_SUCCESS; - } else if (surface->backend->show_text_glyphs) { + } else if (surface->backend->show_text_glyphs != NULL) { /* Intentionally only try show_text_glyphs method for show_glyphs * calls if backend does not have show_glyphs. If backend has * both methods implemented, we don't fallback from show_glyphs to @@ -2988,21 +2611,23 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags, - dev_scaled_font, extents); + dev_scaled_font, + clip); } } - if (status == CAIRO_INT_STATUS_UNSUPPORTED) + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { status = _cairo_surface_fallback_show_glyphs (surface, op, source, glyphs, num_glyphs, - dev_scaled_font); + dev_scaled_font, + clip); + } if (dev_scaled_font != scaled_font) cairo_scaled_font_destroy (dev_scaled_font); - if (source == &dev_source.base) - _cairo_pattern_fini (&dev_source.base); + surface->is_clear &= op == CAIRO_OPERATOR_CLEAR; return _cairo_surface_set_error (surface, status); } @@ -3024,7 +2649,8 @@ _cairo_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, unsigned int width, unsigned int height, cairo_glyph_t *glyphs, - int num_glyphs) + int num_glyphs, + cairo_region_t *clip_region) { cairo_status_t status; @@ -3039,7 +2665,8 @@ _cairo_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, source_x, source_y, dest_x, dest_y, width, height, - glyphs, num_glyphs); + glyphs, num_glyphs, + clip_region); } else status = CAIRO_INT_STATUS_UNSUPPORTED; @@ -3053,7 +2680,8 @@ _cairo_surface_composite_fixup_unbounded_internal (cairo_surface_t *dst, int dst_x, int dst_y, unsigned int width, - unsigned int height) + unsigned int height, + cairo_region_t *clip_region) { cairo_rectangle_int_t dst_rectangle; cairo_region_t clear_region; @@ -3069,23 +2697,29 @@ _cairo_surface_composite_fixup_unbounded_internal (cairo_surface_t *dst, _cairo_region_init_rectangle (&clear_region, &dst_rectangle); - if (src_rectangle) { + if (clip_region != NULL) { + status = cairo_region_intersect (&clear_region, clip_region); + if (unlikely (status)) + goto CLEANUP_REGIONS; + } + + if (src_rectangle != NULL) { if (! _cairo_rectangle_intersect (&dst_rectangle, src_rectangle)) goto EMPTY; } - if (mask_rectangle) { + if (mask_rectangle != NULL) { if (! _cairo_rectangle_intersect (&dst_rectangle, mask_rectangle)) goto EMPTY; } /* Now compute the area that is in dst but not drawn */ status = cairo_region_subtract_rectangle (&clear_region, &dst_rectangle); - if (unlikely (status)) + if (unlikely (status) || cairo_region_is_empty (&clear_region)) goto CLEANUP_REGIONS; EMPTY: - status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_SOURCE, + status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, &clear_region); @@ -3134,13 +2768,14 @@ _cairo_surface_composite_fixup_unbounded (cairo_surface_t *dst, int dst_x, int dst_y, unsigned int width, - unsigned int height) + unsigned int height, + cairo_region_t *clip_region) { cairo_rectangle_int_t src_tmp, mask_tmp; cairo_rectangle_int_t *src_rectangle = NULL; cairo_rectangle_int_t *mask_rectangle = NULL; - if (dst->status) + if (unlikely (dst->status)) return dst->status; assert (_cairo_surface_is_writable (dst)); @@ -3172,7 +2807,8 @@ _cairo_surface_composite_fixup_unbounded (cairo_surface_t *dst, } return _cairo_surface_composite_fixup_unbounded_internal (dst, src_rectangle, mask_rectangle, - dst_x, dst_y, width, height); + dst_x, dst_y, width, height, + clip_region); } /** @@ -3212,11 +2848,11 @@ _cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t *dst, int dst_x, int dst_y, unsigned int width, - unsigned int height) + unsigned int height, + cairo_region_t *clip_region) { - cairo_rectangle_int_t src_tmp, mask_tmp; - cairo_rectangle_int_t *src_rectangle = NULL; - cairo_rectangle_int_t *mask_rectangle = NULL; + cairo_rectangle_int_t src_tmp, *src= NULL; + cairo_rectangle_int_t mask; if (dst->status) return dst->status; @@ -3231,52 +2867,20 @@ _cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t *dst, { src_tmp.x = (dst_x - (src_x + src_attr->x_offset)); src_tmp.y = (dst_y - (src_y + src_attr->y_offset)); - src_tmp.width = src_width; + src_tmp.width = src_width; src_tmp.height = src_height; - src_rectangle = &src_tmp; + src = &src_tmp; } - mask_tmp.x = dst_x - mask_x; - mask_tmp.y = dst_y - mask_y; - mask_tmp.width = mask_width; - mask_tmp.height = mask_height; + mask.x = dst_x - mask_x; + mask.y = dst_y - mask_y; + mask.width = mask_width; + mask.height = mask_height; - mask_rectangle = &mask_tmp; - - return _cairo_surface_composite_fixup_unbounded_internal (dst, src_rectangle, mask_rectangle, - dst_x, dst_y, width, height); -} - -/** - * _cairo_surface_copy_pattern_for_destination - * @pattern: the pattern to copy - * @destination: the destination surface for which the pattern is being copied - * @pattern_copy: the location to hold the copy - * - * Copies the given pattern, taking into account device scale and offsets - * of the destination surface. - */ -static cairo_status_t -_cairo_surface_copy_pattern_for_destination (const cairo_pattern_t **pattern, - cairo_surface_t *destination, - cairo_pattern_t *pattern_copy) -{ - cairo_status_t status; - - if (! _cairo_surface_has_device_transform (destination)) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_pattern_init_copy (pattern_copy, *pattern); - if (unlikely (status)) - return status; - - _cairo_pattern_transform (pattern_copy, - &destination->device_transform_inverse); - - - *pattern = pattern_copy; - return CAIRO_STATUS_SUCCESS; + return _cairo_surface_composite_fixup_unbounded_internal (dst, src, &mask, + dst_x, dst_y, width, height, + clip_region); } /** @@ -3301,6 +2905,168 @@ _cairo_surface_set_resolution (cairo_surface_t *surface, surface->y_resolution = y_res; } +/* Generic methods for determining operation extents. */ + +static void +_rectangle_intersect_clip (cairo_rectangle_int_t *extents, cairo_clip_t *clip) +{ + const cairo_rectangle_int_t *clip_extents; + cairo_bool_t is_empty; + + clip_extents = NULL; + if (clip != NULL) + clip_extents = _cairo_clip_get_extents (clip); + + if (clip_extents != NULL) + is_empty = _cairo_rectangle_intersect (extents, clip_extents); +} + +static void +_cairo_surface_operation_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + cairo_bool_t is_empty; + + is_empty = _cairo_surface_get_extents (surface, extents); + + if (_cairo_operator_bounded_by_source (op)) { + cairo_rectangle_int_t source_extents; + + _cairo_pattern_get_extents (source, &source_extents); + is_empty = _cairo_rectangle_intersect (extents, &source_extents); + } + + _rectangle_intersect_clip (extents, clip); +} + +cairo_status_t +_cairo_surface_paint_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + _cairo_surface_operation_extents (surface, op, source, clip, extents); + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_surface_mask_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + cairo_bool_t is_empty; + + _cairo_surface_operation_extents (surface, op, source, clip, extents); + + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t mask_extents; + + _cairo_pattern_get_extents (mask, &mask_extents); + is_empty = _cairo_rectangle_intersect (extents, &mask_extents); + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_surface_stroke_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + cairo_status_t status; + cairo_bool_t is_empty; + + _cairo_surface_operation_extents (surface, op, source, clip, extents); + + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t mask_extents; + + status = _cairo_path_fixed_stroke_extents (path, style, + ctm, ctm_inverse, + tolerance, + &mask_extents); + if (unlikely (status)) + return status; + + is_empty = _cairo_rectangle_intersect (extents, &mask_extents); + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_surface_fill_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + cairo_bool_t is_empty; + + _cairo_surface_operation_extents (surface, op, source, clip, extents); + + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t mask_extents; + + _cairo_path_fixed_fill_extents (path, fill_rule, tolerance, + &mask_extents); + is_empty = _cairo_rectangle_intersect (extents, &mask_extents); + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_surface_glyphs_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + cairo_status_t status; + cairo_bool_t is_empty; + + _cairo_surface_operation_extents (surface, op, source, clip, extents); + + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t glyph_extents; + + status = _cairo_scaled_font_glyph_device_extents (scaled_font, + glyphs, + num_glyphs, + &glyph_extents, + NULL); + if (unlikely (status)) + return status; + + is_empty = _cairo_rectangle_intersect (extents, &glyph_extents); + } + + return CAIRO_STATUS_SUCCESS; +} + cairo_surface_t * _cairo_surface_create_in_error (cairo_status_t status) { @@ -3309,6 +3075,8 @@ _cairo_surface_create_in_error (cairo_status_t status) return (cairo_surface_t *) &_cairo_surface_nil; case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: return (cairo_surface_t *) &_cairo_surface_nil_surface_type_mismatch; + case CAIRO_STATUS_INVALID_STATUS: + return (cairo_surface_t *) &_cairo_surface_nil_invalid_status; case CAIRO_STATUS_INVALID_CONTENT: return (cairo_surface_t *) &_cairo_surface_nil_invalid_content; case CAIRO_STATUS_INVALID_FORMAT: @@ -3335,7 +3103,6 @@ _cairo_surface_create_in_error (cairo_status_t status) case CAIRO_STATUS_INVALID_POP_GROUP: case CAIRO_STATUS_NO_CURRENT_POINT: case CAIRO_STATUS_INVALID_MATRIX: - case CAIRO_STATUS_INVALID_STATUS: case CAIRO_STATUS_NULL_POINTER: case CAIRO_STATUS_INVALID_STRING: case CAIRO_STATUS_INVALID_PATH_DATA: diff --git a/gfx/cairo/cairo/src/cairo-svg-surface-private.h b/gfx/cairo/cairo/src/cairo-svg-surface-private.h index e7cd4db8cf05..64efe7f2a226 100644 --- a/gfx/cairo/cairo/src/cairo-svg-surface-private.h +++ b/gfx/cairo/cairo/src/cairo-svg-surface-private.h @@ -44,6 +44,7 @@ #include "cairo-svg.h" #include "cairo-surface-private.h" +#include "cairo-surface-clipper-private.h" typedef struct cairo_svg_document cairo_svg_document_t; @@ -52,8 +53,6 @@ typedef struct cairo_svg_surface { cairo_content_t content; - unsigned int id; - double width; double height; @@ -62,6 +61,7 @@ typedef struct cairo_svg_surface { cairo_output_stream_t *xml_node; cairo_array_t page_set; + cairo_surface_clipper_t clipper; unsigned int clip_level; unsigned int base_clip; cairo_bool_t is_base_clip_emitted; diff --git a/gfx/cairo/cairo/src/cairo-svg-surface.c b/gfx/cairo/cairo/src/cairo-svg-surface.c index 15e368452e44..fa25b821e2ca 100644 --- a/gfx/cairo/cairo/src/cairo-svg-surface.c +++ b/gfx/cairo/cairo/src/cairo-svg-surface.c @@ -44,11 +44,12 @@ #include "cairo-svg.h" #include "cairo-analysis-surface-private.h" #include "cairo-image-info-private.h" -#include "cairo-meta-surface-private.h" +#include "cairo-recording-surface-private.h" #include "cairo-output-stream-private.h" #include "cairo-path-fixed-private.h" #include "cairo-paginated-private.h" #include "cairo-scaled-font-subsets-private.h" +#include "cairo-surface-clipper-private.h" #include "cairo-svg-surface-private.h" typedef struct cairo_svg_page cairo_svg_page_t; @@ -63,6 +64,11 @@ static const cairo_svg_version_t _cairo_svg_versions[] = #define CAIRO_SVG_VERSION_LAST ARRAY_LENGTH (_cairo_svg_versions) +static void +_cairo_svg_surface_emit_path (cairo_output_stream_t *output, + cairo_path_fixed_t *path, + cairo_matrix_t *ctm_inverse); + static cairo_bool_t _cairo_svg_version_has_page_set_support (cairo_svg_version_t version) { @@ -99,7 +105,6 @@ struct cairo_svg_document { cairo_output_stream_t *xml_node_defs; cairo_output_stream_t *xml_node_glyphs; - unsigned int surface_id; unsigned int linear_pattern_id; unsigned int radial_pattern_id; unsigned int pattern_id; @@ -109,18 +114,11 @@ struct cairo_svg_document { cairo_bool_t alpha_filter; - cairo_array_t meta_snapshots; - cairo_svg_version_t svg_version; cairo_scaled_font_subsets_t *font_subsets; }; -typedef struct { - unsigned int id; - cairo_meta_surface_t *meta; -} cairo_meta_snapshot_t; - static cairo_status_t _cairo_svg_document_create (cairo_output_stream_t *stream, double width, @@ -278,8 +276,8 @@ _extract_svg_surface (cairo_surface_t *surface, * Since: 1.2 **/ void -cairo_svg_surface_restrict_to_version (cairo_surface_t *abstract_surface, - cairo_svg_version_t version) +cairo_svg_surface_restrict_to_version (cairo_surface_t *abstract_surface, + cairo_svg_version_t version) { cairo_svg_surface_t *surface = NULL; /* hide compiler warning */ cairo_status_t status; @@ -306,7 +304,7 @@ cairo_svg_surface_restrict_to_version (cairo_surface_t *abstract_surface, **/ void cairo_svg_get_versions (cairo_svg_version_t const **versions, - int *num_versions) + int *num_versions) { if (versions != NULL) *versions = _cairo_svg_versions; @@ -336,6 +334,73 @@ cairo_svg_version_to_string (cairo_svg_version_t version) return _cairo_svg_version_strings[version]; } +static cairo_bool_t +_cliprect_covers_surface (cairo_svg_surface_t *surface, + cairo_path_fixed_t *path) +{ + cairo_box_t box; + + if (_cairo_path_fixed_is_box (path, &box)) { + if (box.p1.x <= 0 && + box.p1.y <= 0 && + _cairo_fixed_to_double (box.p2.x) >= surface->width && + _cairo_fixed_to_double (box.p2.y) >= surface->height) + { + return TRUE; + } + } + + return FALSE; +} + +static cairo_status_t +_cairo_svg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_svg_surface_t *surface = cairo_container_of (clipper, + cairo_svg_surface_t, + clipper); + cairo_svg_document_t *document = surface->document; + unsigned int i; + + if (path == NULL) { + for (i = 0; i < surface->clip_level; i++) + _cairo_output_stream_printf (surface->xml_node, "\n"); + + surface->clip_level = 0; + return CAIRO_STATUS_SUCCESS; + } + + /* skip trivial whole-page clips */ + if (_cliprect_covers_surface (surface, path)) + return CAIRO_STATUS_SUCCESS; + + _cairo_output_stream_printf (document->xml_node_defs, + "\n" + " clip_id); + _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL); + + _cairo_output_stream_printf (document->xml_node_defs, + "/>\n" + "\n"); + + _cairo_output_stream_printf (surface->xml_node, + "\n", + document->clip_id, + fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? + "evenodd" : "nonzero"); + + document->clip_id++; + surface->clip_level++; + + return CAIRO_STATUS_SUCCESS; +} + static cairo_surface_t * _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, cairo_content_t content, @@ -359,8 +424,9 @@ _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, surface->document = _cairo_svg_document_reference (document); surface->clip_level = 0; + _cairo_surface_clipper_init (&surface->clipper, + _cairo_svg_surface_clipper_intersect_clip_path); - surface->id = document->surface_id++; surface->base_clip = document->clip_id++; surface->is_base_clip_emitted = FALSE; @@ -388,8 +454,6 @@ _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, paginated = _cairo_paginated_surface_create (&surface->base, surface->content, - surface->width, - surface->height, &cairo_svg_surface_paginated_backend); status = paginated->status; if (status == CAIRO_STATUS_SUCCESS) { @@ -457,7 +521,7 @@ _cairo_svg_surface_store_page (cairo_svg_surface_t *surface) return NULL; } - page.surface_id = surface->id; + page.surface_id = surface->base.unique_id; page.clip_level = surface->clip_level; page.xml_node = surface->xml_node; @@ -468,11 +532,13 @@ _cairo_svg_surface_store_page (cairo_svg_surface_t *surface) surface->xml_node = stream; surface->clip_level = 0; - for (i = 0; i < page.clip_level; i++) _cairo_output_stream_printf (page.xml_node, "\n"); - return _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1); + _cairo_surface_clipper_reset (&surface->clipper); + + return _cairo_array_index (&surface->page_set, + surface->page_set.num_elements - 1); } static cairo_int_status_t @@ -486,7 +552,6 @@ _cairo_svg_surface_copy_page (void *abstract_surface) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_memory_stream_copy (page->xml_node, surface->xml_node); - surface->clip_level = page->clip_level; return CAIRO_STATUS_SUCCESS; } @@ -596,7 +661,7 @@ _cairo_svg_path_close_path (void *closure) return CAIRO_STATUS_SUCCESS; } -static cairo_status_t +static void _cairo_svg_surface_emit_path (cairo_output_stream_t *output, cairo_path_fixed_t *path, cairo_matrix_t *ctm_inverse) @@ -615,12 +680,9 @@ _cairo_svg_surface_emit_path (cairo_output_stream_t *output, _cairo_svg_path_curve_to, _cairo_svg_path_close_path, &info); - if (unlikely (status)) - return status; + assert (status == CAIRO_STATUS_SUCCESS); _cairo_output_stream_printf (output, "\""); - - return status; } static cairo_int_status_t @@ -642,9 +704,8 @@ _cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document, _cairo_output_stream_printf (document->xml_node_glyphs, "xml_node_glyphs, scaled_glyph->path, NULL); - if (unlikely (status)) - return status; + _cairo_svg_surface_emit_path (document->xml_node_glyphs, + scaled_glyph->path, NULL); _cairo_output_stream_printf (document->xml_node_glyphs, "/>\n"); @@ -787,6 +848,12 @@ _cairo_svg_surface_operators[] = { "xor", "plus", "color-dodge", /* FIXME: saturate ? */ + + "multiply", "screen", "overlay", + "darken", "lighten", + "color-dodge", "color-burn", + "hard-light", "soft-light", + "difference", "exclusion" }; static cairo_bool_t @@ -841,22 +908,7 @@ _cairo_svg_surface_operation_supported (cairo_svg_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *pattern) { - if (_cairo_svg_surface_analyze_operation (surface, op, pattern) - != CAIRO_INT_STATUS_UNSUPPORTED) - { - return TRUE; - } else { - return FALSE; - } -} - -static cairo_surface_t * -_cairo_svg_surface_create_similar (void *abstract_src, - cairo_content_t content, - int width, - int height) -{ - return cairo_meta_surface_create (content, width, height); + return _cairo_svg_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_status_t @@ -887,6 +939,8 @@ _cairo_svg_surface_finish (void *abstract_surface) } _cairo_array_fini (&surface->page_set); + _cairo_surface_clipper_reset (&surface->clipper); + status2 = _cairo_svg_document_destroy (document); if (status == CAIRO_STATUS_SUCCESS) status = status2; @@ -894,6 +948,7 @@ _cairo_svg_surface_finish (void *abstract_surface) return status; } + static void _cairo_svg_surface_emit_alpha_filter (cairo_svg_document_t *document) { @@ -1114,112 +1169,139 @@ _cairo_svg_surface_emit_operator_for_style (cairo_output_stream_t *output, } static cairo_status_t -_cairo_svg_surface_emit_composite_image_pattern (cairo_output_stream_t *output, - cairo_svg_surface_t *svg_surface, - cairo_operator_t op, - cairo_surface_pattern_t *pattern, - int pattern_id, - const cairo_matrix_t *parent_matrix, - const char *extra_attributes) +_cairo_svg_surface_emit_surface (cairo_svg_document_t *document, + cairo_surface_t *surface) { cairo_rectangle_int_t extents; + cairo_bool_t is_bounded; cairo_status_t status; - cairo_matrix_t p2u; - status = _cairo_surface_get_extents (pattern->surface, &extents); + if (_cairo_user_data_array_get_data (&surface->user_data, + (cairo_user_data_key_t *) document)) + { + return CAIRO_STATUS_SUCCESS; + } + + is_bounded = _cairo_surface_get_extents (surface, &extents); + assert (is_bounded); + + _cairo_output_stream_printf (document->xml_node_defs, + "unique_id, + extents.width, extents.height); + + _cairo_output_stream_printf (document->xml_node_defs, " xlink:href=\""); + + status = _cairo_surface_base64_encode (surface, + document->xml_node_defs); if (unlikely (status)) return status; + _cairo_output_stream_printf (document->xml_node_defs, "\"/>\n"); + + /* and tag it */ + return _cairo_user_data_array_set_data (&surface->user_data, + (cairo_user_data_key_t *) document, + document, NULL); +} + +static cairo_status_t +_cairo_svg_surface_emit_composite_surface_pattern (cairo_output_stream_t *output, + cairo_svg_surface_t *svg_surface, + cairo_operator_t op, + cairo_surface_pattern_t *pattern, + int pattern_id, + const cairo_matrix_t *parent_matrix, + const char *extra_attributes) +{ + cairo_status_t status; + cairo_matrix_t p2u; + p2u = pattern->base.matrix; status = cairo_matrix_invert (&p2u); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); + status = _cairo_svg_surface_emit_surface (svg_surface->document, + pattern->surface); + if (unlikely (status)) + return status; + if (pattern_id != invalid_pattern_id) { + cairo_rectangle_int_t extents; + cairo_bool_t is_bounded; + + is_bounded = _cairo_surface_get_extents (pattern->surface, &extents); + assert (is_bounded); + _cairo_output_stream_printf (output, "\n"); + _cairo_svg_surface_emit_transform (output, + " patternTransform", + &p2u, parent_matrix); + _cairo_output_stream_printf (output, ">\n "); } _cairo_output_stream_printf (output, - " surface->unique_id); if (extra_attributes) _cairo_output_stream_printf (output, " %s", extra_attributes); - _cairo_output_stream_printf (output, " xlink:href=\""); + if (pattern_id == invalid_pattern_id) { + _cairo_svg_surface_emit_operator (output, svg_surface, op); + _cairo_svg_surface_emit_transform (output, + " transform", + &p2u, parent_matrix); + } + _cairo_output_stream_printf (output, "/>\n"); - status = _cairo_surface_base64_encode (pattern->surface, output); - - _cairo_output_stream_printf (output, "\"/>\n"); if (pattern_id != invalid_pattern_id) _cairo_output_stream_printf (output, "\n"); - return status; + return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_svg_surface_emit_meta_surface (cairo_svg_document_t *document, - cairo_meta_surface_t *surface, - int *id) +_cairo_svg_surface_emit_recording_surface (cairo_svg_document_t *document, + cairo_recording_surface_t *source) { cairo_status_t status; cairo_surface_t *paginated_surface; cairo_svg_surface_t *svg_surface; - cairo_meta_snapshot_t new_snapshot; cairo_array_t *page_set; cairo_output_stream_t *contents; - cairo_meta_surface_t *meta; - cairo_meta_snapshot_t *snapshot; - unsigned int num_elements; - unsigned int i; - /* search in already emitted meta snapshots */ - num_elements = document->meta_snapshots.num_elements; - for (i = 0; i < num_elements; i++) { - snapshot = _cairo_array_index (&document->meta_snapshots, i); - meta = snapshot->meta; - if (meta->commands.num_elements == surface->commands.num_elements && - _cairo_array_index (&meta->commands, 0) == _cairo_array_index (&surface->commands, 0)) { - *id = snapshot->id; - return CAIRO_STATUS_SUCCESS; - } + if (_cairo_user_data_array_get_data (&source->base.user_data, + (cairo_user_data_key_t *) document)) + { + return CAIRO_STATUS_SUCCESS; } - meta = (cairo_meta_surface_t *) _cairo_surface_snapshot (&surface->base); - if (unlikely (meta->base.status)) - return meta->base.status; - paginated_surface = _cairo_svg_surface_create_for_document (document, - meta->content, - meta->width_pixels, - meta->height_pixels); - if (paginated_surface->status) { - cairo_surface_destroy (&meta->base); + source->content, + source->extents_pixels.width, + source->extents_pixels.height); + if (unlikely (paginated_surface->status)) return paginated_surface->status; - } - svg_surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (paginated_surface); + svg_surface = (cairo_svg_surface_t *) + _cairo_paginated_surface_get_target (paginated_surface); cairo_surface_set_fallback_resolution (paginated_surface, document->owner->x_fallback_resolution, document->owner->y_fallback_resolution); + cairo_surface_set_device_offset (&svg_surface->base, + -source->extents_pixels.x, + -source->extents_pixels.y); - status = cairo_meta_surface_replay (&meta->base, paginated_surface); + status = _cairo_recording_surface_replay (&source->base, paginated_surface); if (unlikely (status)) { - cairo_surface_destroy (&meta->base); cairo_surface_destroy (paginated_surface); return status; } @@ -1227,21 +1309,11 @@ _cairo_svg_surface_emit_meta_surface (cairo_svg_document_t *document, cairo_surface_show_page (paginated_surface); status = cairo_surface_status (paginated_surface); if (unlikely (status)) { - cairo_surface_destroy (&meta->base); cairo_surface_destroy (paginated_surface); return status; } - new_snapshot.meta = meta; - new_snapshot.id = svg_surface->id; - status = _cairo_array_append (&document->meta_snapshots, &new_snapshot); - if (unlikely (status)) { - cairo_surface_destroy (&meta->base); - cairo_surface_destroy (paginated_surface); - return status; - } - - if (!svg_surface->is_base_clip_emitted) { + if (! svg_surface->is_base_clip_emitted) { svg_surface->is_base_clip_emitted = TRUE; _cairo_output_stream_printf (document->xml_node_defs, "\n" @@ -1252,19 +1324,19 @@ _cairo_svg_surface_emit_meta_surface (cairo_svg_document_t *document, svg_surface->height); } - if (meta->content == CAIRO_CONTENT_ALPHA) { + if (source->content == CAIRO_CONTENT_ALPHA) { _cairo_svg_surface_emit_alpha_filter (document); _cairo_output_stream_printf (document->xml_node_defs, "\n", - svg_surface->id, + source->base.unique_id, svg_surface->base_clip); } else { _cairo_output_stream_printf (document->xml_node_defs, "\n", - svg_surface->id, + source->base.unique_id, svg_surface->base_clip); } @@ -1287,44 +1359,39 @@ _cairo_svg_surface_emit_meta_surface (cairo_svg_document_t *document, _cairo_output_stream_printf (document->xml_node_defs, "\n"); - *id = new_snapshot.id; - status = cairo_surface_status (paginated_surface); cairo_surface_destroy (paginated_surface); - /* FIXME: cairo_paginated_surface doesn't take a ref to the - * passed in target surface so we can't call destroy here. - * cairo_paginated_surface should be fixed, but for now just - * work around it. */ + if (unlikely (status)) + return status; - /* cairo_surface_destroy (svg_surface); */ - - return status; + /* and tag it */ + return _cairo_user_data_array_set_data (&source->base.user_data, + (cairo_user_data_key_t *) document, + document, NULL); } static cairo_status_t -_cairo_svg_surface_emit_composite_meta_pattern (cairo_output_stream_t *output, - cairo_svg_surface_t *surface, - cairo_operator_t op, - cairo_surface_pattern_t *pattern, - int pattern_id, - const cairo_matrix_t *parent_matrix, - const char *extra_attributes) +_cairo_svg_surface_emit_composite_recording_pattern (cairo_output_stream_t *output, + cairo_svg_surface_t *surface, + cairo_operator_t op, + cairo_surface_pattern_t *pattern, + int pattern_id, + const cairo_matrix_t *parent_matrix, + const char *extra_attributes) { cairo_svg_document_t *document = surface->document; - cairo_meta_surface_t *meta_surface; + cairo_recording_surface_t *recording_surface; cairo_matrix_t p2u; cairo_status_t status; - int id = 0; p2u = pattern->base.matrix; status = cairo_matrix_invert (&p2u); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); - meta_surface = (cairo_meta_surface_t *) pattern->surface; - - status = _cairo_svg_surface_emit_meta_surface (document, meta_surface, &id); + recording_surface = (cairo_recording_surface_t *) pattern->surface; + status = _cairo_svg_surface_emit_recording_surface (document, recording_surface); if (unlikely (status)) return status; @@ -1334,15 +1401,15 @@ _cairo_svg_surface_emit_composite_meta_pattern (cairo_output_stream_t *output, "patternUnits=\"userSpaceOnUse\" " "width=\"%d\" height=\"%d\"", pattern_id, - (int) ceil (meta_surface->width_pixels), - (int) ceil (meta_surface->height_pixels)); + recording_surface->extents.width, + recording_surface->extents.height); _cairo_svg_surface_emit_transform (output, " patternTransform", &p2u, parent_matrix); _cairo_output_stream_printf (output, ">\n"); } _cairo_output_stream_printf (output, "base.unique_id); if (pattern_id == invalid_pattern_id) { _cairo_svg_surface_emit_operator (output, surface, op); @@ -1370,13 +1437,19 @@ _cairo_svg_surface_emit_composite_pattern (cairo_output_stream_t *output, const char *extra_attributes) { - if (_cairo_surface_is_meta (pattern->surface)) { - return _cairo_svg_surface_emit_composite_meta_pattern (output, surface, op, pattern, - pattern_id, parent_matrix, extra_attributes); + if (_cairo_surface_is_recording (pattern->surface)) { + return _cairo_svg_surface_emit_composite_recording_pattern (output, surface, + op, pattern, + pattern_id, + parent_matrix, + extra_attributes); } - return _cairo_svg_surface_emit_composite_image_pattern (output, surface, op, pattern, - pattern_id, parent_matrix, extra_attributes); + return _cairo_svg_surface_emit_composite_surface_pattern (output, surface, + op, pattern, + pattern_id, + parent_matrix, + extra_attributes); } static cairo_status_t @@ -1710,7 +1783,9 @@ _cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface, document->radial_pattern_id, x1, y1, x1, y1, r1); - _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix); + _cairo_svg_surface_emit_transform (document->xml_node_defs, + "gradientTransform", + &p2u, parent_matrix); _cairo_output_stream_printf (document->xml_node_defs, ">\n"); if (extend == CAIRO_EXTEND_NONE || n_stops < 1) @@ -1975,11 +2050,15 @@ _cairo_svg_surface_fill_stroke (void *abstract_surface, cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, cairo_antialias_t stroke_antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_svg_surface_t *surface = abstract_surface; cairo_status_t status; + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + _cairo_output_stream_printf (surface->xml_node, "xml_node, surface, fill_op, fill_source, fill_rule, stroke_ctm_inverse); @@ -1993,9 +2072,7 @@ _cairo_svg_surface_fill_stroke (void *abstract_surface, _cairo_output_stream_printf (surface->xml_node, "\" "); - status = _cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse); - if (unlikely (status)) - return status; + _cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse); _cairo_svg_surface_emit_transform (surface->xml_node, " transform", stroke_ctm, NULL); _cairo_output_stream_printf (surface->xml_node, "/>\n"); @@ -2011,7 +2088,7 @@ _cairo_svg_surface_fill (void *abstract_surface, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_svg_surface_t *surface = abstract_surface; cairo_status_t status; @@ -2021,6 +2098,10 @@ _cairo_svg_surface_fill (void *abstract_surface, assert (_cairo_svg_surface_operation_supported (surface, op, source)); + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + _cairo_output_stream_printf (surface->xml_node, "xml_node, surface, op, source, fill_rule, NULL); if (unlikely (status)) @@ -2028,16 +2109,14 @@ _cairo_svg_surface_fill (void *abstract_surface, _cairo_output_stream_printf (surface->xml_node, "\" "); - status = _cairo_svg_surface_emit_path (surface->xml_node, path, NULL); - if (unlikely (status)) - return status; + _cairo_svg_surface_emit_path (surface->xml_node, path, NULL); _cairo_output_stream_printf (surface->xml_node, "/>\n"); return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t +static cairo_bool_t _cairo_svg_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { @@ -2047,13 +2126,13 @@ _cairo_svg_surface_get_extents (void *abstract_surface, rectangle->y = 0; /* XXX: The conversion to integers here is pretty bogus, (not to - * mention the aribitray limitation of width to a short(!). We + * mention the arbitrary limitation of width to a short(!). We * may need to come up with a better interface for get_size. */ - rectangle->width = (int) ceil (surface->width); - rectangle->height = (int) ceil (surface->height); + rectangle->width = ceil (surface->width); + rectangle->height = ceil (surface->height); - return CAIRO_STATUS_SUCCESS; + return TRUE; } static cairo_status_t @@ -2100,52 +2179,63 @@ static cairo_int_status_t _cairo_svg_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_status_t status; cairo_svg_surface_t *surface = abstract_surface; - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_svg_surface_analyze_operation (surface, op, source); - - assert (_cairo_svg_surface_operation_supported (surface, op, source)); - /* Emulation of clear and source operators, when no clipping region * is defined. We just delete existing content of surface root node, * and exit early if operator is clear. - * XXX: optimization of SOURCE operator doesn't work, since analyze - * above always return FALSE. In order to make it work, we need a way - * to know if there's an active clipping path. - * Optimization of CLEAR works because of a test in paginated surface, - * and an optimization in meta surface. */ - if (surface->clip_level == 0 && op == CAIRO_OPERATOR_CLEAR) { - status = _cairo_output_stream_destroy (surface->xml_node); - if (unlikely (status)) { - surface->xml_node = NULL; - return status; - } - - surface->xml_node = _cairo_memory_stream_create (); - if (_cairo_output_stream_get_status (surface->xml_node)) { - status = _cairo_output_stream_destroy (surface->xml_node); - surface->xml_node = NULL; - return status; - } - - if (op == CAIRO_OPERATOR_CLEAR) { - if (surface->content == CAIRO_CONTENT_COLOR) { - _cairo_output_stream_printf (surface->xml_node, - "\n", - surface->width, surface->height); - } + */ + if ((op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) && + clip == NULL) + { + switch (surface->paginated_mode) { + case CAIRO_PAGINATED_MODE_FALLBACK: + ASSERT_NOT_REACHED; + case CAIRO_PAGINATED_MODE_ANALYZE: return CAIRO_STATUS_SUCCESS; + + case CAIRO_PAGINATED_MODE_RENDER: + status = _cairo_output_stream_destroy (surface->xml_node); + if (unlikely (status)) { + surface->xml_node = NULL; + return status; + } + + surface->xml_node = _cairo_memory_stream_create (); + if (_cairo_output_stream_get_status (surface->xml_node)) { + status = _cairo_output_stream_destroy (surface->xml_node); + surface->xml_node = NULL; + return status; + } + + if (op == CAIRO_OPERATOR_CLEAR) { + if (surface->content == CAIRO_CONTENT_COLOR) { + _cairo_output_stream_printf (surface->xml_node, + "\n", + surface->width, surface->height); + } + return CAIRO_STATUS_SUCCESS; + } + break; } + } else { + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _cairo_svg_surface_analyze_operation (surface, op, source); + + assert (_cairo_svg_surface_operation_supported (surface, op, source)); } + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + return _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, NULL); } @@ -2155,7 +2245,7 @@ _cairo_svg_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_status_t status; cairo_svg_surface_t *surface = abstract_surface; @@ -2172,9 +2262,13 @@ _cairo_svg_surface_mask (void *abstract_surface, if (_cairo_status_is_error (source_status)) return source_status; - mask_status = _cairo_svg_surface_analyze_operation (surface, op, mask); - if (_cairo_status_is_error (mask_status)) - return mask_status; + if (mask->has_component_alpha) { + mask_status = CAIRO_INT_STATUS_UNSUPPORTED; + } else { + mask_status = _cairo_svg_surface_analyze_operation (surface, op, mask); + if (_cairo_status_is_error (mask_status)) + return mask_status; + } return _cairo_analysis_surface_merge_status (source_status, mask_status); @@ -2183,6 +2277,10 @@ _cairo_svg_surface_mask (void *abstract_surface, assert (_cairo_svg_surface_operation_supported (surface, op, source)); assert (_cairo_svg_surface_operation_supported (surface, CAIRO_OPERATOR_OVER, mask)); + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) { const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t*) mask; cairo_content_t content = cairo_surface_get_content (surface_pattern->surface); @@ -2243,7 +2341,7 @@ _cairo_svg_surface_stroke (void *abstract_dst, cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_svg_surface_t *surface = abstract_dst; cairo_status_t status; @@ -2253,6 +2351,10 @@ _cairo_svg_surface_stroke (void *abstract_dst, assert (_cairo_svg_surface_operation_supported (surface, op, source)); + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + _cairo_output_stream_printf (surface->xml_node, "xml_node, surface, op, source, stroke_style, ctm_inverse); @@ -2261,9 +2363,7 @@ _cairo_svg_surface_stroke (void *abstract_dst, _cairo_output_stream_printf (surface->xml_node, "\" "); - status = _cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse); - if (unlikely (status)) - return status; + _cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse); _cairo_svg_surface_emit_transform (surface->xml_node, " transform", ctm, NULL); _cairo_output_stream_printf (surface->xml_node, "/>\n"); @@ -2278,8 +2378,8 @@ _cairo_svg_surface_show_glyphs (void *abstract_surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip, + int *remaining_glyphs) { cairo_svg_surface_t *surface = abstract_surface; cairo_svg_document_t *document = surface->document; @@ -2296,6 +2396,10 @@ _cairo_svg_surface_show_glyphs (void *abstract_surface, if (num_glyphs <= 0) return CAIRO_STATUS_SUCCESS; + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + /* FIXME it's probably possible to apply a pattern of a gradient to * a group of symbols, but I don't know how yet. Gradients or patterns * are translated by x and y properties of use element. */ @@ -2343,7 +2447,9 @@ _cairo_svg_surface_show_glyphs (void *abstract_surface, FALLBACK: _cairo_path_fixed_init (&path); - status = _cairo_scaled_font_glyph_path (scaled_font,(cairo_glyph_t *) glyphs, num_glyphs, &path); + status = _cairo_scaled_font_glyph_path (scaled_font, + (cairo_glyph_t *) glyphs, + num_glyphs, &path); if (unlikely (status)) { _cairo_path_fixed_fini (&path); @@ -2351,58 +2457,15 @@ FALLBACK: } status = _cairo_svg_surface_fill (abstract_surface, op, pattern, - &path, CAIRO_FILL_RULE_WINDING, 0.0, CAIRO_ANTIALIAS_SUBPIXEL, NULL); + &path, CAIRO_FILL_RULE_WINDING, + 0.0, CAIRO_ANTIALIAS_SUBPIXEL, + clip); _cairo_path_fixed_fini (&path); return status; } -static cairo_int_status_t -_cairo_svg_surface_intersect_clip_path (void *dst, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_svg_surface_t *surface = dst; - cairo_svg_document_t *document = surface->document; - cairo_status_t status; - unsigned int i; - - if (path == NULL) { - for (i = 0; i < surface->clip_level; i++) - _cairo_output_stream_printf (surface->xml_node, "\n"); - - surface->clip_level = 0; - return CAIRO_STATUS_SUCCESS; - } - - _cairo_output_stream_printf (document->xml_node_defs, - "\n" - " clip_id); - status = _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (document->xml_node_defs, - "/>\n" - "\n"); - - _cairo_output_stream_printf (surface->xml_node, - "\n", - document->clip_id, - fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? - "evenodd" : "nonzero"); - - document->clip_id++; - surface->clip_level++; - - return CAIRO_STATUS_SUCCESS; -} - static void _cairo_svg_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options) @@ -2416,7 +2479,7 @@ _cairo_svg_surface_get_font_options (void *abstract_surface, static const cairo_surface_backend_t cairo_svg_surface_backend = { CAIRO_SURFACE_TYPE_SVG, - _cairo_svg_surface_create_similar, + NULL, /* create_similar: handled by wrapper */ _cairo_svg_surface_finish, NULL, /* acquire_source_image */ NULL, /* release_source_image */ @@ -2430,8 +2493,6 @@ static const cairo_surface_backend_t cairo_svg_surface_backend = { NULL, /* check_span_renderer */ _cairo_svg_surface_copy_page, _cairo_svg_surface_show_page, - NULL, /* set_clip_region */ - _cairo_svg_surface_intersect_clip_path, _cairo_svg_surface_get_extents, NULL, /* _cairo_svg_surface_old_show_glyphs, */ _cairo_svg_surface_get_font_options, @@ -2446,7 +2507,6 @@ static const cairo_surface_backend_t cairo_svg_surface_backend = { _cairo_svg_surface_show_glyphs, NULL, /* snapshot */ NULL, /* is_similar */ - NULL, /* reset */ _cairo_svg_surface_fill_stroke }; @@ -2481,7 +2541,6 @@ _cairo_svg_document_create (cairo_output_stream_t *output_stream, document->width = width; document->height = height; - document->surface_id = 0; document->linear_pattern_id = 0; document->radial_pattern_id = 0; document->pattern_id = 0; @@ -2501,9 +2560,6 @@ _cairo_svg_document_create (cairo_output_stream_t *output_stream, document->alpha_filter = FALSE; - _cairo_array_init (&document->meta_snapshots, - sizeof (cairo_meta_snapshot_t)); - document->svg_version = version; *document_out = document; @@ -2554,7 +2610,6 @@ _cairo_svg_document_finish (cairo_svg_document_t *document) { cairo_status_t status, status2; cairo_output_stream_t *output = document->output_stream; - cairo_meta_snapshot_t *snapshot; cairo_svg_page_t *page; unsigned int i; @@ -2656,16 +2711,6 @@ _cairo_svg_document_finish (cairo_svg_document_t *document) if (status == CAIRO_STATUS_SUCCESS) status = status2; - for (i = 0; i < document->meta_snapshots.num_elements; i++) { - snapshot = _cairo_array_index (&document->meta_snapshots, i); - cairo_surface_finish (&snapshot->meta->base); - status2 = cairo_surface_status (&snapshot->meta->base); - cairo_surface_destroy (&snapshot->meta->base); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } - _cairo_array_fini (&document->meta_snapshots); - document->finished = TRUE; return status; diff --git a/gfx/cairo/cairo/src/cairo-tee-surface-private.h b/gfx/cairo/cairo/src/cairo-tee-surface-private.h new file mode 100644 index 000000000000..258816b59741 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-tee-surface-private.h @@ -0,0 +1,47 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_TEE_SURFACE_PRIVATE_H +#define CAIRO_TEE_SURFACE_PRIVATE_H + +#include "cairoint.h" + +cairo_private cairo_surface_t * +_cairo_tee_surface_find_match (void *abstract_surface, + const cairo_surface_backend_t *backend, + cairo_content_t content); + +#endif /* CAIRO_TEE_SURFACE_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-tee-surface.c b/gfx/cairo/cairo/src/cairo-tee-surface.c new file mode 100644 index 000000000000..1111fa1b49f4 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-tee-surface.c @@ -0,0 +1,599 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Carl Worth + * Chris Wilson + */ + +/* This surface supports redirecting all its input to multiple surfaces. + */ + +#include "cairoint.h" + +#include "cairo-tee-surface-private.h" +#include "cairo-surface-wrapper-private.h" + +typedef struct _cairo_tee_surface { + cairo_surface_t base; + + cairo_surface_wrapper_t master; + cairo_array_t slaves; +} cairo_tee_surface_t; + +slim_hidden_proto (cairo_tee_surface_create); +slim_hidden_proto (cairo_tee_surface_add); + +static cairo_surface_t * +_cairo_tee_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + + cairo_tee_surface_t *other = abstract_surface; + cairo_surface_t *similar; + cairo_surface_t *surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + + similar = _cairo_surface_wrapper_create_similar (&other->master, + content, width, height); + surface = cairo_tee_surface_create (similar); + cairo_surface_destroy (similar); + if (unlikely (surface->status)) + return surface; + + num_slaves = _cairo_array_num_elements (&other->slaves); + slaves = _cairo_array_index (&other->slaves, 0); + for (n = 0; n < num_slaves; n++) { + + similar = _cairo_surface_wrapper_create_similar (&slaves[n], + content, + width, height); + cairo_tee_surface_add (surface, similar); + cairo_surface_destroy (similar); + } + + if (unlikely (surface->status)) { + cairo_status_t status = surface->status; + cairo_surface_destroy (surface); + surface = _cairo_surface_create_in_error (status); + } + + return surface; +} + +static cairo_status_t +_cairo_tee_surface_finish (void *abstract_surface) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + + _cairo_surface_wrapper_fini (&surface->master); + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) + _cairo_surface_wrapper_fini (&slaves[n]); + + _cairo_array_fini (&surface->slaves); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_tee_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int num_slaves, n; + + /* we prefer to use a real image surface if available */ + if (_cairo_surface_is_image (surface->master.target)) { + return _cairo_surface_wrapper_acquire_source_image (&surface->master, + image_out, image_extra); + } + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + if (_cairo_surface_is_image (slaves[n].target)) { + return _cairo_surface_wrapper_acquire_source_image (&slaves[n], + image_out, + image_extra); + } + } + + return _cairo_surface_wrapper_acquire_source_image (&surface->master, + image_out, image_extra); +} + +static void +_cairo_tee_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_tee_surface_t *surface = abstract_surface; + + _cairo_surface_wrapper_release_source_image (&surface->master, + image, image_extra); +} + +static cairo_surface_t * +_cairo_tee_surface_snapshot (void *abstract_surface) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int num_slaves, n; + + /* we prefer to use a recording surface for our snapshots */ + if (_cairo_surface_is_recording (surface->master.target)) + return _cairo_surface_wrapper_snapshot (&surface->master); + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + if (_cairo_surface_is_recording (slaves[n].target)) + return _cairo_surface_wrapper_snapshot (&slaves[n]); + } + + return _cairo_surface_wrapper_snapshot (&surface->master); +} + +static cairo_bool_t +_cairo_tee_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_tee_surface_t *surface = abstract_surface; + + return _cairo_surface_wrapper_get_extents (&surface->master, rectangle); +} + +static void +_cairo_tee_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + cairo_tee_surface_t *surface = abstract_surface; + + _cairo_surface_wrapper_get_font_options (&surface->master, options); +} + +static cairo_int_status_t +_cairo_tee_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + cairo_status_t status; + + status = _cairo_surface_wrapper_paint (&surface->master, op, source, clip); + if (unlikely (status)) + return status; + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + status = _cairo_surface_wrapper_paint (&slaves[n], op, source, clip); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_tee_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + cairo_status_t status; + + status = _cairo_surface_wrapper_mask (&surface->master, + op, source, mask, clip); + if (unlikely (status)) + return status; + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + status = _cairo_surface_wrapper_mask (&slaves[n], + op, source, mask, clip); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_tee_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_stroke_style_t *style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + cairo_status_t status; + + status = _cairo_surface_wrapper_stroke (&surface->master, + op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); + if (unlikely (status)) + return status; + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + status = _cairo_surface_wrapper_stroke (&slaves[n], + op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_tee_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + cairo_status_t status; + + status = _cairo_surface_wrapper_fill (&surface->master, + op, source, + path, fill_rule, + tolerance, antialias, + clip); + if (unlikely (status)) + return status; + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + status = _cairo_surface_wrapper_fill (&slaves[n], + op, source, + path, fill_rule, + tolerance, antialias, + clip); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_tee_surface_has_show_text_glyphs (void *abstract_surface) +{ + return TRUE; +} + +static cairo_int_status_t +_cairo_tee_surface_show_text_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + cairo_status_t status; + cairo_glyph_t *glyphs_copy; + + /* XXX: This copying is ugly. */ + glyphs_copy = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); + if (unlikely (glyphs_copy == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs); + status = _cairo_surface_wrapper_show_text_glyphs (&surface->master, op, + source, + utf8, utf8_len, + glyphs_copy, num_glyphs, + clusters, num_clusters, + cluster_flags, + scaled_font, + clip); + if (unlikely (status)) + goto CLEANUP; + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs); + status = _cairo_surface_wrapper_show_text_glyphs (&slaves[n], op, + source, + utf8, utf8_len, + glyphs_copy, num_glyphs, + clusters, num_clusters, + cluster_flags, + scaled_font, + clip); + if (unlikely (status)) + goto CLEANUP; + } + + CLEANUP: + free (glyphs_copy); + return status; +} + +static const cairo_surface_backend_t cairo_tee_surface_backend = { + CAIRO_SURFACE_TYPE_TEE, + _cairo_tee_surface_create_similar, + _cairo_tee_surface_finish, + _cairo_tee_surface_acquire_source_image, + _cairo_tee_surface_release_source_image, + NULL, NULL, /* dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_tee_surface_get_extents, + NULL, /* old_show_glyphs */ + _cairo_tee_surface_get_font_options, + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + + _cairo_tee_surface_paint, + _cairo_tee_surface_mask, + _cairo_tee_surface_stroke, + _cairo_tee_surface_fill, + NULL, /* replaced by show_text_glyphs */ + + _cairo_tee_surface_snapshot, + NULL, /* is_similar */ + NULL, /* fill_stroke */ + NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ + + _cairo_tee_surface_has_show_text_glyphs, + _cairo_tee_surface_show_text_glyphs +}; + +cairo_surface_t * +cairo_tee_surface_create (cairo_surface_t *master) +{ + cairo_tee_surface_t *surface; + + if (unlikely (master->status)) + return _cairo_surface_create_in_error (master->status); + + surface = malloc (sizeof (cairo_tee_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &cairo_tee_surface_backend, + master->content); + + _cairo_surface_wrapper_init (&surface->master, master); + /* we trust that these are already set and remain constant */ + surface->base.device_transform = master->device_transform; + surface->base.device_transform_inverse = master->device_transform_inverse; + + _cairo_array_init (&surface->slaves, sizeof (cairo_surface_wrapper_t)); + + return &surface->base; +} +slim_hidden_def (cairo_tee_surface_create); + +void +cairo_tee_surface_add (cairo_surface_t *abstract_surface, + cairo_surface_t *target) +{ + cairo_tee_surface_t *surface; + cairo_surface_wrapper_t slave; + cairo_status_t status; + + if (unlikely (abstract_surface->status)) + return; + + if (abstract_surface->backend != &cairo_tee_surface_backend) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return; + } + + if (unlikely (target->status)) { + status = _cairo_surface_set_error (abstract_surface, target->status); + return; + } + + surface = (cairo_tee_surface_t *) abstract_surface; + + _cairo_surface_wrapper_init (&slave, target); + status = _cairo_array_append (&surface->slaves, &slave); + if (unlikely (status)) { + _cairo_surface_wrapper_fini (&slave); + status = _cairo_surface_set_error (&surface->base, status); + } +} +slim_hidden_def (cairo_tee_surface_add); + +void +cairo_tee_surface_remove (cairo_surface_t *abstract_surface, + cairo_surface_t *target) +{ + cairo_tee_surface_t *surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + cairo_status_t status; + + if (abstract_surface->backend != &cairo_tee_surface_backend) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return; + } + + surface = (cairo_tee_surface_t *) abstract_surface; + if (target == surface->master.target) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_INVALID_INDEX)); + return; + } + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + if (slaves[n].target == target) + break; + } + + if (n == num_slaves) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_INVALID_INDEX)); + return; + } + + _cairo_surface_wrapper_fini (&slaves[n]); + for (n++; n < num_slaves; n++) + slaves[n-1] = slaves[n]; + surface->slaves.num_elements--; /* XXX: cairo_array_remove()? */ +} + +cairo_surface_t * +cairo_tee_surface_index (cairo_surface_t *abstract_surface, + int index) +{ + cairo_tee_surface_t *surface; + + if (unlikely (abstract_surface->status)) + return _cairo_surface_create_in_error (abstract_surface->status); + + if (abstract_surface->backend != &cairo_tee_surface_backend) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + + surface = (cairo_tee_surface_t *) abstract_surface; + if (index == 0) { + return surface->master.target; + } else { + cairo_surface_wrapper_t *slave; + + index--; + + if (index >= _cairo_array_num_elements (&surface->slaves)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_INDEX)); + + slave = _cairo_array_index (&surface->slaves, index); + return slave->target; + } +} + +cairo_surface_t * +_cairo_tee_surface_find_match (void *abstract_surface, + const cairo_surface_backend_t *backend, + cairo_content_t content) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int num_slaves, n; + + /* exact match first */ + if (surface->master.target->backend == backend && + surface->master.target->content == content) + { + return surface->master.target; + } + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + if (slaves[n].target->backend == backend && + slaves[n].target->content == content) + { + return slaves[n].target; + } + } + + /* matching backend? */ + if (surface->master.target->backend == backend) + return surface->master.target; + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + if (slaves[n].target->backend == backend) + return slaves[n].target; + } + + return NULL; +} diff --git a/gfx/cairo/cairo/src/cairo-tor-scan-converter.c b/gfx/cairo/cairo/src/cairo-tor-scan-converter.c index 7d37f475c976..2b9fb1bc707d 100644 --- a/gfx/cairo/cairo/src/cairo-tor-scan-converter.c +++ b/gfx/cairo/cairo/src/cairo-tor-scan-converter.c @@ -128,18 +128,29 @@ blit_with_span_renderer( cairo_span_renderer_t *span_renderer, struct pool *span_pool, int y, + int height, int xmin, int xmax); +static glitter_status_t +blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height); + #define GLITTER_BLIT_COVERAGES_ARGS \ cairo_span_renderer_t *span_renderer, \ struct pool *span_pool -#define GLITTER_BLIT_COVERAGES(cells, y, xmin, xmax) do { \ +#define GLITTER_BLIT_COVERAGES(cells, y, height,xmin, xmax) do { \ cairo_status_t status = blit_with_span_renderer (cells, \ span_renderer, \ span_pool, \ - y, xmin, xmax); \ + y, height, \ + xmin, xmax); \ + if (unlikely (status)) \ + return status; \ +} while (0) + +#define GLITTER_BLIT_COVERAGES_EMPTY(y, height, xmin, xmax) do { \ + cairo_status_t status = blit_empty_with_span_renderer (span_renderer, y, height); \ if (unlikely (status)) \ return status; \ } while (0) @@ -190,11 +201,8 @@ glitter_scan_converter_reset( * converter should be reset or destroyed. Dir must be +1 or -1, * with the latter reversing the orientation of the edge. */ I glitter_status_t -glitter_scan_converter_add_edge( - glitter_scan_converter_t *converter, - glitter_input_scaled_t x1, glitter_input_scaled_t y1, - glitter_input_scaled_t x2, glitter_input_scaled_t y2, - int dir); +glitter_scan_converter_add_edge (glitter_scan_converter_t *converter, + const cairo_edge_t *edge); /* Render the polygon in the scan converter to the given A8 format * image raster. Only the pixels accessible as pixels[y*stride+x] for @@ -303,8 +311,8 @@ typedef int grid_area_t; #define UNROLL3(x) x x x struct quorem { - int quo; - int rem; + int32_t quo; + int32_t rem; }; /* Header for a chunk of memory in a memory pool. */ @@ -376,6 +384,7 @@ struct edge { /* Original sign of the edge: +1 for downwards, -1 for upwards * edges. */ int dir; + int vertical; }; /* Number of subsample rows per y-bucket. Must be GRID_Y. */ @@ -383,18 +392,28 @@ struct edge { #define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT) +struct bucket { + /* Unsorted list of edges starting within this bucket. */ + struct edge *edges; + + /* Set to non-zero if there are edges starting strictly within the + * bucket. */ + unsigned have_inside_edges; +}; + /* A collection of sorted and vertically clipped edges of the polygon. * Edges are moved from the polygon to an active list while scan * converting. */ struct polygon { - /* The vertical clip extents. */ + /* The clip extents. */ + grid_scaled_x_t xmin, xmax; grid_scaled_y_t ymin, ymax; /* Array of edges all starting in the same bucket. An edge is put * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when * it is added to the polygon. */ - struct edge **y_buckets; - struct edge *y_buckets_embedded[64]; + struct bucket *y_buckets; + struct bucket y_buckets_embedded[64]; struct { struct pool base[1]; @@ -614,10 +633,8 @@ _pool_alloc_from_new_chunk( } if (NULL == chunk) { - chunk = _pool_chunk_create( - pool->current, - capacity); - if (NULL == chunk) + chunk = _pool_chunk_create (pool->current, capacity); + if (unlikely (NULL == chunk)) return NULL; } pool->current = chunk; @@ -634,9 +651,7 @@ _pool_alloc_from_new_chunk( * allocation failures. The pool retains ownership of the returned * memory. */ inline static void * -pool_alloc( - struct pool *pool, - size_t size) +pool_alloc (struct pool *pool, size_t size) { struct _pool_chunk *chunk = pool->current; @@ -644,15 +659,14 @@ pool_alloc( void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size); chunk->size += size; return obj; - } - else { + } else { return _pool_alloc_from_new_chunk(pool, size); } } /* Relinquish all pool_alloced memory back to the pool. */ static void -pool_reset(struct pool *pool) +pool_reset (struct pool *pool) { /* Transfer all used chunks to the chunk free list. */ struct _pool_chunk *chunk = pool->current; @@ -671,19 +685,18 @@ pool_reset(struct pool *pool) /* Rewinds the cell list's cursor to the beginning. After rewinding * we're good to cell_list_find() the cell any x coordinate. */ inline static void -cell_list_rewind(struct cell_list *cells) +cell_list_rewind (struct cell_list *cells) { cells->cursor = &cells->head; } /* Rewind the cell list if its cursor has been advanced past x. */ inline static void -cell_list_maybe_rewind(struct cell_list *cells, int x) +cell_list_maybe_rewind (struct cell_list *cells, int x) { struct cell *tail = *cells->cursor; - if (tail->x > x) { - cell_list_rewind(cells); - } + if (tail->x > x) + cell_list_rewind (cells); } static void @@ -695,24 +708,43 @@ cell_list_init(struct cell_list *cells) cells->tail.next = NULL; cells->tail.x = INT_MAX; cells->head = &cells->tail; - cell_list_rewind(cells); + cell_list_rewind (cells); } static void cell_list_fini(struct cell_list *cells) { - pool_fini(cells->cell_pool.base); - cell_list_init(cells); + pool_fini (cells->cell_pool.base); } /* Empty the cell list. This is called at the start of every pixel * row. */ inline static void -cell_list_reset(struct cell_list *cells) +cell_list_reset (struct cell_list *cells) { - cell_list_rewind(cells); + cell_list_rewind (cells); cells->head = &cells->tail; - pool_reset(cells->cell_pool.base); + pool_reset (cells->cell_pool.base); +} + +static struct cell * +cell_list_alloc (struct cell_list *cells, + struct cell **cursor, + struct cell *tail, + int x) +{ + struct cell *cell; + + cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell)); + if (unlikely (NULL == cell)) + return NULL; + + *cursor = cell; + cell->next = tail; + cell->x = x; + cell->uncovered_area = 0; + cell->covered_height = 0; + return cell; } /* Find a cell at the given x-coordinate. Returns %NULL if a new cell @@ -721,7 +753,7 @@ cell_list_reset(struct cell_list *cells) * cell_list_rewind(). Ownership of the returned cell is retained by * the cell list. */ inline static struct cell * -cell_list_find(struct cell_list *cells, int x) +cell_list_find (struct cell_list *cells, int x) { struct cell **cursor = cells->cursor; struct cell *tail; @@ -737,21 +769,10 @@ cell_list_find(struct cell_list *cells, int x) } cells->cursor = cursor; - if (tail->x == x) { + if (tail->x == x) return tail; - } else { - struct cell *cell = pool_alloc( - cells->cell_pool.base, - sizeof(struct cell)); - if (NULL == cell) - return NULL; - *cursor = cell; - cell->next = tail; - cell->x = x; - cell->uncovered_area = 0; - cell->covered_height = 0; - return cell; - } + + return cell_list_alloc (cells, cursor, tail, x); } /* Find two cells at x1 and x2. This is exactly equivalent @@ -776,17 +797,18 @@ cell_list_find_pair(struct cell_list *cells, int x1, int x2) cell1 = *cursor; if (cell1->x > x1) break; + if (cell1->x == x1) goto found_first; + cursor = &cell1->next; }); } /* New first cell at x1. */ - newcell = pool_alloc( - cells->cell_pool.base, - sizeof(struct cell)); - if (NULL != newcell) { + newcell = pool_alloc (cells->cell_pool.base, + sizeof (struct cell)); + if (likely (NULL != newcell)) { *cursor = newcell; newcell->next = cell1; newcell->x = x1; @@ -809,10 +831,9 @@ cell_list_find_pair(struct cell_list *cells, int x1, int x2) } /* New second cell at x2. */ - newcell = pool_alloc( - cells->cell_pool.base, - sizeof(struct cell)); - if (NULL != newcell) { + newcell = pool_alloc (cells->cell_pool.base, + sizeof (struct cell)); + if (likely (NULL != newcell)) { *cursor = newcell; newcell->next = cell2; newcell->x = x2; @@ -831,21 +852,21 @@ cell_list_find_pair(struct cell_list *cells, int x1, int x2) /* Add an unbounded subpixel span covering subpixels >= x to the * coverage cells. */ static glitter_status_t -cell_list_add_unbounded_subspan( - struct cell_list *cells, - grid_scaled_x_t x) +cell_list_add_unbounded_subspan (struct cell_list *cells, + grid_scaled_x_t x) { struct cell *cell; int ix, fx; GRID_X_TO_INT_FRAC(x, ix, fx); - cell = cell_list_find(cells, ix); - if (cell) { + cell = cell_list_find (cells, ix); + if (likely (cell != NULL)) { cell->uncovered_area += 2*fx; cell->covered_height++; return GLITTER_STATUS_SUCCESS; } + return GLITTER_STATUS_NO_MEMORY; } @@ -865,17 +886,16 @@ cell_list_add_subspan( if (ix1 != ix2) { struct cell_pair p; p = cell_list_find_pair(cells, ix1, ix2); - if (p.cell1 && p.cell2) { + if (likely (p.cell1 != NULL && p.cell2 != NULL)) { p.cell1->uncovered_area += 2*fx1; ++p.cell1->covered_height; p.cell2->uncovered_area -= 2*fx2; --p.cell2->covered_height; return GLITTER_STATUS_SUCCESS; } - } - else { + } else { struct cell *cell = cell_list_find(cells, ix1); - if (cell) { + if (likely (cell != NULL)) { cell->uncovered_area += 2*(fx1-fx2); return GLITTER_STATUS_SUCCESS; } @@ -906,20 +926,24 @@ cell_list_render_edge( struct edge *edge, int sign) { - struct quorem x1 = edge->x; - struct quorem x2 = x1; grid_scaled_y_t y1, y2, dy; grid_scaled_x_t dx; int ix1, ix2; grid_scaled_x_t fx1, fx2; - x2.quo += edge->dxdy_full.quo; - x2.rem += edge->dxdy_full.rem; - if (x2.rem >= 0) { - ++x2.quo; - x2.rem -= edge->dy; + struct quorem x1 = edge->x; + struct quorem x2 = x1; + + if (! edge->vertical) { + x2.quo += edge->dxdy_full.quo; + x2.rem += edge->dxdy_full.rem; + if (x2.rem >= 0) { + ++x2.quo; + x2.rem -= edge->dy; + } + + edge->x = x2; } - edge->x = x2; GRID_X_TO_INT_FRAC(x1.quo, ix1, fx1); GRID_X_TO_INT_FRAC(x2.quo, ix2, fx2); @@ -929,8 +953,9 @@ cell_list_render_edge( /* We always know that ix1 is >= the cell list cursor in this * case due to the no-intersections precondition. */ struct cell *cell = cell_list_find(cells, ix1); - if (NULL == cell) + if (unlikely (NULL == cell)) return GLITTER_STATUS_NO_MEMORY; + cell->covered_height += sign*GRID_Y; cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y; return GLITTER_STATUS_SUCCESS; @@ -979,7 +1004,7 @@ cell_list_render_edge( cell_list_maybe_rewind(cells, ix1); pair = cell_list_find_pair(cells, ix1, ix1+1); - if (!pair.cell1 || !pair.cell2) + if (unlikely (!pair.cell1 || !pair.cell2)) return GLITTER_STATUS_NO_MEMORY; pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1); @@ -1007,7 +1032,7 @@ cell_list_render_edge( ++ix1; cell = cell_list_find(cells, ix1); - if (NULL == cell) + if (unlikely (NULL == cell)) return GLITTER_STATUS_NO_MEMORY; } while (ix1 != ix2); @@ -1021,32 +1046,34 @@ cell_list_render_edge( } static void -polygon_init(struct polygon *polygon) +polygon_init (struct polygon *polygon) { polygon->ymin = polygon->ymax = 0; + polygon->xmin = polygon->xmax = 0; polygon->y_buckets = polygon->y_buckets_embedded; - pool_init(polygon->edge_pool.base, - 8192 - sizeof(struct _pool_chunk), - sizeof(polygon->edge_pool.embedded)); + pool_init (polygon->edge_pool.base, + 8192 - sizeof (struct _pool_chunk), + sizeof (polygon->edge_pool.embedded)); } static void -polygon_fini(struct polygon *polygon) +polygon_fini (struct polygon *polygon) { if (polygon->y_buckets != polygon->y_buckets_embedded) - free(polygon->y_buckets); - pool_fini(polygon->edge_pool.base); - polygon_init(polygon); + free (polygon->y_buckets); + + pool_fini (polygon->edge_pool.base); } /* Empties the polygon of all edges. The polygon is then prepared to * receive new edges and clip them to the vertical range * [ymin,ymax). */ static glitter_status_t -polygon_reset( - struct polygon *polygon, - grid_scaled_y_t ymin, - grid_scaled_y_t ymax) +polygon_reset (struct polygon *polygon, + grid_scaled_x_t xmin, + grid_scaled_x_t xmax, + grid_scaled_y_t ymin, + grid_scaled_y_t ymax) { unsigned h = ymax - ymin; unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + EDGE_Y_BUCKET_HEIGHT-1, @@ -1054,22 +1081,25 @@ polygon_reset( pool_reset(polygon->edge_pool.base); - if (h > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT) + if (unlikely (h > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT)) goto bail_no_mem; /* even if you could, you wouldn't want to. */ if (polygon->y_buckets != polygon->y_buckets_embedded) free (polygon->y_buckets); + polygon->y_buckets = polygon->y_buckets_embedded; if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) { polygon->y_buckets = _cairo_malloc_ab (num_buckets, - sizeof (struct edge *)); + sizeof (struct bucket)); if (unlikely (NULL == polygon->y_buckets)) goto bail_no_mem; } - memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *)); + memset (polygon->y_buckets, 0, num_buckets * sizeof (struct bucket)); polygon->ymin = ymin; polygon->ymax = ymax; + polygon->xmin = xmin; + polygon->xmax = xmax; return GLITTER_STATUS_SUCCESS; bail_no_mem: @@ -1083,18 +1113,18 @@ _polygon_insert_edge_into_its_y_bucket( struct polygon *polygon, struct edge *e) { - unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin); - struct edge **ptail = &polygon->y_buckets[ix]; + unsigned j = e->ytop - polygon->ymin; + unsigned ix = j / EDGE_Y_BUCKET_HEIGHT; + unsigned offset = j % EDGE_Y_BUCKET_HEIGHT; + struct edge **ptail = &polygon->y_buckets[ix].edges; e->next = *ptail; *ptail = e; + polygon->y_buckets[ix].have_inside_edges |= offset; } inline static glitter_status_t -polygon_add_edge( - struct polygon *polygon, - int x0, int y0, - int x1, int y1, - int dir) +polygon_add_edge (struct polygon *polygon, + const cairo_edge_t *edge) { struct edge *e; grid_scaled_x_t dx; @@ -1103,54 +1133,68 @@ polygon_add_edge( grid_scaled_y_t ymin = polygon->ymin; grid_scaled_y_t ymax = polygon->ymax; - if (y0 == y1) + assert (edge->bottom > edge->top); + + if (unlikely (edge->top >= ymax || edge->bottom <= ymin)) return GLITTER_STATUS_SUCCESS; - if (y0 > y1) { - int tmp; - tmp = x0; x0 = x1; x1 = tmp; - tmp = y0; y0 = y1; y1 = tmp; - dir = -dir; - } - - if (y0 >= ymax || y1 <= ymin) - return GLITTER_STATUS_SUCCESS; - - e = pool_alloc(polygon->edge_pool.base, - sizeof(struct edge)); - if (NULL == e) + e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge)); + if (unlikely (NULL == e)) return GLITTER_STATUS_NO_MEMORY; - dx = x1 - x0; - dy = y1 - y0; + dx = edge->line.p2.x - edge->line.p1.x; + dy = edge->line.p2.y - edge->line.p1.y; e->dy = dy; - e->dxdy = floored_divrem(dx, dy); + e->dir = edge->dir; - if (ymin <= y0) { - ytop = y0; - e->x.quo = x0; - e->x.rem = 0; - } - else { - ytop = ymin; - e->x = floored_muldivrem(ymin - y0, dx, dy); - e->x.quo += x0; - } - - e->dir = dir; + ytop = edge->top >= ymin ? edge->top : ymin; + ybot = edge->bottom <= ymax ? edge->bottom : ymax; e->ytop = ytop; - ybot = y1 < ymax ? y1 : ymax; e->height_left = ybot - ytop; - if (e->height_left >= GRID_Y) { - e->dxdy_full = floored_muldivrem(GRID_Y, dx, dy); - } - else { + if (dx == 0) { + e->vertical = TRUE; + e->x.quo = edge->line.p1.x; + e->x.rem = 0; + e->dxdy.quo = 0; + e->dxdy.rem = 0; e->dxdy_full.quo = 0; e->dxdy_full.rem = 0; + + /* Drop edges to the right of the clip extents. */ + if (e->x.quo >= polygon->xmax) + return GLITTER_STATUS_SUCCESS; + + /* Offset vertical edges at the left side of the clip extents + * to just shy of the left side. We depend on this when + * checking for possible intersections within the clip + * rectangle. */ + if (e->x.quo <= polygon->xmin) { + e->x.quo = polygon->xmin - 1; + } + } else { + e->vertical = FALSE; + e->dxdy = floored_divrem (dx, dy); + if (ytop == edge->line.p1.y) { + e->x.quo = edge->line.p1.x; + e->x.rem = 0; + } else { + e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy); + e->x.quo += edge->line.p1.x; + } + + if (e->x.quo >= polygon->xmax && e->dxdy.quo >= 0) + return GLITTER_STATUS_SUCCESS; + + if (e->height_left >= GRID_Y) { + e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy); + } else { + e->dxdy_full.quo = 0; + e->dxdy_full.rem = 0; + } } - _polygon_insert_edge_into_its_y_bucket(polygon, e); + _polygon_insert_edge_into_its_y_bucket (polygon, e); e->x.rem -= dy; /* Bias the remainder for faster * edge advancement. */ @@ -1158,8 +1202,7 @@ polygon_add_edge( } static void -active_list_reset( - struct active_list *active) +active_list_reset (struct active_list *active) { active->head = NULL; active->min_height = 0; @@ -1171,31 +1214,30 @@ active_list_init(struct active_list *active) active_list_reset(active); } -static void -active_list_fini( - struct active_list *active) -{ - active_list_reset(active); -} - /* Merge the edges in an unsorted list of edges into a sorted * list. The sort order is edges ascending by edge->x.quo. Returns * the new head of the sorted list. */ static struct edge * merge_unsorted_edges(struct edge *sorted_head, struct edge *unsorted_head) { - struct edge *head = unsorted_head; struct edge **cursor = &sorted_head; int x; - while (NULL != head) { - struct edge *prev = *cursor; - struct edge *next = head->next; - x = head->x.quo; + if (sorted_head == NULL) { + sorted_head = unsorted_head; + unsorted_head = unsorted_head->next; + sorted_head->next = NULL; + if (unsorted_head == NULL) + return sorted_head; + } - if (NULL == prev || x < prev->x.quo) { + do { + struct edge *next = unsorted_head->next; + struct edge *prev = *cursor; + + x = unsorted_head->x.quo; + if (x < prev->x.quo) cursor = &sorted_head; - } while (1) { UNROLL3({ @@ -1206,26 +1248,29 @@ merge_unsorted_edges(struct edge *sorted_head, struct edge *unsorted_head) }); } - head->next = *cursor; - *cursor = head; + unsorted_head->next = *cursor; + *cursor = unsorted_head; + unsorted_head = next; + } while (unsorted_head != NULL); - head = next; - } return sorted_head; } /* Test if the edges on the active list can be safely advanced by a * full row without intersections or any edges ending. */ inline static int -active_list_can_step_full_row( - struct active_list *active) +active_list_can_step_full_row (struct active_list *active, + grid_scaled_x_t xmin) { + const struct edge *e; + grid_scaled_x_t prev_x = INT_MIN; + /* Recomputes the minimum height of all edges on the active * list if we have been dropping edges. */ if (active->min_height <= 0) { - struct edge *e = active->head; int min_height = INT_MAX; + e = active->head; while (NULL != e) { if (e->height_left < min_height) min_height = e->height_left; @@ -1235,27 +1280,38 @@ active_list_can_step_full_row( active->min_height = min_height; } - /* Check for intersections only if no edges end during the next - * row. */ - if (active->min_height >= GRID_Y) { - grid_scaled_x_t prev_x = INT_MIN; - struct edge *e = active->head; - while (NULL != e) { - struct quorem x = e->x; + if (active->min_height < GRID_Y) + return 0; + /* Check for intersections as no edges end during the next row. */ + e = active->head; + while (NULL != e) { + struct quorem x = e->x; + + if (! e->vertical) { x.quo += e->dxdy_full.quo; x.rem += e->dxdy_full.rem; if (x.rem >= 0) ++x.quo; - - if (x.quo <= prev_x) - return 0; - prev_x = x.quo; - e = e->next; } - return 1; + + /* There's may be an intersection if the edge sort order might + * change. */ + if (x.quo <= prev_x) { + /* Ignore intersections to the left of the clip extents. + * This assumes that all vertical edges on or at the left + * side of the clip rectangle have been shifted slightly + * to the left in polygon_add_edge(). */ + if (prev_x >= xmin || x.quo >= xmin || e->x.quo >= xmin) + return 0; + } + else { + prev_x = x.quo; + } + e = e->next; } - return 0; + + return 1; } /* Merges edges on the given subpixel row from the polygon to the @@ -1271,7 +1327,7 @@ active_list_merge_edges_from_polygon( unsigned ix = EDGE_Y_BUCKET_INDEX(y, polygon->ymin); int min_height = active->min_height; struct edge *subrow_edges = NULL; - struct edge **ptail = &polygon->y_buckets[ix]; + struct edge **ptail = &polygon->y_buckets[ix].edges; while (1) { struct edge *tail = *ptail; @@ -1283,13 +1339,14 @@ active_list_merge_edges_from_polygon( subrow_edges = tail; if (tail->height_left < min_height) min_height = tail->height_left; - } - else { + } else { ptail = &tail->next; } } - active->head = merge_unsorted_edges(active->head, subrow_edges); - active->min_height = min_height; + if (subrow_edges) { + active->head = merge_unsorted_edges(active->head, subrow_edges); + active->min_height = min_height; + } } /* Advance the edges on the active list by one subsample row by @@ -1338,9 +1395,8 @@ active_list_substep_edges( } inline static glitter_status_t -apply_nonzero_fill_rule_for_subrow( - struct active_list *active, - struct cell_list *coverages) +apply_nonzero_fill_rule_for_subrow (struct active_list *active, + struct cell_list *coverages) { struct edge *edge = active->head; int winding = 0; @@ -1348,25 +1404,26 @@ apply_nonzero_fill_rule_for_subrow( int xend; int status; - cell_list_rewind(coverages); + cell_list_rewind (coverages); while (NULL != edge) { xstart = edge->x.quo; winding = edge->dir; while (1) { edge = edge->next; - if (NULL == edge) { - return cell_list_add_unbounded_subspan( - coverages, xstart); - } + if (NULL == edge) + return cell_list_add_unbounded_subspan (coverages, xstart); + winding += edge->dir; - if (0 == winding) - break; + if (0 == winding) { + if (edge->next == NULL || edge->next->x.quo != edge->x.quo) + break; + } } xend = edge->x.quo; - status = cell_list_add_subspan(coverages, xstart, xend); - if (status) + status = cell_list_add_subspan (coverages, xstart, xend); + if (unlikely (status)) return status; edge = edge->next; @@ -1376,29 +1433,33 @@ apply_nonzero_fill_rule_for_subrow( } static glitter_status_t -apply_evenodd_fill_rule_for_subrow( - struct active_list *active, - struct cell_list *coverages) +apply_evenodd_fill_rule_for_subrow (struct active_list *active, + struct cell_list *coverages) { struct edge *edge = active->head; int xstart; int xend; int status; - cell_list_rewind(coverages); + cell_list_rewind (coverages); while (NULL != edge) { xstart = edge->x.quo; - edge = edge->next; - if (NULL == edge) { - return cell_list_add_unbounded_subspan( - coverages, xstart); + while (1) { + edge = edge->next; + if (NULL == edge) + return cell_list_add_unbounded_subspan (coverages, xstart); + + if (edge->next == NULL || edge->next->x.quo != edge->x.quo) + break; + + edge = edge->next; } xend = edge->x.quo; - status = cell_list_add_subspan(coverages, xstart, xend); - if (status) + status = cell_list_add_subspan (coverages, xstart, xend); + if (unlikely (status)) return status; edge = edge->next; @@ -1408,9 +1469,8 @@ apply_evenodd_fill_rule_for_subrow( } static glitter_status_t -apply_nonzero_fill_rule_and_step_edges( - struct active_list *active, - struct cell_list *coverages) +apply_nonzero_fill_rule_and_step_edges (struct active_list *active, + struct cell_list *coverages) { struct edge **cursor = &active->head; struct edge *left_edge; @@ -1422,48 +1482,47 @@ apply_nonzero_fill_rule_and_step_edges( int winding = left_edge->dir; left_edge->height_left -= GRID_Y; - if (left_edge->height_left) { + if (left_edge->height_left) cursor = &left_edge->next; - } - else { + else *cursor = left_edge->next; - } while (1) { right_edge = *cursor; - - if (NULL == right_edge) { - return cell_list_render_edge( - coverages, left_edge, +1); - } + if (NULL == right_edge) + return cell_list_render_edge (coverages, left_edge, +1); right_edge->height_left -= GRID_Y; - if (right_edge->height_left) { + if (right_edge->height_left) cursor = &right_edge->next; - } - else { + else *cursor = right_edge->next; - } winding += right_edge->dir; - if (0 == winding) - break; + if (0 == winding) { + if (right_edge->next == NULL || + right_edge->next->x.quo != right_edge->x.quo) + { + break; + } + } - right_edge->x.quo += right_edge->dxdy_full.quo; - right_edge->x.rem += right_edge->dxdy_full.rem; - if (right_edge->x.rem >= 0) { - ++right_edge->x.quo; - right_edge->x.rem -= right_edge->dy; + if (! right_edge->vertical) { + right_edge->x.quo += right_edge->dxdy_full.quo; + right_edge->x.rem += right_edge->dxdy_full.rem; + if (right_edge->x.rem >= 0) { + ++right_edge->x.quo; + right_edge->x.rem -= right_edge->dy; + } } } - status = cell_list_render_edge( - coverages, left_edge, +1); - if (status) + status = cell_list_render_edge (coverages, left_edge, +1); + if (unlikely (status)) return status; - status = cell_list_render_edge( - coverages, right_edge, -1); - if (status) + + status = cell_list_render_edge (coverages, right_edge, -1); + if (unlikely (status)) return status; left_edge = *cursor; @@ -1473,9 +1532,8 @@ apply_nonzero_fill_rule_and_step_edges( } static glitter_status_t -apply_evenodd_fill_rule_and_step_edges( - struct active_list *active, - struct cell_list *coverages) +apply_evenodd_fill_rule_and_step_edges (struct active_list *active, + struct cell_list *coverages) { struct edge **cursor = &active->head; struct edge *left_edge; @@ -1484,37 +1542,50 @@ apply_evenodd_fill_rule_and_step_edges( left_edge = *cursor; while (NULL != left_edge) { struct edge *right_edge; + int winding = left_edge->dir; left_edge->height_left -= GRID_Y; - if (left_edge->height_left) { + if (left_edge->height_left) cursor = &left_edge->next; - } - else { + else *cursor = left_edge->next; + + while (1) { + right_edge = *cursor; + if (NULL == right_edge) + return cell_list_render_edge (coverages, left_edge, +1); + + right_edge->height_left -= GRID_Y; + if (right_edge->height_left) + cursor = &right_edge->next; + else + *cursor = right_edge->next; + + winding += right_edge->dir; + if ((winding & 1) == 0) { + if (right_edge->next == NULL || + right_edge->next->x.quo != right_edge->x.quo) + { + break; + } + } + + if (! right_edge->vertical) { + right_edge->x.quo += right_edge->dxdy_full.quo; + right_edge->x.rem += right_edge->dxdy_full.rem; + if (right_edge->x.rem >= 0) { + ++right_edge->x.quo; + right_edge->x.rem -= right_edge->dy; + } + } } - right_edge = *cursor; - - if (NULL == right_edge) { - return cell_list_render_edge( - coverages, left_edge, +1); - } - - right_edge->height_left -= GRID_Y; - if (right_edge->height_left) { - cursor = &right_edge->next; - } - else { - *cursor = right_edge->next; - } - - status = cell_list_render_edge( - coverages, left_edge, +1); - if (status) + status = cell_list_render_edge (coverages, left_edge, +1); + if (unlikely (status)) return status; - status = cell_list_render_edge( - coverages, right_edge, -1); - if (status) + + status = cell_list_render_edge (coverages, right_edge, -1); + if (unlikely (status)) return status; left_edge = *cursor; @@ -1542,8 +1613,14 @@ blit_span( } } -#define GLITTER_BLIT_COVERAGES(coverages, y, xmin, xmax) \ - blit_cells(coverages, raster_pixels + (y)*raster_stride, xmin, xmax) +#define GLITTER_BLIT_COVERAGES(coverages, y, height, xmin, xmax) \ + do { \ + int __y = y; \ + int __h = height; \ + do { \ + blit_cells(coverages, raster_pixels + (__y)*raster_stride, xmin, xmax); \ + } while (--__h); \ + } while (0) static void blit_cells( @@ -1602,7 +1679,6 @@ static void _glitter_scan_converter_fini(glitter_scan_converter_t *converter) { polygon_fini(converter->polygon); - active_list_fini(converter->active); cell_list_fini(converter->coverages); converter->xmin=0; converter->ymin=0; @@ -1646,7 +1722,7 @@ glitter_scan_converter_reset( active_list_reset(converter->active); cell_list_reset(converter->coverages); - status = polygon_reset(converter->polygon, ymin, ymax); + status = polygon_reset(converter->polygon, xmin, xmax, ymin, ymax); if (status) return status; @@ -1683,26 +1759,28 @@ glitter_scan_converter_reset( } while (0) I glitter_status_t -glitter_scan_converter_add_edge( - glitter_scan_converter_t *converter, - glitter_input_scaled_t x1, glitter_input_scaled_t y1, - glitter_input_scaled_t x2, glitter_input_scaled_t y2, - int dir) +glitter_scan_converter_add_edge (glitter_scan_converter_t *converter, + const cairo_edge_t *edge) { - /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */ - grid_scaled_y_t sx1, sy1; - grid_scaled_y_t sx2, sy2; + cairo_edge_t e; - INPUT_TO_GRID_Y(y1, sy1); - INPUT_TO_GRID_Y(y2, sy2); - if (sy1 == sy2) + INPUT_TO_GRID_Y (edge->top, e.top); + INPUT_TO_GRID_Y (edge->bottom, e.bottom); + if (e.top >= e.bottom) return GLITTER_STATUS_SUCCESS; - INPUT_TO_GRID_X(x1, sx1); - INPUT_TO_GRID_X(x2, sx2); + /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */ + INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y); + INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y); + if (e.line.p1.y == e.line.p2.y) + return GLITTER_STATUS_SUCCESS; - return polygon_add_edge( - converter->polygon, sx1, sy1, sx2, sy2, dir); + INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x); + INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x); + + e.dir = edge->dir; + + return polygon_add_edge (converter->polygon, &e); } #ifndef GLITTER_BLIT_COVERAGES_BEGIN @@ -1714,19 +1792,48 @@ glitter_scan_converter_add_edge( #endif #ifndef GLITTER_BLIT_COVERAGES_EMPTY -# define GLITTER_BLIT_COVERAGES_EMPTY(y, xmin, xmax) +# define GLITTER_BLIT_COVERAGES_EMPTY(y0, y1, xmin, xmax) #endif +static cairo_bool_t +active_list_is_vertical (struct active_list *active) +{ + struct edge *e; + + for (e = active->head; e != NULL; e = e->next) { + if (! e->vertical) + return FALSE; + } + + return TRUE; +} + +static void +step_edges (struct active_list *active, int count) +{ + struct edge **cursor = &active->head; + struct edge *edge; + + for (edge = *cursor; edge != NULL; edge = *cursor) { + edge->height_left -= GRID_Y * count; + if (edge->height_left) + cursor = &edge->next; + else + *cursor = edge->next; + } +} + I glitter_status_t glitter_scan_converter_render( glitter_scan_converter_t *converter, int nonzero_fill, GLITTER_BLIT_COVERAGES_ARGS) { - int i; + int i, j; int ymax_i = converter->ymax / GRID_Y; int ymin_i = converter->ymin / GRID_Y; int xmin_i, xmax_i; + grid_scaled_x_t xmin = converter->xmin; int h = ymax_i - ymin_i; struct polygon *polygon = converter->polygon; struct cell_list *coverages = converter->coverages; @@ -1741,66 +1848,80 @@ glitter_scan_converter_render( GLITTER_BLIT_COVERAGES_BEGIN; /* Render each pixel row. */ - for (i=0; iy_buckets[i]) - { - if (!active->head) { - GLITTER_BLIT_COVERAGES_EMPTY(i+ymin_i, xmin_i, xmax_i); + if (polygon->y_buckets[i].edges == NULL) { + if (! active->head) { + for (; j < h && ! polygon->y_buckets[j].edges; j++) + ; + GLITTER_BLIT_COVERAGES_EMPTY (i+ymin_i, j-i, xmin_i, xmax_i); continue; } - do_full_step = active_list_can_step_full_row(active); + do_full_step = active_list_can_step_full_row (active, xmin); + } + else if (! polygon->y_buckets[i].have_inside_edges) { + grid_scaled_y_t y = (i+ymin_i)*GRID_Y; + active_list_merge_edges_from_polygon (active, y, polygon); + do_full_step = active_list_can_step_full_row (active, xmin); } - - cell_list_reset(coverages); if (do_full_step) { /* Step by a full pixel row's worth. */ if (nonzero_fill) { - status = apply_nonzero_fill_rule_and_step_edges( - active, coverages); + status = apply_nonzero_fill_rule_and_step_edges (active, + coverages); + } else { + status = apply_evenodd_fill_rule_and_step_edges (active, + coverages); } - else { - status = apply_evenodd_fill_rule_and_step_edges( - active, coverages); + + if (active_list_is_vertical (active)) { + while (j < h && + polygon->y_buckets[j].edges == NULL && + active->min_height >= 2*GRID_Y) + { + active->min_height -= GRID_Y; + j++; + } + if (j != i + 1) + step_edges (active, j - (i + 1)); } - } - else { - /* Subsample this row. */ + } else { + /* Supersample this row. */ grid_scaled_y_t suby; for (suby = 0; suby < GRID_Y; suby++) { grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby; - active_list_merge_edges_from_polygon( - active, y, polygon); + active_list_merge_edges_from_polygon (active, y, polygon); - if (nonzero_fill) - status |= apply_nonzero_fill_rule_for_subrow( - active, coverages); - else - status |= apply_evenodd_fill_rule_for_subrow( - active, coverages); + if (nonzero_fill) { + status |= apply_nonzero_fill_rule_for_subrow (active, + coverages); + } else { + status |= apply_evenodd_fill_rule_for_subrow (active, + coverages); + } active_list_substep_edges(active); } } - if (status) + if (unlikely (status)) return status; - GLITTER_BLIT_COVERAGES(coverages, i+ymin_i, xmin_i, xmax_i); + GLITTER_BLIT_COVERAGES(coverages, i+ymin_i, j-i, xmin_i, xmax_i); + cell_list_reset (coverages); - if (!active->head) { + if (! active->head) active->min_height = INT_MAX; - } - else { + else active->min_height -= GRID_Y; - } } /* Clean up the coverage blitter. */ @@ -1814,21 +1935,20 @@ glitter_scan_converter_render( * scan converter subclass. */ static glitter_status_t -blit_with_span_renderer( - struct cell_list *cells, - cairo_span_renderer_t *renderer, - struct pool *span_pool, - int y, - int xmin, - int xmax) +blit_with_span_renderer (struct cell_list *cells, + cairo_span_renderer_t *renderer, + struct pool *span_pool, + int y, int height, + int xmin, int xmax) { struct cell *cell = cells->head; int prev_x = xmin; int cover = 0; cairo_half_open_span_t *spans; unsigned num_spans; + if (cell == NULL) - return CAIRO_STATUS_SUCCESS; + return blit_empty_with_span_renderer (renderer, y, height); /* Skip cells to the left of the clip region. */ while (cell != NULL && cell->x < xmin) { @@ -1840,18 +1960,18 @@ blit_with_span_renderer( /* Count number of cells remaining. */ { struct cell *next = cell; - num_spans = 0; - while (next) { + num_spans = 1; + while (next != NULL) { next = next->next; ++num_spans; } - num_spans = 2*num_spans + 1; + num_spans = 2*num_spans; } /* Allocate enough spans for the row. */ pool_reset (span_pool); spans = pool_alloc (span_pool, sizeof(spans[0])*num_spans); - if (spans == NULL) + if (unlikely (spans == NULL)) return GLITTER_STATUS_NO_MEMORY; num_spans = 0; @@ -1860,6 +1980,7 @@ blit_with_span_renderer( for (; cell != NULL; cell = cell->next) { int x = cell->x; int area; + if (x >= xmax) break; @@ -1879,18 +2000,31 @@ blit_with_span_renderer( prev_x = x+1; } - if (prev_x < xmax) { + if (prev_x <= xmax) { spans[num_spans].x = prev_x; spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover); ++num_spans; } + if (prev_x < xmax && cover) { + spans[num_spans].x = xmax; + spans[num_spans].coverage = 0; + ++num_spans; + } + /* Dump them into the renderer. */ - return renderer->render_row (renderer, y, spans, num_spans); + return renderer->render_rows (renderer, y, height, spans, num_spans); +} + +static glitter_status_t +blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height) +{ + return renderer->render_rows (renderer, y, height, NULL, 0); } struct _cairo_tor_scan_converter { cairo_scan_converter_t base; + glitter_scan_converter_t converter[1]; cairo_fill_rule_t fill_rule; @@ -1903,9 +2037,9 @@ struct _cairo_tor_scan_converter { typedef struct _cairo_tor_scan_converter cairo_tor_scan_converter_t; static void -_cairo_tor_scan_converter_destroy(void *abstract_converter) +_cairo_tor_scan_converter_destroy (void *converter) { - cairo_tor_scan_converter_t *self = abstract_converter; + cairo_tor_scan_converter_t *self = converter; if (self == NULL) { return; } @@ -1915,69 +2049,95 @@ _cairo_tor_scan_converter_destroy(void *abstract_converter) } static cairo_status_t -_cairo_tor_scan_converter_add_edge( - void *abstract_converter, - cairo_fixed_t x1, - cairo_fixed_t y1, - cairo_fixed_t x2, - cairo_fixed_t y2) +_cairo_tor_scan_converter_add_edge (void *converter, + const cairo_point_t *p1, + const cairo_point_t *p2, + int top, int bottom, + int dir) { - cairo_tor_scan_converter_t *self = abstract_converter; + cairo_tor_scan_converter_t *self = converter; cairo_status_t status; - status = glitter_scan_converter_add_edge ( - self->converter, - x1, y1, x2, y2, +1); - if (status) { - return _cairo_scan_converter_set_error (self, - _cairo_error (status)); - } + cairo_edge_t edge; + + edge.line.p1 = *p1; + edge.line.p2 = *p2; + edge.top = top; + edge.bottom = bottom; + edge.dir = dir; + + status = glitter_scan_converter_add_edge (self->converter, &edge); + if (unlikely (status)) + return _cairo_scan_converter_set_error (self, _cairo_error (status)); + return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_tor_scan_converter_generate( - void *abstract_converter, - cairo_span_renderer_t *renderer) +_cairo_tor_scan_converter_add_polygon (void *converter, + const cairo_polygon_t *polygon) { - cairo_tor_scan_converter_t *self = abstract_converter; - cairo_status_t status = glitter_scan_converter_render ( - self->converter, - self->fill_rule == CAIRO_FILL_RULE_WINDING, - renderer, - self->span_pool.base); - if (status) { - return _cairo_scan_converter_set_error (self, - _cairo_error (status)); + cairo_tor_scan_converter_t *self = converter; + cairo_status_t status; + int i; + + for (i = 0; i < polygon->num_edges; i++) { + status = glitter_scan_converter_add_edge (self->converter, + &polygon->edges[i]); + if (unlikely (status)) { + return _cairo_scan_converter_set_error (self, + _cairo_error (status)); + } } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_tor_scan_converter_generate (void *converter, + cairo_span_renderer_t *renderer) +{ + cairo_tor_scan_converter_t *self = converter; + cairo_status_t status; + + status = glitter_scan_converter_render (self->converter, + self->fill_rule == CAIRO_FILL_RULE_WINDING, + renderer, + self->span_pool.base); + if (unlikely (status)) + return _cairo_scan_converter_set_error (self, _cairo_error (status)); + return CAIRO_STATUS_SUCCESS; } cairo_scan_converter_t * -_cairo_tor_scan_converter_create( - int xmin, - int ymin, - int xmax, - int ymax, - cairo_fill_rule_t fill_rule) +_cairo_tor_scan_converter_create (int xmin, + int ymin, + int xmax, + int ymax, + cairo_fill_rule_t fill_rule) { + cairo_tor_scan_converter_t *self; cairo_status_t status; - cairo_tor_scan_converter_t *self = - calloc (1, sizeof(struct _cairo_tor_scan_converter)); - if (self == NULL) - goto bail_nomem; - self->base.destroy = &_cairo_tor_scan_converter_destroy; - self->base.add_edge = &_cairo_tor_scan_converter_add_edge; - self->base.generate = &_cairo_tor_scan_converter_generate; + self = calloc (1, sizeof(struct _cairo_tor_scan_converter)); + if (unlikely (self == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto bail_nomem; + } + + self->base.destroy = _cairo_tor_scan_converter_destroy; + self->base.add_edge = _cairo_tor_scan_converter_add_edge; + self->base.add_polygon = _cairo_tor_scan_converter_add_polygon; + self->base.generate = _cairo_tor_scan_converter_generate; pool_init (self->span_pool.base, 250 * sizeof(self->span_pool.embedded[0]), sizeof(self->span_pool.embedded)); _glitter_scan_converter_init (self->converter); - status = glitter_scan_converter_reset ( - self->converter, xmin, ymin, xmax, ymax); - if (status != CAIRO_STATUS_SUCCESS) + status = glitter_scan_converter_reset (self->converter, + xmin, ymin, xmax, ymax); + if (unlikely (status)) goto bail; self->fill_rule = fill_rule; @@ -1987,5 +2147,5 @@ _cairo_tor_scan_converter_create( bail: self->base.destroy(&self->base); bail_nomem: - return _cairo_scan_converter_create_in_error (CAIRO_STATUS_NO_MEMORY); + return _cairo_scan_converter_create_in_error (status); } diff --git a/gfx/cairo/cairo/src/cairo-toy-font-face.c b/gfx/cairo/cairo/src/cairo-toy-font-face.c index a9b600a5c849..e7c841a9cf43 100644 --- a/gfx/cairo/cairo/src/cairo-toy-font-face.c +++ b/gfx/cairo/cairo/src/cairo-toy-font-face.c @@ -520,5 +520,6 @@ _cairo_toy_font_face_reset_static_data (void) cairo_toy_font_face_hash_table = NULL; CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex); - _cairo_hash_table_destroy (hash_table); + if (hash_table != NULL) + _cairo_hash_table_destroy (hash_table); } diff --git a/gfx/cairo/cairo/src/cairo-traps.c b/gfx/cairo/cairo/src/cairo-traps.c index d8464cc88dd2..70d4c69e0e13 100644 --- a/gfx/cairo/cairo/src/cairo-traps.c +++ b/gfx/cairo/cairo/src/cairo-traps.c @@ -39,10 +39,10 @@ #include "cairoint.h" -/* private functions */ +#include "cairo-region-private.h" +#include "cairo-slope-private.h" -static int -_compare_point_fixed_by_y (const void *av, const void *bv); +/* private functions */ void _cairo_traps_init (cairo_traps_t *traps) @@ -51,31 +51,26 @@ _cairo_traps_init (cairo_traps_t *traps) traps->status = CAIRO_STATUS_SUCCESS; + traps->maybe_region = 1; + traps->is_rectilinear = 0; + traps->is_rectangular = 0; + traps->num_traps = 0; traps->traps_size = ARRAY_LENGTH (traps->traps_embedded); traps->traps = traps->traps_embedded; - traps->extents.p1.x = traps->extents.p1.y = INT32_MAX; - traps->extents.p2.x = traps->extents.p2.y = INT32_MIN; - traps->has_limits = FALSE; + traps->num_limits = 0; + traps->has_intersections = FALSE; } void _cairo_traps_limit (cairo_traps_t *traps, - cairo_box_t *limits) + const cairo_box_t *limits, + int num_limits) { - traps->has_limits = TRUE; - - traps->limits = *limits; -} - -cairo_bool_t -_cairo_traps_get_limit (cairo_traps_t *traps, - cairo_box_t *limits) -{ - *limits = traps->limits; - return traps->has_limits; + traps->limits = limits; + traps->num_limits = num_limits; } void @@ -83,9 +78,12 @@ _cairo_traps_clear (cairo_traps_t *traps) { traps->status = CAIRO_STATUS_SUCCESS; + traps->maybe_region = 1; + traps->is_rectilinear = 0; + traps->is_rectangular = 0; + traps->num_traps = 0; - traps->extents.p1.x = traps->extents.p1.y = INT32_MAX; - traps->extents.p2.x = traps->extents.p2.y = INT32_MIN; + traps->has_intersections = FALSE; } void @@ -97,43 +95,12 @@ _cairo_traps_fini (cairo_traps_t *traps) VG (VALGRIND_MAKE_MEM_NOACCESS (traps, sizeof (cairo_traps_t))); } -/** - * _cairo_traps_init_box: - * @traps: a #cairo_traps_t - * @box: a box that will be converted to a single trapezoid - * to store in @traps. - * - * Initializes a #cairo_traps_t to contain a single rectangular - * trapezoid. - **/ -void -_cairo_traps_init_box (cairo_traps_t *traps, - const cairo_box_t *box) -{ - _cairo_traps_init (traps); - - assert (traps->traps_size >= 1); - - traps->num_traps = 1; - - traps->traps[0].top = box->p1.y; - traps->traps[0].bottom = box->p2.y; - traps->traps[0].left.p1 = box->p1; - traps->traps[0].left.p2.x = box->p1.x; - traps->traps[0].left.p2.y = box->p2.y; - traps->traps[0].right.p1.x = box->p2.x; - traps->traps[0].right.p1.y = box->p1.y; - traps->traps[0].right.p2 = box->p2; - - traps->extents = *box; -} - /* make room for at least one more trap */ static cairo_bool_t _cairo_traps_grow (cairo_traps_t *traps) { cairo_trapezoid_t *new_traps; - int new_size = 2 * MAX (traps->traps_size, 16); + int new_size = 4 * traps->traps_size; if (CAIRO_INJECT_FAULT ()) { traps->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -166,122 +133,165 @@ _cairo_traps_add_trap (cairo_traps_t *traps, { cairo_trapezoid_t *trap; - /* Note: With the goofy trapezoid specification, (where an - * arbitrary two points on the lines can specified for the left - * and right edges), these limit checks would not work in - * general. For example, one can imagine a trapezoid entirely - * within the limits, but with two points used to specify the left - * edge entirely to the right of the limits. Fortunately, for our - * purposes, cairo will never generate such a crazy - * trapezoid. Instead, cairo always uses for its points the - * extreme positions of the edge that are visible on at least some - * trapezoid. With this constraint, it's impossible for both - * points to be outside the limits while the relevant edge is - * entirely inside the limits. - */ - if (traps->has_limits) { - /* Trivially reject if trapezoid is entirely to the right or - * to the left of the limits. */ - if (left->p1.x >= traps->limits.p2.x && - left->p2.x >= traps->limits.p2.x) - { - return; - } - - if (right->p1.x <= traps->limits.p1.x && - right->p2.x <= traps->limits.p1.x) - { - return; - } - - /* And reject if the trapezoid is entirely above or below */ - if (top > traps->limits.p2.y || bottom < traps->limits.p1.y) - return; - - /* Otherwise, clip the trapezoid to the limits. We only clip - * where an edge is entirely outside the limits. If we wanted - * to be more clever, we could handle cases where a trapezoid - * edge intersects the edge of the limits, but that would - * require slicing this trapezoid into multiple trapezoids, - * and I'm not sure the effort would be worth it. */ - if (top < traps->limits.p1.y) - top = traps->limits.p1.y; - - if (bottom > traps->limits.p2.y) - bottom = traps->limits.p2.y; - - if (left->p1.x <= traps->limits.p1.x && - left->p2.x <= traps->limits.p1.x) - { - left->p1.x = traps->limits.p1.x; - left->p2.x = traps->limits.p1.x; - } - - if (right->p1.x >= traps->limits.p2.x && - right->p2.x >= traps->limits.p2.x) - { - right->p1.x = traps->limits.p2.x; - right->p2.x = traps->limits.p2.x; - } - } - - /* Trivial discards for empty trapezoids that are likely to be produced - * by our tessellators (most notably convex_quad when given a simple - * rectangle). - */ - if (top >= bottom) - return; - /* cheap colinearity check */ - if (right->p1.x <= left->p1.x && right->p1.y == left->p1.y && - right->p2.x <= left->p2.x && right->p2.y == left->p2.y) - return; - - if (traps->num_traps == traps->traps_size) { - if (! _cairo_traps_grow (traps)) + if (unlikely (traps->num_traps == traps->traps_size)) { + if (unlikely (! _cairo_traps_grow (traps))) return; } - trap = &traps->traps[traps->num_traps]; + trap = &traps->traps[traps->num_traps++]; trap->top = top; trap->bottom = bottom; trap->left = *left; trap->right = *right; - - if (top < traps->extents.p1.y) - traps->extents.p1.y = top; - if (bottom > traps->extents.p2.y) - traps->extents.p2.y = bottom; - /* - * This isn't generally accurate, but it is close enough for - * this purpose. Assuming that the left and right segments always - * contain the trapezoid vertical extents, these compares will - * yield a containing box. Assuming that the points all come from - * the same figure which will eventually be completely drawn, then - * the compares will yield the correct overall extents - */ - if (left->p1.x < traps->extents.p1.x) - traps->extents.p1.x = left->p1.x; - if (left->p2.x < traps->extents.p1.x) - traps->extents.p1.x = left->p2.x; - - if (right->p1.x > traps->extents.p2.x) - traps->extents.p2.x = right->p1.x; - if (right->p2.x > traps->extents.p2.x) - traps->extents.p2.x = right->p2.x; - - traps->num_traps++; } -static int -_compare_point_fixed_by_y (const void *av, const void *bv) +/** + * _cairo_traps_init_box: + * @traps: a #cairo_traps_t + * @box: an array box that will each be converted to a single trapezoid + * to store in @traps. + * + * Initializes a #cairo_traps_t to contain an array of rectangular + * trapezoids. + **/ +cairo_status_t +_cairo_traps_init_boxes (cairo_traps_t *traps, + const cairo_box_t *boxes, + int num_boxes) { - const cairo_point_t *a = av, *b = bv; + cairo_trapezoid_t *trap; - int ret = a->y - b->y; - if (ret == 0) { - ret = a->x - b->x; + _cairo_traps_init (traps); + + while (traps->traps_size < num_boxes) { + if (unlikely (! _cairo_traps_grow (traps))) { + _cairo_traps_fini (traps); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } } - return ret; + + traps->num_traps = num_boxes; + traps->is_rectilinear = TRUE; + traps->is_rectangular = TRUE; + + trap = &traps->traps[0]; + while (num_boxes--) { + trap->top = boxes->p1.y; + trap->bottom = boxes->p2.y; + + trap->left.p1 = boxes->p1; + trap->left.p2.x = boxes->p1.x; + trap->left.p2.y = boxes->p2.y; + + trap->right.p1.x = boxes->p2.x; + trap->right.p1.y = boxes->p1.y; + trap->right.p2 = boxes->p2; + + if (traps->maybe_region) { + traps->maybe_region = _cairo_fixed_is_integer (boxes->p1.x) && + _cairo_fixed_is_integer (boxes->p1.y) && + _cairo_fixed_is_integer (boxes->p2.x) && + _cairo_fixed_is_integer (boxes->p2.y); + } + + trap++, boxes++; + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_traps_tessellate_rectangle (cairo_traps_t *traps, + const cairo_point_t *top_left, + const cairo_point_t *bottom_right) +{ + cairo_line_t left; + cairo_line_t right; + cairo_fixed_t top, bottom; + + if (top_left->y == bottom_right->y) + return CAIRO_STATUS_SUCCESS; + + if (top_left->x == bottom_right->x) + return CAIRO_STATUS_SUCCESS; + + left.p1.x = left.p2.x = top_left->x; + left.p1.y = right.p1.y = top_left->y; + right.p1.x = right.p2.x = bottom_right->x; + left.p2.y = right.p2.y = bottom_right->y; + + top = top_left->y; + bottom = bottom_right->y; + + if (traps->num_limits) { + cairo_bool_t reversed; + int n; + + /* support counter-clockwise winding for rectangular tessellation */ + reversed = top_left->x > bottom_right->x; + if (reversed) { + right.p1.x = right.p2.x = top_left->x; + left.p1.x = left.p2.x = bottom_right->x; + } + + for (n = 0; n < traps->num_limits; n++) { + const cairo_box_t *limits = &traps->limits[n]; + cairo_line_t _left, _right; + cairo_fixed_t _top, _bottom; + + if (top >= limits->p2.y) + continue; + if (bottom <= limits->p1.y) + continue; + + /* Trivially reject if trapezoid is entirely to the right or + * to the left of the limits. */ + if (left.p1.x >= limits->p2.x) + continue; + if (right.p1.x <= limits->p1.x) + continue; + + /* Otherwise, clip the trapezoid to the limits. */ + _top = top; + if (_top < limits->p1.y) + _top = limits->p1.y; + + _bottom = bottom; + if (_bottom > limits->p2.y) + _bottom = limits->p2.y; + + if (_bottom <= _top) + continue; + + _left = left; + if (_left.p1.x < limits->p1.x) { + _left.p1.x = limits->p1.x; + _left.p1.y = limits->p1.y; + _left.p2.x = limits->p1.x; + _left.p2.y = limits->p2.y; + } + + _right = right; + if (_right.p1.x > limits->p2.x) { + _right.p1.x = limits->p2.x; + _right.p1.y = limits->p1.y; + _right.p2.x = limits->p2.x; + _right.p2.y = limits->p2.y; + } + + if (left.p1.x >= right.p1.x) + continue; + + if (reversed) + _cairo_traps_add_trap (traps, _top, _bottom, &_right, &_left); + else + _cairo_traps_add_trap (traps, _top, _bottom, &_left, &_right); + } + } else { + _cairo_traps_add_trap (traps, top, bottom, &left, &right); + } + + return traps->status; } void @@ -356,184 +366,6 @@ _cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps, } } -/* A triangle is simply a degenerate case of a convex - * quadrilateral. We would not benefit from having any distinct - * implementation of triangle vs. quadrilateral tessellation here. */ -cairo_status_t -_cairo_traps_tessellate_triangle (cairo_traps_t *traps, - const cairo_point_t t[3]) -{ - cairo_point_t quad[4]; - - quad[0] = t[0]; - quad[1] = t[0]; - quad[2] = t[1]; - quad[3] = t[2]; - - return _cairo_traps_tessellate_convex_quad (traps, quad); -} - -cairo_status_t -_cairo_traps_tessellate_rectangle (cairo_traps_t *traps, - const cairo_point_t *top_left, - const cairo_point_t *bottom_right) -{ - cairo_line_t left; - cairo_line_t right; - - left.p1.x = left.p2.x = top_left->x; - left.p1.y = right.p1.y = top_left->y; - right.p1.x = right.p2.x = bottom_right->x; - left.p2.y = right.p2.y = bottom_right->y; - - _cairo_traps_add_trap (traps, top_left->y, bottom_right->y, &left, &right); - - return traps->status; -} - -cairo_status_t -_cairo_traps_tessellate_convex_quad (cairo_traps_t *traps, - const cairo_point_t q[4]) -{ - int a, b, c, d; - int i; - cairo_slope_t ab, ad; - cairo_bool_t b_left_of_d; - cairo_line_t left; - cairo_line_t right; - - /* Choose a as a point with minimal y */ - a = 0; - for (i = 1; i < 4; i++) - if (_compare_point_fixed_by_y (&q[i], &q[a]) < 0) - a = i; - - /* b and d are adjacent to a, while c is opposite */ - b = (a + 1) % 4; - c = (a + 2) % 4; - d = (a + 3) % 4; - - /* Choose between b and d so that b.y is less than d.y */ - if (_compare_point_fixed_by_y (&q[d], &q[b]) < 0) { - b = (a + 3) % 4; - d = (a + 1) % 4; - } - - /* Without freedom left to choose anything else, we have four - * cases to tessellate. - * - * First, we have to determine the Y-axis sort of the four - * vertices, (either abcd or abdc). After that we need to detemine - * which edges will be "left" and which will be "right" in the - * resulting trapezoids. This can be determined by computing a - * slope comparison of ab and ad to determine if b is left of d or - * not. - * - * Note that "left of" here is in the sense of which edges should - * be the left vs. right edges of the trapezoid. In particular, b - * left of d does *not* mean that b.x is less than d.x. - * - * This should hopefully be made clear in the lame ASCII art - * below. Since the same slope comparison is used in all cases, we - * compute it before testing for the Y-value sort. */ - - /* Note: If a == b then the ab slope doesn't give us any - * information. In that case, we can replace it with the ac (or - * equivalenly the bc) slope which gives us exactly the same - * information we need. At worst the names of the identifiers ab - * and b_left_of_d are inaccurate in this case, (would be ac, and - * c_left_of_d). */ - if (q[a].x == q[b].x && q[a].y == q[b].y) - _cairo_slope_init (&ab, &q[a], &q[c]); - else - _cairo_slope_init (&ab, &q[a], &q[b]); - - _cairo_slope_init (&ad, &q[a], &q[d]); - - b_left_of_d = (_cairo_slope_compare (&ab, &ad) > 0); - - if (q[c].y <= q[d].y) { - if (b_left_of_d) { - /* Y-sort is abcd and b is left of d, (slope(ab) > slope (ad)) - * - * top bot left right - * _a a a - * / / /| |\ a.y b.y ab ad - * b / b | b \ - * / / | | \ \ b.y c.y bc ad - * c / c | c \ - * | / \| \ \ c.y d.y cd ad - * d d d - */ - left.p1 = q[a]; left.p2 = q[b]; - right.p1 = q[a]; right.p2 = q[d]; - _cairo_traps_add_trap (traps, q[a].y, q[b].y, &left, &right); - left.p1 = q[b]; left.p2 = q[c]; - _cairo_traps_add_trap (traps, q[b].y, q[c].y, &left, &right); - left.p1 = q[c]; left.p2 = q[d]; - _cairo_traps_add_trap (traps, q[c].y, q[d].y, &left, &right); - } else { - /* Y-sort is abcd and b is right of d, (slope(ab) <= slope (ad)) - * - * a a a_ - * /| |\ \ \ a.y b.y ad ab - * / b | b \ b - * / / | | \ \ b.y c.y ad bc - * / c | c \ c - * / / |/ \ | c.y d.y ad cd - * d d d - */ - left.p1 = q[a]; left.p2 = q[d]; - right.p1 = q[a]; right.p2 = q[b]; - _cairo_traps_add_trap (traps, q[a].y, q[b].y, &left, &right); - right.p1 = q[b]; right.p2 = q[c]; - _cairo_traps_add_trap (traps, q[b].y, q[c].y, &left, &right); - right.p1 = q[c]; right.p2 = q[d]; - _cairo_traps_add_trap (traps, q[c].y, q[d].y, &left, &right); - } - } else { - if (b_left_of_d) { - /* Y-sort is abdc and b is left of d, (slope (ab) > slope (ad)) - * - * a a a - * // / \ |\ a.y b.y ab ad - * /b/ b \ b \ - * / / \ \ \ \ b.y d.y bc ad - * /d/ \ d \ d - * // \ / \| d.y c.y bc dc - * c c c - */ - left.p1 = q[a]; left.p2 = q[b]; - right.p1 = q[a]; right.p2 = q[d]; - _cairo_traps_add_trap (traps, q[a].y, q[b].y, &left, &right); - left.p1 = q[b]; left.p2 = q[c]; - _cairo_traps_add_trap (traps, q[b].y, q[d].y, &left, &right); - right.p1 = q[d]; right.p2 = q[c]; - _cairo_traps_add_trap (traps, q[d].y, q[c].y, &left, &right); - } else { - /* Y-sort is abdc and b is right of d, (slope (ab) <= slope (ad)) - * - * a a a - * /| / \ \\ a.y b.y ad ab - * / b / b \b\ - * / / / / \ \ b.y d.y ad bc - * d / d / \d\ - * |/ \ / \\ d.y c.y dc bc - * c c c - */ - left.p1 = q[a]; left.p2 = q[d]; - right.p1 = q[a]; right.p2 = q[b]; - _cairo_traps_add_trap (traps, q[a].y, q[b].y, &left, &right); - right.p1 = q[b]; right.p2 = q[c]; - _cairo_traps_add_trap (traps, q[b].y, q[d].y, &left, &right); - left.p1 = q[d]; left.p2 = q[c]; - _cairo_traps_add_trap (traps, q[d].y, q[c].y, &left, &right); - } - } - - return traps->status; -} - static cairo_bool_t _cairo_trap_contains (cairo_trapezoid_t *t, cairo_point_t *pt) { @@ -577,30 +409,81 @@ _cairo_traps_contain (const cairo_traps_t *traps, return FALSE; } +static cairo_fixed_t +_line_compute_intersection_x_for_y (const cairo_line_t *line, + cairo_fixed_t y) +{ + return _cairo_edge_compute_intersection_x_for_y (&line->p1, &line->p2, y); +} + void _cairo_traps_extents (const cairo_traps_t *traps, - cairo_box_t *extents) + cairo_box_t *extents) { - if (traps->num_traps == 0) { - extents->p1.x = extents->p1.y = _cairo_fixed_from_int (0); - extents->p2.x = extents->p2.y = _cairo_fixed_from_int (0); - } else { - *extents = traps->extents; - if (traps->has_limits) { - /* clip the traps to the imposed limits */ - if (extents->p1.x < traps->limits.p1.x) - extents->p1.x = traps->limits.p1.x; - if (extents->p2.x > traps->limits.p2.x) - extents->p2.x = traps->limits.p2.x; + int i; - if (extents->p1.y < traps->limits.p1.y) - extents->p1.y = traps->limits.p1.y; - if (extents->p2.y > traps->limits.p2.y) - extents->p2.y = traps->limits.p2.y; + if (traps->num_traps == 0) { + extents->p1.x = extents->p1.y = 0; + extents->p2.x = extents->p2.y = 0; + return; + } + + extents->p1.x = extents->p1.y = INT32_MAX; + extents->p2.x = extents->p2.y = INT32_MIN; + + for (i = 0; i < traps->num_traps; i++) { + const cairo_trapezoid_t *trap = &traps->traps[i]; + + if (trap->top < extents->p1.y) + extents->p1.y = trap->top; + if (trap->bottom > extents->p2.y) + extents->p2.y = trap->bottom; + + if (trap->left.p1.x < extents->p1.x) { + cairo_fixed_t x = trap->left.p1.x; + if (trap->top != trap->left.p1.y) { + x = _line_compute_intersection_x_for_y (&trap->left, + trap->top); + if (x < extents->p1.x) + extents->p1.x = x; + } else + extents->p1.x = x; + } + if (trap->left.p2.x < extents->p1.x) { + cairo_fixed_t x = trap->left.p2.x; + if (trap->bottom != trap->left.p2.y) { + x = _line_compute_intersection_x_for_y (&trap->left, + trap->bottom); + if (x < extents->p1.x) + extents->p1.x = x; + } else + extents->p1.x = x; + } + + if (trap->right.p1.x > extents->p2.x) { + cairo_fixed_t x = trap->right.p1.x; + if (trap->top != trap->right.p1.y) { + x = _line_compute_intersection_x_for_y (&trap->right, + trap->top); + if (x > extents->p2.x) + extents->p2.x = x; + } else + extents->p2.x = x; + } + if (trap->right.p2.x > extents->p2.x) { + cairo_fixed_t x = trap->right.p2.x; + if (trap->bottom != trap->right.p2.y) { + x = _line_compute_intersection_x_for_y (&trap->right, + trap->bottom); + if (x > extents->p2.x) + extents->p2.x = x; + } else + extents->p2.x = x; } } } + /** * _cairo_traps_extract_region: * @traps: a #cairo_traps_t @@ -616,14 +499,18 @@ _cairo_traps_extents (const cairo_traps_t *traps, * or %CAIRO_STATUS_NO_MEMORY **/ cairo_int_status_t -_cairo_traps_extract_region (const cairo_traps_t *traps, - cairo_region_t **region) +_cairo_traps_extract_region (cairo_traps_t *traps, + cairo_region_t **region) { cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; cairo_rectangle_int_t *rects = stack_rects; cairo_int_status_t status; int i, rect_count; + /* we only treat this a hint... */ + if (! traps->maybe_region) + return CAIRO_INT_STATUS_UNSUPPORTED; + for (i = 0; i < traps->num_traps; i++) { if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x || traps->traps[i].right.p1.x != traps->traps[i].right.p2.x || @@ -632,6 +519,7 @@ _cairo_traps_extract_region (const cairo_traps_t *traps, ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) { + traps->maybe_region = FALSE; return CAIRO_INT_STATUS_UNSUPPORTED; } } @@ -650,12 +538,6 @@ _cairo_traps_extract_region (const cairo_traps_t *traps, int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x); int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom); - /* XXX: Sometimes we get degenerate trapezoids from the tesellator; - * skip these. - */ - if (x1 == x2 || y1 == y2) - continue; - rects[rect_count].x = x1; rects[rect_count].y = y1; rects[rect_count].width = x2 - x1; @@ -663,18 +545,13 @@ _cairo_traps_extract_region (const cairo_traps_t *traps, rect_count++; } - + *region = cairo_region_create_rectangles (rects, rect_count); - status = cairo_region_status (*region); + status = (*region)->status; if (rects != stack_rects) free (rects); - if (unlikely (status)) { - cairo_region_destroy (*region); - *region = NULL; - } - return status; } @@ -686,7 +563,7 @@ _sanitize_trap (cairo_trapezoid_t *t) #define FIX(lr, tb, p) \ if (t->lr.p.y != t->tb) { \ - t->lr.p.x = s.lr.p2.x + _cairo_fixed_mul_div (s.lr.p1.x - s.lr.p2.x, s.tb - s.lr.p2.y, s.lr.p1.y - s.lr.p2.y); \ + t->lr.p.x = s.lr.p2.x + _cairo_fixed_mul_div_floor (s.lr.p1.x - s.lr.p2.x, s.tb - s.lr.p2.y, s.lr.p1.y - s.lr.p2.y); \ t->lr.p.y = s.tb; \ } FIX (left, top, p1); diff --git a/gfx/cairo/cairo/src/cairo-truetype-subset.c b/gfx/cairo/cairo/src/cairo-truetype-subset.c index 92c0547751b0..3a813ee7e942 100644 --- a/gfx/cairo/cairo/src/cairo-truetype-subset.c +++ b/gfx/cairo/cairo/src/cairo-truetype-subset.c @@ -216,6 +216,7 @@ _cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset, if (font->base.units_per_em == 0) font->base.units_per_em = 2048; + font->base.ps_name = NULL; font->base.font_name = NULL; status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font, &font->base.ps_name, diff --git a/gfx/cairo/cairo/src/cairo-type1-subset.c b/gfx/cairo/cairo/src/cairo-type1-subset.c index fe74dc6c5bdb..7ded39a41081 100644 --- a/gfx/cairo/cairo/src/cairo-type1-subset.c +++ b/gfx/cairo/cairo/src/cairo-type1-subset.c @@ -142,7 +142,7 @@ _cairo_type1_font_subset_init (cairo_type1_font_subset_t *font, } #endif - memset (font, 0, sizeof (font)); + memset (font, 0, sizeof (*font)); font->base.unscaled_font = _cairo_unscaled_font_reference (unscaled_font); font->base.num_glyphs = face->num_glyphs; font->base.x_min = face->bbox.xMin; @@ -235,7 +235,7 @@ cairo_type1_font_subset_find_segments (cairo_type1_font_subset_t *font) { unsigned char *p; const char *eexec_token; - int size; + int size, i; p = (unsigned char *) font->type1_data; font->type1_end = font->type1_data + font->type1_length; @@ -266,6 +266,10 @@ cairo_type1_font_subset_find_segments (cairo_type1_font_subset_t *font) font->eexec_segment_size = font->type1_length - font->header_segment_size; font->eexec_segment = (char *) p + font->header_segment_size; font->eexec_segment_is_ascii = TRUE; + for (i = 0; i < 4; i++) { + if (!isxdigit(font->eexec_segment[i])) + font->eexec_segment_is_ascii = FALSE; + } } return CAIRO_STATUS_SUCCESS; @@ -295,8 +299,8 @@ cairo_type1_font_erase_dict_key (cairo_type1_font_subset_t *font, p = start + strlen(key); /* skip integers or array of integers */ while (p < segment_end && - (isspace(*p) || - isdigit(*p) || + (_cairo_isspace(*p) || + _cairo_isdigit(*p) || *p == '[' || *p == ']')) { @@ -354,7 +358,7 @@ cairo_type1_font_subset_write_header (cairo_type1_font_subset_t *font, start = find_token (font->header_segment, segment_end, "/UniqueID"); if (start) { start += 9; - while (start < segment_end && isspace (*start)) + while (start < segment_end && _cairo_isspace (*start)) start++; if (start + 5 < segment_end && memcmp(start, "known", 5) == 0) { _cairo_output_stream_write (font->output, font->header_segment, @@ -474,7 +478,7 @@ cairo_type1_font_subset_decrypt_eexec_segment (cairo_type1_font_subset_t *font) while (in < end) { if (font->eexec_segment_is_ascii) { c = *in++; - if (isspace (c)) + if (_cairo_isspace (c)) continue; c = (hex_to_int (c) << 4) | hex_to_int (*in++); } else { @@ -510,10 +514,10 @@ cairo_type1_font_subset_decrypt_eexec_segment (cairo_type1_font_subset_t *font) static const char * skip_token (const char *p, const char *end) { - while (p < end && isspace(*p)) + while (p < end && _cairo_isspace(*p)) p++; - while (p < end && !isspace(*p)) + while (p < end && !_cairo_isspace(*p)) p++; if (p == end) @@ -969,7 +973,7 @@ cairo_type1_font_subset_for_each_glyph (cairo_type1_font_subset_t *font, /* Skip binary data and |- or ND token. */ p = skip_token (charstring + charstring_length, dict_end); - while (p < dict_end && isspace(*p)) + while (p < dict_end && _cairo_isspace(*p)) p++; /* In case any of the skip_token() calls above reached EOF, p will @@ -1110,7 +1114,8 @@ cairo_type1_font_subset_write_private_dict (cairo_type1_font_subset_t *font, if (unlikely (status)) return status; - _cairo_output_stream_write (font->output, "\n", 1); + if (font->hex_encode) + _cairo_output_stream_write (font->output, "\n", 1); return CAIRO_STATUS_SUCCESS; } @@ -1123,19 +1128,27 @@ cairo_type1_font_subset_write_trailer(cairo_type1_font_subset_t *font) static const char zeros[65] = "0000000000000000000000000000000000000000000000000000000000000000\n"; - /* Some fonts have conditional save/restore around the entire font - * dict, so we need to retain whatever postscript code that may - * come after 'cleartomark'. */ for (i = 0; i < 8; i++) _cairo_output_stream_write (font->output, zeros, sizeof zeros); cleartomark_token = find_token (font->type1_data, font->type1_end, "cleartomark"); - if (cleartomark_token == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (cleartomark_token) { + /* Some fonts have conditional save/restore around the entire + * font dict, so we need to retain whatever postscript code + * that may come after 'cleartomark'. */ - _cairo_output_stream_write (font->output, cleartomark_token, - font->type1_end - cleartomark_token); + _cairo_output_stream_write (font->output, cleartomark_token, + font->type1_end - cleartomark_token); + } else if (!font->eexec_segment_is_ascii) { + /* Fonts embedded in PDF may omit the fixed-content portion + * that includes the 'cleartomark' operator. Type 1 in PDF is + * always binary. */ + + _cairo_output_stream_printf (font->output, "cleartomark"); + } else { + return CAIRO_INT_STATUS_UNSUPPORTED; + } /* some fonts do not have a newline at the end of the last line */ _cairo_output_stream_printf (font->output, "\n"); @@ -1243,10 +1256,8 @@ cairo_type1_font_subset_generate (void *abstract_font, goto fail; font->output = _cairo_output_stream_create (type1_font_write, NULL, font); - if (_cairo_output_stream_get_status (font->output)) { - status = _cairo_output_stream_destroy (font->output); + if (unlikely ((status = font->output->status))) goto fail; - } status = cairo_type1_font_subset_write (font, name); if (unlikely (status)) @@ -1260,9 +1271,10 @@ cairo_type1_font_subset_generate (void *abstract_font, return status; } -static void +static cairo_status_t _cairo_type1_font_subset_fini (cairo_type1_font_subset_t *font) { + cairo_status_t status = CAIRO_STATUS_SUCCESS; unsigned int i; /* If the subset generation failed, some of the pointers below may @@ -1271,16 +1283,21 @@ _cairo_type1_font_subset_fini (cairo_type1_font_subset_t *font) _cairo_array_fini (&font->contents); free (font->type1_data); - if (font->glyphs != NULL) - for (i = 0; i < font->base.num_glyphs; i++) { + if (font->glyphs != NULL) { + for (i = 0; i < font->base.num_glyphs; i++) free (font->glyphs[i].name); - } + } _cairo_unscaled_font_destroy (font->base.unscaled_font); + if (font->output != NULL) + status = _cairo_output_stream_destroy (font->output); + if (font->base.base_font) free (font->base.base_font); free (font->glyphs); + + return status; } cairo_status_t @@ -1290,7 +1307,7 @@ _cairo_type1_subset_init (cairo_type1_subset_t *type1_subset, cairo_bool_t hex_encode) { cairo_type1_font_subset_t font; - cairo_status_t status; + cairo_status_t status, status_ignored; unsigned long parent_glyph, length; unsigned int i; cairo_unscaled_font_t *unscaled_font; @@ -1359,16 +1376,14 @@ _cairo_type1_subset_init (cairo_type1_subset_t *type1_subset, type1_subset->data_length = font.base.data_size; type1_subset->trailer_length = font.base.trailer_size; - _cairo_type1_font_subset_fini (&font); - - return CAIRO_STATUS_SUCCESS; + return _cairo_type1_font_subset_fini (&font); fail3: free (type1_subset->widths); fail2: free (type1_subset->base_font); fail1: - _cairo_type1_font_subset_fini (&font); + status_ignored = _cairo_type1_font_subset_fini (&font); return status; } diff --git a/gfx/cairo/cairo/src/cairo-type3-glyph-surface-private.h b/gfx/cairo/cairo/src/cairo-type3-glyph-surface-private.h index 33314ae9e690..0b0010bcca48 100644 --- a/gfx/cairo/cairo/src/cairo-type3-glyph-surface-private.h +++ b/gfx/cairo/cairo/src/cairo-type3-glyph-surface-private.h @@ -42,6 +42,7 @@ #if CAIRO_HAS_FONT_SUBSET #include "cairo-surface-private.h" +#include "cairo-surface-clipper-private.h" #include "cairo-pdf-operators-private.h" typedef cairo_status_t (*cairo_type3_glyph_surface_emit_image_t) (cairo_image_surface_t *image, @@ -55,16 +56,18 @@ typedef struct cairo_type3_glyph_surface { cairo_pdf_operators_t pdf_operators; cairo_matrix_t cairo_to_pdf; cairo_type3_glyph_surface_emit_image_t emit_image; + + cairo_surface_clipper_t clipper; } cairo_type3_glyph_surface_t; cairo_private cairo_surface_t * -_cairo_type3_glyph_surface_create (cairo_scaled_font_t *scaled_font, +_cairo_type3_glyph_surface_create (cairo_scaled_font_t *scaled_font, cairo_output_stream_t *stream, cairo_type3_glyph_surface_emit_image_t emit_image, - cairo_scaled_font_subsets_t *font_subsets); + cairo_scaled_font_subsets_t *font_subsets); cairo_private void -_cairo_type3_glyph_surface_set_font_subsets_callback (void *abstract_surface, +_cairo_type3_glyph_surface_set_font_subsets_callback (void *abstract_surface, cairo_pdf_operators_use_font_subset_t use_font_subset, void *closure); diff --git a/gfx/cairo/cairo/src/cairo-type3-glyph-surface.c b/gfx/cairo/cairo/src/cairo-type3-glyph-surface.c index 6e7259609119..6878bbaae25d 100644 --- a/gfx/cairo/cairo/src/cairo-type3-glyph-surface.c +++ b/gfx/cairo/cairo/src/cairo-type3-glyph-surface.c @@ -40,11 +40,33 @@ #include "cairo-type3-glyph-surface-private.h" #include "cairo-output-stream-private.h" -#include "cairo-meta-surface-private.h" +#include "cairo-recording-surface-private.h" #include "cairo-analysis-surface-private.h" +#include "cairo-surface-clipper-private.h" static const cairo_surface_backend_t cairo_type3_glyph_surface_backend; +static cairo_status_t +_cairo_type3_glyph_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_type3_glyph_surface_t *surface = cairo_container_of (clipper, + cairo_type3_glyph_surface_t, + clipper); + + if (path == NULL) { + _cairo_output_stream_printf (surface->stream, "Q q\n"); + return CAIRO_STATUS_SUCCESS; + } + + return _cairo_pdf_operators_clip (&surface->pdf_operators, + path, + fill_rule); +} + cairo_surface_t * _cairo_type3_glyph_surface_create (cairo_scaled_font_t *scaled_font, cairo_output_stream_t *stream, @@ -81,6 +103,9 @@ _cairo_type3_glyph_surface_create (cairo_scaled_font_t *scaled_font, &surface->cairo_to_pdf, font_subsets); + _cairo_surface_clipper_init (&surface->clipper, + _cairo_type3_glyph_surface_clipper_intersect_clip_path); + return &surface->base; } @@ -155,30 +180,11 @@ _cairo_type3_glyph_surface_finish (void *abstract_surface) return _cairo_pdf_operators_fini (&surface->pdf_operators); } -static cairo_int_status_t -_cairo_type3_glyph_surface_intersect_clip_path (void *abstract_surface, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_type3_glyph_surface_t *surface = abstract_surface; - - if (path == NULL) { - _cairo_output_stream_printf (surface->stream, "Q q\n"); - return CAIRO_STATUS_SUCCESS; - } - - return _cairo_pdf_operators_clip (&surface->pdf_operators, - path, - fill_rule); -} - static cairo_int_status_t _cairo_type3_glyph_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_type3_glyph_surface_t *surface = abstract_surface; const cairo_surface_pattern_t *pattern; @@ -189,8 +195,13 @@ _cairo_type3_glyph_surface_paint (void *abstract_surface, if (source->type != CAIRO_PATTERN_TYPE_SURFACE) return CAIRO_INT_STATUS_IMAGE_FALLBACK; + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + pattern = (const cairo_surface_pattern_t *) source; - status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); + status = _cairo_surface_acquire_source_image (pattern->surface, + &image, &image_extra); if (unlikely (status)) goto fail; @@ -209,9 +220,11 @@ _cairo_type3_glyph_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { - return _cairo_type3_glyph_surface_paint (abstract_surface, op, mask, extents); + return _cairo_type3_glyph_surface_paint (abstract_surface, + op, mask, + clip); } static cairo_int_status_t @@ -224,9 +237,14 @@ _cairo_type3_glyph_surface_stroke (void *abstract_surface, cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_type3_glyph_surface_t *surface = abstract_surface; + cairo_int_status_t status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; return _cairo_pdf_operators_stroke (&surface->pdf_operators, path, @@ -243,16 +261,18 @@ _cairo_type3_glyph_surface_fill (void *abstract_surface, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_type3_glyph_surface_t *surface = abstract_surface; cairo_int_status_t status; - status = _cairo_pdf_operators_fill (&surface->pdf_operators, - path, - fill_rule); + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; - return status; + return _cairo_pdf_operators_fill (&surface->pdf_operators, + path, + fill_rule); } static cairo_int_status_t @@ -262,8 +282,8 @@ _cairo_type3_glyph_surface_show_glyphs (void *abstract_surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip, + int *remaining_glyphs) { cairo_type3_glyph_surface_t *surface = abstract_surface; cairo_int_status_t status; @@ -271,8 +291,14 @@ _cairo_type3_glyph_surface_show_glyphs (void *abstract_surface, cairo_matrix_t new_ctm, ctm_inverse; int i; - for (i = 0; i < num_glyphs; i++) - cairo_matrix_transform_point (&surface->cairo_to_pdf, &glyphs[i].x, &glyphs[i].y); + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + for (i = 0; i < num_glyphs; i++) { + cairo_matrix_transform_point (&surface->cairo_to_pdf, + &glyphs[i].x, &glyphs[i].y); + } /* We require the matrix to be invertable. */ ctm_inverse = scaled_font->ctm; @@ -316,8 +342,6 @@ static const cairo_surface_backend_t cairo_type3_glyph_surface_backend = { NULL, /* check_span_renderer */ NULL, /* cairo_type3_glyph_surface_copy_page */ NULL, /* _cairo_type3_glyph_surface_show_page */ - NULL, /* set_clip_region */ - _cairo_type3_glyph_surface_intersect_clip_path, NULL, /* _cairo_type3_glyph_surface_get_extents */ NULL, /* old_show_glyphs */ NULL, /* _cairo_type3_glyph_surface_get_font_options */ @@ -414,7 +438,7 @@ _cairo_type3_glyph_surface_analyze_glyph (void *abstract_surface, status = _cairo_scaled_glyph_lookup (surface->scaled_font, glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS | - CAIRO_SCALED_GLYPH_INFO_META_SURFACE, + CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, &scaled_glyph); if (_cairo_status_is_error (status)) @@ -425,8 +449,8 @@ _cairo_type3_glyph_surface_analyze_glyph (void *abstract_surface, goto cleanup; } - status = cairo_meta_surface_replay (scaled_glyph->meta_surface, - &surface->base); + status = _cairo_recording_surface_replay (scaled_glyph->recording_surface, + &surface->base); if (unlikely (status)) goto cleanup; @@ -469,7 +493,7 @@ _cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, status = _cairo_scaled_glyph_lookup (surface->scaled_font, glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS | - CAIRO_SCALED_GLYPH_INFO_META_SURFACE, + CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, &scaled_glyph); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { status = _cairo_scaled_glyph_lookup (surface->scaled_font, @@ -520,8 +544,8 @@ _cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, _cairo_type3_glyph_surface_set_stream (surface, mem_stream); _cairo_output_stream_printf (surface->stream, "q\n"); - status = cairo_meta_surface_replay (scaled_glyph->meta_surface, - &surface->base); + status = _cairo_recording_surface_replay (scaled_glyph->recording_surface, + &surface->base); status2 = _cairo_pdf_operators_flush (&surface->pdf_operators); if (status == CAIRO_STATUS_SUCCESS) diff --git a/gfx/cairo/cairo/src/cairo-types-private.h b/gfx/cairo/cairo/src/cairo-types-private.h index 2ed3264b62dd..3a3b8496273b 100644 --- a/gfx/cairo/cairo/src/cairo-types-private.h +++ b/gfx/cairo/cairo/src/cairo-types-private.h @@ -41,6 +41,7 @@ #include "cairo.h" #include "cairo-fixed-type-private.h" +#include "cairo-list-private.h" #include "cairo-reference-count-private.h" typedef struct _cairo_array cairo_array_t; @@ -63,6 +64,7 @@ typedef struct _cairo_scaled_font_backend cairo_scaled_font_backend_t; typedef struct _cairo_scaled_font_subsets cairo_scaled_font_subsets_t; typedef struct _cairo_solid_pattern cairo_solid_pattern_t; typedef struct _cairo_surface_backend cairo_surface_backend_t; +typedef struct _cairo_surface_wrapper cairo_surface_wrapper_t; typedef struct _cairo_unscaled_font_backend cairo_unscaled_font_backend_t; typedef struct _cairo_xlib_screen_info cairo_xlib_screen_info_t; @@ -156,7 +158,7 @@ typedef enum _cairo_int_status { CAIRO_INT_STATUS_NOTHING_TO_DO, CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY, CAIRO_INT_STATUS_IMAGE_FALLBACK, - CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN, + CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN, CAIRO_INT_STATUS_LAST_STATUS } cairo_int_status_t; @@ -164,20 +166,18 @@ typedef enum _cairo_int_status { typedef enum _cairo_internal_surface_type { CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED = 0x1000, CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS, - CAIRO_INTERNAL_SURFACE_TYPE_TEST_META, CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED, + CAIRO_INTERNAL_SURFACE_TYPE_TEST_WRAPPING, CAIRO_INTERNAL_SURFACE_TYPE_NULL, CAIRO_INTERNAL_SURFACE_TYPE_TYPE3_GLYPH } cairo_internal_surface_type_t; -typedef struct _cairo_point { - cairo_fixed_t x; - cairo_fixed_t y; -} cairo_point_t; +#define CAIRO_HAS_TEST_PAGINATED_SURFACE 1 +#define CAIRO_HAS_TEST_NULL_SURFACE 1 +#define CAIRO_HAS_TEST_WRAPPING_SURFACE 1 -typedef struct _cairo_slope -{ +typedef struct _cairo_slope { cairo_fixed_t dx; cairo_fixed_t dy; } cairo_slope_t, cairo_distance_t; @@ -237,14 +237,9 @@ typedef enum _cairo_direction { CAIRO_DIRECTION_REVERSE } cairo_direction_t; -typedef enum _cairo_clip_mode { - CAIRO_CLIP_MODE_PATH, - CAIRO_CLIP_MODE_REGION, - CAIRO_CLIP_MODE_MASK -} cairo_clip_mode_t; - typedef struct _cairo_edge { - cairo_line_t edge; + cairo_line_t line; + int top, bottom; int dir; } cairo_edge_t; @@ -252,8 +247,16 @@ typedef struct _cairo_polygon { cairo_status_t status; cairo_point_t first_point; + cairo_point_t last_point; cairo_point_t current_point; + cairo_slope_t current_edge; cairo_bool_t has_current_point; + cairo_bool_t has_current_edge; + + cairo_box_t extents; + cairo_box_t limit; + const cairo_box_t *limits; + int num_limits; int num_edges; int edges_size; @@ -337,4 +340,75 @@ struct _cairo_mime_data { void *closure; }; +struct _cairo_pattern { + cairo_pattern_type_t type; + cairo_reference_count_t ref_count; + cairo_status_t status; + cairo_user_data_array_t user_data; + + cairo_matrix_t matrix; + cairo_filter_t filter; + cairo_extend_t extend; + + cairo_bool_t has_component_alpha; +}; + +struct _cairo_solid_pattern { + cairo_pattern_t base; + cairo_color_t color; + cairo_content_t content; +}; + +typedef struct _cairo_surface_pattern { + cairo_pattern_t base; + + cairo_surface_t *surface; +} cairo_surface_pattern_t; + +typedef struct _cairo_gradient_stop { + double offset; + cairo_color_t color; +} cairo_gradient_stop_t; + +typedef struct _cairo_gradient_pattern { + cairo_pattern_t base; + + unsigned int n_stops; + unsigned int stops_size; + cairo_gradient_stop_t *stops; + cairo_gradient_stop_t stops_embedded[2]; +} cairo_gradient_pattern_t; + +typedef struct _cairo_linear_pattern { + cairo_gradient_pattern_t base; + + cairo_point_t p1; + cairo_point_t p2; +} cairo_linear_pattern_t; + +typedef struct _cairo_radial_pattern { + cairo_gradient_pattern_t base; + + cairo_point_t c1; + cairo_fixed_t r1; + cairo_point_t c2; + cairo_fixed_t r2; +} cairo_radial_pattern_t; + +typedef union { + cairo_gradient_pattern_t base; + + cairo_linear_pattern_t linear; + cairo_radial_pattern_t radial; +} cairo_gradient_pattern_union_t; + +typedef union { + cairo_pattern_type_t type; + cairo_pattern_t base; + + cairo_solid_pattern_t solid; + cairo_surface_pattern_t surface; + cairo_gradient_pattern_union_t gradient; +} cairo_pattern_union_t; + #endif /* CAIRO_TYPES_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-user-font.c b/gfx/cairo/cairo/src/cairo-user-font.c index 1adfd7d64298..3304392f4501 100644 --- a/gfx/cairo/cairo/src/cairo-user-font.c +++ b/gfx/cairo/cairo/src/cairo-user-font.c @@ -36,7 +36,7 @@ #include "cairoint.h" #include "cairo-user-font-private.h" -#include "cairo-meta-surface-private.h" +#include "cairo-recording-surface-private.h" #include "cairo-analysis-surface-private.h" typedef struct _cairo_user_scaled_font_methods { @@ -75,19 +75,19 @@ typedef struct _cairo_user_scaled_font { /* #cairo_user_scaled_font_t */ static cairo_t * -_cairo_user_scaled_font_create_meta_context (cairo_user_scaled_font_t *scaled_font) +_cairo_user_scaled_font_create_recording_context (cairo_user_scaled_font_t *scaled_font) { cairo_content_t content; - cairo_surface_t *meta_surface; + cairo_surface_t *recording_surface; cairo_t *cr; content = scaled_font->base.options.antialias == CAIRO_ANTIALIAS_SUBPIXEL ? CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_ALPHA; - meta_surface = cairo_meta_surface_create (content, -1, -1); - cr = cairo_create (meta_surface); - cairo_surface_destroy (meta_surface); + recording_surface = cairo_recording_surface_create (content, NULL); + cr = cairo_create (recording_surface); + cairo_surface_destroy (recording_surface); cairo_set_matrix (cr, &scaled_font->base.scale); cairo_set_font_size (cr, 1.0); @@ -104,64 +104,53 @@ _cairo_user_scaled_glyph_init (void *abstract_font, { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_user_scaled_font_t *scaled_font = abstract_font; - cairo_surface_t *meta_surface = scaled_glyph->meta_surface; + cairo_surface_t *recording_surface = scaled_glyph->recording_surface; - if (!scaled_glyph->meta_surface) { + if (!scaled_glyph->recording_surface) { cairo_user_font_face_t *face = (cairo_user_font_face_t *) scaled_font->base.font_face; cairo_text_extents_t extents = scaled_font->default_glyph_extents; cairo_t *cr; - cr = _cairo_user_scaled_font_create_meta_context (scaled_font); + cr = _cairo_user_scaled_font_create_recording_context (scaled_font); - if (face->scaled_font_methods.render_glyph) + if (face->scaled_font_methods.render_glyph) { status = face->scaled_font_methods.render_glyph ((cairo_scaled_font_t *)scaled_font, _cairo_scaled_glyph_index(scaled_glyph), cr, &extents); - else + } else status = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; if (status == CAIRO_STATUS_SUCCESS) status = cairo_status (cr); - meta_surface = cairo_surface_reference (cairo_get_target (cr)); + recording_surface = cairo_surface_reference (cairo_get_target (cr)); cairo_destroy (cr); if (unlikely (status)) { - cairo_surface_destroy (meta_surface); + cairo_surface_destroy (recording_surface); return status; } - _cairo_scaled_glyph_set_meta_surface (scaled_glyph, - &scaled_font->base, - meta_surface); + _cairo_scaled_glyph_set_recording_surface (scaled_glyph, + &scaled_font->base, + recording_surface); /* set metrics */ if (extents.width == 0.) { - /* Compute extents.x/y/width/height from meta_surface, in font space */ - cairo_box_t bbox; double x1, y1, x2, y2; double x_scale, y_scale; - cairo_surface_t *null_surface; - cairo_surface_t *analysis_surface; - - null_surface = _cairo_null_surface_create (cairo_surface_get_content (meta_surface)); - analysis_surface = _cairo_analysis_surface_create (null_surface, -1, -1); - cairo_surface_destroy (null_surface); - status = analysis_surface->status; - if (unlikely (status)) - return status; - - _cairo_analysis_surface_set_ctm (analysis_surface, - &scaled_font->extent_scale); - status = cairo_meta_surface_replay (meta_surface, analysis_surface); - _cairo_analysis_surface_get_bounding_box (analysis_surface, &bbox); - cairo_surface_destroy (analysis_surface); + /* Compute extents.x/y/width/height from recording_surface, + * in font space. + */ + status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, + &bbox, + &scaled_font->extent_scale); if (unlikely (status)) return status; @@ -213,7 +202,7 @@ _cairo_user_scaled_glyph_init (void *abstract_font, cairo_surface_set_device_offset (surface, - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x), - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y)); - status = cairo_meta_surface_replay (meta_surface, surface); + status = _cairo_recording_surface_replay (recording_surface, surface); if (unlikely (status)) { cairo_surface_destroy(surface); @@ -230,8 +219,7 @@ _cairo_user_scaled_glyph_init (void *abstract_font, if (!path) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - status = _cairo_meta_surface_get_path (meta_surface, path); - + status = _cairo_recording_surface_get_path (recording_surface, path); if (unlikely (status)) { _cairo_path_fixed_destroy (path); return status; @@ -433,7 +421,7 @@ _cairo_user_font_face_scaled_font_create (void *abstract_ if (status == CAIRO_STATUS_SUCCESS) { cairo_t *cr; - cr = _cairo_user_scaled_font_create_meta_context (user_scaled_font); + cr = _cairo_user_scaled_font_create_recording_context (user_scaled_font); status = font_face->scaled_font_methods.init (&user_scaled_font->base, cr, diff --git a/gfx/cairo/cairo/src/cairo-vg-surface.c b/gfx/cairo/cairo/src/cairo-vg-surface.c new file mode 100644 index 000000000000..5ca01b6e0e4c --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-vg-surface.c @@ -0,0 +1,1930 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2008 Opened Hand Ltd. + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.og/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * Contributor(s): + * Pierre Tardy + * Øyvind KolÃ¥s + * Vladimi Vukicevic (stubbed out base backend) + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-vg.h" + +#include "cairo-path-fixed-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-surface-clipper-private.h" +#include "cairo-cache-private.h" + +#include +#include + +//#define OPENVG_DEBUG + +/* + * Work that needs to be done: + * - Glyph cache / proper font support + * + * - First-class paths + * Paths are expensive for OpenVG, reuse paths whenever possible. + * So add a path cache, and first class paths! + */ + +typedef struct _cairo_vg_surface cairo_vg_surface_t; + +/* XXX need GL specific context control. :( */ +struct _cairo_vg_context { + cairo_status_t status; + cairo_reference_count_t ref_count; + + unsigned long target_id; + + VGPaint paint; + cairo_vg_surface_t *source; + double alpha; + + cairo_cache_t snapshot_cache; + + void *display; + void *context; + + cairo_status_t (*create_target) (cairo_vg_context_t *, + cairo_vg_surface_t *); + cairo_status_t (*set_target) (cairo_vg_context_t *, + cairo_vg_surface_t *); + void (*destroy_target) (cairo_vg_context_t *, cairo_vg_surface_t *); +}; + +struct _cairo_vg_surface { + cairo_surface_t base; + + cairo_vg_context_t *context; + + VGImage image; + VGImageFormat format; + int width; + int height; + cairo_bool_t own_image; + + cairo_cache_entry_t snapshot_cache_entry; + + cairo_surface_clipper_t clipper; + + unsigned long target_id; +}; + +static const cairo_surface_backend_t cairo_vg_surface_backend; + +slim_hidden_proto (cairo_vg_surface_create); + +static cairo_surface_t * +_vg_surface_create_internal (cairo_vg_context_t *context, + VGImage image, + VGImageFormat format, + int width, int height); + +static cairo_vg_context_t * +_vg_context_reference (cairo_vg_context_t *context) +{ + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count)); + + _cairo_reference_count_inc (&context->ref_count); + + return context; +} + +static cairo_vg_context_t * +_vg_context_lock (cairo_vg_context_t *context) +{ + /* XXX if we need to add locking, then it has to be recursive */ + return context; +} + +static cairo_int_status_t +_vg_context_set_target (cairo_vg_context_t *context, + cairo_vg_surface_t *surface) +{ + cairo_status_t status; + + if (surface->target_id == 0) { + status = context->create_target (context, surface); + if (unlikely (status)) + return status; + } + + if (context->target_id == surface->target_id) + return CAIRO_STATUS_SUCCESS; + + context->target_id = surface->target_id; + + return context->set_target (context, surface); +} + +static void +_vg_context_destroy_target (cairo_vg_context_t *context, + cairo_vg_surface_t *surface) +{ + if (surface->target_id == 0) + return; + + if (context->target_id == surface->target_id) + context->set_target (context, NULL); + + context->destroy_target (context, surface); +} + +static cairo_bool_t +_vg_snapshot_cache_can_remove (const void *entry) +{ + return TRUE; +} + +static void +_vg_snapshot_cache_remove (void *cache_entry) +{ + cairo_vg_surface_t *surface = cairo_container_of (cache_entry, + cairo_vg_surface_t, + snapshot_cache_entry); + surface->snapshot_cache_entry.hash = 0; + cairo_surface_destroy (&surface->base); +} + +static cairo_status_t +_vg_context_init (cairo_vg_context_t *context) +{ + cairo_status_t status; + + context->status = CAIRO_STATUS_SUCCESS; + CAIRO_REFERENCE_COUNT_INIT (&context->ref_count, 1); + + status = _cairo_cache_init (&context->snapshot_cache, + NULL, + _vg_snapshot_cache_can_remove, + _vg_snapshot_cache_remove, + 16*1024*1024); + if (unlikely (status)) + return status; + + context->target_id = 0; + context->source = NULL; + context->alpha = 1.0; + + context->paint = vgCreatePaint (); + vgLoadIdentity (); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_vg_context_destroy (cairo_vg_context_t *context) +{ + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count)); + + if (! _cairo_reference_count_dec_and_test (&context->ref_count)) + return; + + if (context->paint != VG_INVALID_HANDLE) + vgDestroyPaint (context->paint); + + _cairo_cache_fini (&context->snapshot_cache); + free (context); +} + +static void +_vg_context_unlock (cairo_vg_context_t *context) +{ +} + +#ifdef OPENVG_DEBUG +static void check_vg_errors(const char*function,int line) +{ + int err = vgGetError(); + if (err != VG_NO_ERROR){ + printf("%s+%d:vgError detected: 0x%08x.\n",function, line,err); + assert(err == VG_NO_ERROR); + } + +} +#define CHECK_VG_ERRORS() check_vg_errors(__FILE__,__LINE__) +#else +#define CHECK_VG_ERRORS() do{}while(0) +#endif //OPENVG_DEBUG + +static pixman_format_code_t +_vg_format_to_pixman (VGImageFormat format, + cairo_bool_t *needs_premult_fixup) +{ + *needs_premult_fixup = FALSE; + switch (format) { + /* RGB{A,X} channel ordering */ + case VG_sRGBX_8888: return 0; //PIXMAN_r8g8b8x8; + case VG_sRGBA_8888: return 0; + case VG_sRGBA_8888_PRE: return 0; //PIXMAN_r8b8g8a8; + case VG_sRGB_565: return PIXMAN_r5g6b5; + case VG_sRGBA_5551: return 0; + case VG_sRGBA_4444: return 0; + case VG_sL_8: return PIXMAN_g8; + case VG_lRGBX_8888: return 0; + case VG_lRGBA_8888: return 0; + case VG_lRGBA_8888_PRE: return 0; + case VG_lL_8: return 0; + case VG_A_8: return PIXMAN_a8; + case VG_BW_1: return PIXMAN_a1; + case VG_A_1: return PIXMAN_a1; + case VG_A_4: return PIXMAN_a4; + + /* {A,X}RGB channel ordering */ + case VG_sXRGB_8888: return PIXMAN_x8r8g8b8; + case VG_sARGB_8888: *needs_premult_fixup = TRUE; return PIXMAN_a8r8g8b8; + case VG_sARGB_8888_PRE: return PIXMAN_a8r8g8b8; + case VG_sARGB_1555: return 0; + case VG_sARGB_4444: return 0; + case VG_lXRGB_8888: return 0; + case VG_lARGB_8888: return 0; + case VG_lARGB_8888_PRE: return 0; + + /* BGR{A,X} channel ordering */ + case VG_sBGRX_8888: return PIXMAN_b8g8r8x8; + case VG_sBGRA_8888: *needs_premult_fixup = TRUE; return PIXMAN_b8g8r8a8; + case VG_sBGRA_8888_PRE: return PIXMAN_b8g8r8a8; + case VG_sBGR_565: return PIXMAN_b5g6r5; + case VG_sBGRA_5551: return 0; + case VG_sBGRA_4444: return 0; + case VG_lBGRX_8888: return 0; + case VG_lBGRA_8888: return 0; + case VG_lBGRA_8888_PRE: return 0; + + /* {A,X}BGR channel ordering */ + case VG_sXBGR_8888: return PIXMAN_x8b8g8r8; + case VG_sABGR_8888: *needs_premult_fixup = TRUE; return PIXMAN_a8b8g8r8; + case VG_sABGR_8888_PRE: return PIXMAN_a8b8g8r8; + case VG_sABGR_1555: return 0; + case VG_sABGR_4444: return 0; + case VG_lXBGR_8888: return 0; + case VG_lABGR_8888: return 0; + case VG_lABGR_8888_PRE: return 0; + default: return 0; + } +} + +static pixman_format_code_t +_vg_format_to_content (VGImageFormat format) +{ + /* XXX could use more simple bit tests */ + switch (format) { + /* RGB{A,X} channel ordering */ + case VG_sRGBX_8888: return CAIRO_CONTENT_COLOR; + case VG_sRGBA_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sRGBA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sRGB_565: return CAIRO_CONTENT_COLOR; + case VG_sRGBA_5551: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sRGBA_4444: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sL_8: return CAIRO_CONTENT_ALPHA; + case VG_lRGBX_8888: return CAIRO_CONTENT_COLOR; + case VG_lRGBA_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lRGBA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lL_8: return CAIRO_CONTENT_ALPHA; + case VG_A_8: return CAIRO_CONTENT_ALPHA; + case VG_A_4: return CAIRO_CONTENT_ALPHA; + case VG_A_1: return CAIRO_CONTENT_ALPHA; + case VG_BW_1: return CAIRO_CONTENT_ALPHA; + + /* {A,X}RGB channel ordering */ + case VG_sXRGB_8888: return CAIRO_CONTENT_COLOR; + case VG_sARGB_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sARGB_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sARGB_1555: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sARGB_4444: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lXRGB_8888: return CAIRO_CONTENT_COLOR; + case VG_lARGB_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lARGB_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + + /* BGR{A,X} channel ordering */ + case VG_sBGRX_8888: return CAIRO_CONTENT_COLOR; + case VG_sBGRA_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sBGRA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sBGR_565: return CAIRO_CONTENT_COLOR; + case VG_sBGRA_5551: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sBGRA_4444: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lBGRX_8888: return CAIRO_CONTENT_COLOR; + case VG_lBGRA_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lBGRA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + + /* {A,X}BGR channel ordering */ + case VG_sXBGR_8888: return CAIRO_CONTENT_COLOR; + case VG_sABGR_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sABGR_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sABGR_1555: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sABGR_4444: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lXBGR_8888: return CAIRO_CONTENT_COLOR; + case VG_lABGR_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lABGR_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + default: return 0; + } +} + +static VGImageFormat +_vg_format_from_pixman (pixman_format_code_t format) +{ + /* XXX _PRE needs fixup */ + switch ((int) format) { + case PIXMAN_r5g6b5: return VG_sRGB_565; + case PIXMAN_g8: return VG_sL_8; + case PIXMAN_a8: return VG_A_8; + case PIXMAN_a1: return VG_BW_1; + case PIXMAN_x8r8g8b8: return VG_sXRGB_8888; + case PIXMAN_a8r8g8b8: return VG_sARGB_8888; // _PRE + case PIXMAN_b8g8r8x8: return VG_sBGRX_8888; + case PIXMAN_b8g8r8a8: return VG_sBGRA_8888; // _PRE + case PIXMAN_b5g6r5: return VG_sBGR_565; + case PIXMAN_x8b8g8r8: return VG_sXBGR_8888; + case PIXMAN_a8b8g8r8: return VG_sABGR_8888; // _PRE + default: return 0; + } +} + +static VGImageFormat +_vg_format_for_content (cairo_content_t content) +{ + switch (content) { + case CAIRO_CONTENT_ALPHA: return VG_A_8; + case CAIRO_CONTENT_COLOR: return VG_sXRGB_8888; + default: ASSERT_NOT_REACHED; + case CAIRO_CONTENT_COLOR_ALPHA: return VG_sARGB_8888; // _PRE + } +} + +static cairo_surface_t * +_vg_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_vg_surface_t *surface = abstract_surface; + + if (width > vgGeti (VG_MAX_IMAGE_WIDTH) || + height > vgGeti (VG_MAX_IMAGE_HEIGHT)) + { + return NULL; + } + + return cairo_vg_surface_create (surface->context, content, width, height); +} + +static cairo_status_t +_vg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_vg_surface_t *surface = cairo_container_of (clipper, + cairo_vg_surface_t, + clipper); + cairo_vg_surface_t *mask; + cairo_solid_pattern_t white; + cairo_status_t status; + + if (path == NULL) { + vgMask (VG_INVALID_HANDLE, + VG_FILL_MASK, 0, 0, surface->width, surface->height); + vgSeti (VG_MASKING, VG_FALSE); + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; + } + + mask = (cairo_vg_surface_t *) + _vg_surface_create_similar (surface, CAIRO_CONTENT_ALPHA, + surface->width, surface->height); + if (unlikely (mask == NULL)) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (unlikely (mask->base.status)) + return mask->base.status; + + _cairo_pattern_init_solid (&white, CAIRO_COLOR_WHITE, CAIRO_CONTENT_ALPHA); + status = _cairo_surface_fill (&mask->base, + CAIRO_OPERATOR_SOURCE, &white.base, + path, fill_rule, tolerance, antialias, + NULL); + if (status) { + cairo_surface_destroy (&mask->base); + return status; + } + + vgSeti (VG_MASKING, VG_TRUE); + vgMask (mask->image, VG_INTERSECT_MASK, 0, 0, mask->width, mask->height); + + cairo_surface_destroy (&mask->base); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_vg_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_vg_surface_t *surface = abstract_surface; + + extents->x = 0; + extents->y = 0; + extents->width = surface->width; + extents->height = surface->height; + + return TRUE; +} + +#define MAX_SEG 16 /* max number of knots to upload in a batch */ + +typedef struct _vg_path { + VGPath path; + cairo_matrix_t *ctm_inverse; + + VGubyte gseg[MAX_SEG]; + VGfloat gdata[MAX_SEG*3*2]; + int dcount; + int scount; +} vg_path_t; + +static cairo_status_t +_vg_move_to (void *closure, + const cairo_point_t *point) +{ + vg_path_t *path = closure; + double x = _cairo_fixed_to_double (point->x); + double y = _cairo_fixed_to_double (point->y); + + if (path->ctm_inverse) + cairo_matrix_transform_point (path->ctm_inverse, &x, &y); + + path->gseg[path->scount++] = VG_MOVE_TO; + path->gdata[path->dcount++] = x; + path->gdata[path->dcount++] = y; + + if (path->scount >= MAX_SEG-1) { + vgAppendPathData (path->path, path->scount, path->gseg, path->gdata); + path->scount = 0; + path->dcount = 0; + } + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_vg_line_to (void *closure, + const cairo_point_t *point) +{ + vg_path_t *path = closure; + double x = _cairo_fixed_to_double (point->x); + double y = _cairo_fixed_to_double (point->y); + + if (path->ctm_inverse) + cairo_matrix_transform_point (path->ctm_inverse, &x, &y); + + path->gseg[path->scount++] = VG_LINE_TO; + path->gdata[path->dcount++] = x; + path->gdata[path->dcount++] = y; + + if (path->scount >= MAX_SEG-1) { + vgAppendPathData (path->path, path->scount, path->gseg, path->gdata); + path->scount = 0; + path->dcount = 0; + } + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_vg_curve_to (void *closure, + const cairo_point_t *p0, + const cairo_point_t *p1, + const cairo_point_t *p2) +{ + vg_path_t *path = closure; + double x0 = _cairo_fixed_to_double (p0->x); + double y0 = _cairo_fixed_to_double (p0->y); + double x1 = _cairo_fixed_to_double (p1->x); + double y1 = _cairo_fixed_to_double (p1->y); + double x2 = _cairo_fixed_to_double (p2->x); + double y2 = _cairo_fixed_to_double (p2->y); + + if (path->ctm_inverse) { + cairo_matrix_transform_point (path->ctm_inverse, &x0, &y0); + cairo_matrix_transform_point (path->ctm_inverse, &x1, &y1); + cairo_matrix_transform_point (path->ctm_inverse, &x2, &y2); + } + + path->gseg[path->scount++] = VG_CUBIC_TO; + path->gdata[path->dcount++] = x0; + path->gdata[path->dcount++] = y0; + path->gdata[path->dcount++] = x1; + path->gdata[path->dcount++] = y1; + path->gdata[path->dcount++] = x2; + path->gdata[path->dcount++] = y2; + + if (path->scount >= MAX_SEG-1) { + vgAppendPathData(path->path, path->scount, path->gseg, path->gdata); + path->scount = 0; + path->dcount = 0; + } + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_vg_close_path (void *closure) +{ + vg_path_t *path = closure; + + path->gseg[path->scount++] = VG_CLOSE_PATH; + + if (path->scount >= MAX_SEG-1) { + vgAppendPathData (path->path, path->scount, path->gseg, path->gdata); + path->scount = 0; + path->dcount = 0; + } + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static void +_vg_path_from_cairo (vg_path_t *vg_path, + const cairo_path_fixed_t *path) +{ + cairo_status_t status; + + vg_path->scount = 0; + vg_path->dcount = 0; + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _vg_move_to, + _vg_line_to, + _vg_curve_to, + _vg_close_path, + vg_path); + assert (status == CAIRO_STATUS_SUCCESS); + + vgAppendPathData (vg_path->path, + vg_path->scount, vg_path->gseg, vg_path->gdata); + CHECK_VG_ERRORS(); +} + +static cairo_bool_t +_vg_is_supported_operator (cairo_operator_t op) +{ + switch ((int) op) { + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_ADD: + return TRUE; + + default: + return FALSE; + } +} + +static VGBlendMode +_vg_operator (cairo_operator_t op) +{ + switch ((int) op) { + case CAIRO_OPERATOR_SOURCE: + return VG_BLEND_SRC; + case CAIRO_OPERATOR_OVER: + return VG_BLEND_SRC_OVER; + case CAIRO_OPERATOR_IN: + return VG_BLEND_SRC_IN; + case CAIRO_OPERATOR_DEST_OVER: + return VG_BLEND_DST_OVER; + case CAIRO_OPERATOR_DEST_IN: + return VG_BLEND_DST_IN; + case CAIRO_OPERATOR_ADD: + return VG_BLEND_ADDITIVE; + default: + ASSERT_NOT_REACHED; + return VG_BLEND_SRC_OVER; + } +} + +static VGFillRule +_vg_fill_rule_from_cairo (cairo_fill_rule_t rule) +{ + switch (rule) { + case CAIRO_FILL_RULE_EVEN_ODD: return VG_EVEN_ODD; + case CAIRO_FILL_RULE_WINDING: return VG_NON_ZERO; + } + + ASSERT_NOT_REACHED; + return VG_NON_ZERO; +} + +static VGRenderingQuality +_vg_rendering_quality_from_cairo (cairo_antialias_t aa) +{ + switch (aa) { + case CAIRO_ANTIALIAS_DEFAULT: + case CAIRO_ANTIALIAS_SUBPIXEL: + return VG_RENDERING_QUALITY_BETTER; + + case CAIRO_ANTIALIAS_GRAY: + return VG_RENDERING_QUALITY_FASTER; + + case CAIRO_ANTIALIAS_NONE: + return VG_RENDERING_QUALITY_NONANTIALIASED; + } + + ASSERT_NOT_REACHED; + return VG_RENDERING_QUALITY_BETTER; +} + +static VGCapStyle +_vg_line_cap_from_cairo (cairo_line_cap_t cap) +{ + switch (cap) { + case CAIRO_LINE_CAP_BUTT: return VG_CAP_BUTT; + case CAIRO_LINE_CAP_ROUND: return VG_CAP_ROUND; + case CAIRO_LINE_CAP_SQUARE: return VG_CAP_SQUARE; + } + + ASSERT_NOT_REACHED; + return VG_CAP_BUTT; +} + +static VGJoinStyle +_vg_line_join_from_cairo (cairo_line_join_t join) +{ + switch (join) { + case CAIRO_LINE_JOIN_MITER: return VG_JOIN_MITER; + case CAIRO_LINE_JOIN_ROUND: return VG_JOIN_ROUND; + case CAIRO_LINE_JOIN_BEVEL: return VG_JOIN_BEVEL; + } + + ASSERT_NOT_REACHED; + return VG_JOIN_MITER; +} + +static void +_vg_matrix_from_cairo (VGfloat *dst, const cairo_matrix_t *src) +{ + dst[0] = /* sx */ src->xx; + dst[1] = /* shy */ src->yx; + dst[2] = /* w0 */ 0; + dst[3] = /* shx */ src->xy; + dst[4] = /* sy */ src->yy; + dst[5] = /* w1 */ 0; + dst[6] = /* tx */ src->x0; + dst[7] = /* ty */ src->y0; + dst[8] = /* w2 */ 0; +} + +static cairo_status_t +_vg_setup_gradient_stops (cairo_vg_context_t *context, + const cairo_gradient_pattern_t *pattern) +{ + VGint numstops = pattern->n_stops; + VGfloat *stops, stack_stops[CAIRO_STACK_ARRAY_LENGTH (VGfloat)]; + int i; + + if (numstops*5 < ARRAY_LENGTH (stack_stops)) { + stops = stack_stops; + } else { + stops = _cairo_malloc_ab (numstops, 5*sizeof (VGfloat)); + if (unlikely (stops == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + for (i = 0; i < numstops; i++) { + stops[i*5 + 0] = pattern->stops[i].offset; + stops[i*5 + 1] = pattern->stops[i].color.red; + stops[i*5 + 2] = pattern->stops[i].color.green; + stops[i*5 + 3] = pattern->stops[i].color.blue; + stops[i*5 + 4] = pattern->stops[i].color.alpha * context->alpha; + } + + vgSetParameterfv (context->paint, + VG_PAINT_COLOR_RAMP_STOPS, numstops * 5, stops); + + if (stops != stack_stops) + free (stops); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static void +_vg_set_source_matrix (const cairo_pattern_t *pat) +{ + cairo_matrix_t mat; + cairo_status_t status; + VGfloat vmat[9]; + + mat = pat->matrix; + status = cairo_matrix_invert (&mat); + assert (status == CAIRO_STATUS_SUCCESS); + + _vg_matrix_from_cairo (vmat, &mat); + + vgSeti (VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER); + vgLoadMatrix (vmat); + vgSeti (VG_MATRIX_MODE, VG_MATRIX_STROKE_PAINT_TO_USER); + vgLoadMatrix (vmat); + vgSeti (VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); + + CHECK_VG_ERRORS(); +} + +static cairo_status_t +_vg_setup_linear_source (cairo_vg_context_t *context, + const cairo_linear_pattern_t *lpat) +{ + VGfloat linear[4]; + + linear[0] = _cairo_fixed_to_double (lpat->p1.x); + linear[1] = _cairo_fixed_to_double (lpat->p1.y); + linear[2] = _cairo_fixed_to_double (lpat->p2.x); + linear[3] = _cairo_fixed_to_double (lpat->p2.y); + + vgSetParameteri (context->paint, + VG_PAINT_COLOR_RAMP_SPREAD_MODE, + VG_COLOR_RAMP_SPREAD_PAD); + vgSetParameteri (context->paint, + VG_PAINT_TYPE, + VG_PAINT_TYPE_LINEAR_GRADIENT); + vgSetParameterfv (context->paint, + VG_PAINT_LINEAR_GRADIENT, 4, linear); + + _vg_set_source_matrix (&lpat->base.base); + + CHECK_VG_ERRORS(); + return _vg_setup_gradient_stops (context, &lpat->base); + +} + +static cairo_status_t +_vg_setup_radial_source (cairo_vg_context_t *context, + const cairo_radial_pattern_t *rpat) +{ + VGfloat radial[5]; + + radial[0] = _cairo_fixed_to_double (rpat->c1.x); + radial[1] = _cairo_fixed_to_double (rpat->c1.y); + radial[2] = _cairo_fixed_to_double (rpat->c2.x); + radial[3] = _cairo_fixed_to_double (rpat->c2.y); + radial[4] = _cairo_fixed_to_double (rpat->r2); + + vgSetParameteri (context->paint, + VG_PAINT_COLOR_RAMP_SPREAD_MODE, VG_COLOR_RAMP_SPREAD_PAD); + vgSetParameteri (context->paint, + VG_PAINT_TYPE, VG_PAINT_TYPE_RADIAL_GRADIENT); + vgSetParameterfv (context->paint, + VG_PAINT_RADIAL_GRADIENT, 5, radial); + + _vg_set_source_matrix (&rpat->base.base); + + /* FIXME: copy/adapt fixes from SVG backend to add inner radius */ + + CHECK_VG_ERRORS(); + return _vg_setup_gradient_stops (context, &rpat->base); +} + +static cairo_status_t +_vg_setup_solid_source (cairo_vg_context_t *context, + const cairo_solid_pattern_t *spat) +{ + VGfloat color[] = { + spat->color.red, + spat->color.green, + spat->color.blue, + spat->color.alpha * context->alpha + }; + + vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); + vgSetParameterfv (context->paint, VG_PAINT_COLOR, 4, color); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_vg_surface_t * +_vg_clone_recording_surface (cairo_vg_context_t *context, + cairo_surface_t *surface) +{ + VGImage vg_image; + VGImageFormat format; + cairo_status_t status; + cairo_rectangle_int_t extents; + cairo_vg_surface_t *clone; + + status = _cairo_surface_get_extents (surface, &extents); + if (status) + return NULL; + + if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) || + extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT)) + { + return NULL; + } + + format = _vg_format_for_content (surface->content); + + /* NONALIASED, FASTER, BETTER */ + vg_image = vgCreateImage (format, + extents.width, extents.height, + VG_IMAGE_QUALITY_FASTER); + clone = (cairo_vg_surface_t *) + _vg_surface_create_internal (context, vg_image, format, + extents.width, extents.height); + cairo_surface_set_device_offset (&clone->base, -extents.x, -extents.y); + + status = _cairo_recording_surface_replay (surface, &clone->base); + if (unlikely (status)) { + cairo_surface_destroy (&clone->base); + return (cairo_vg_surface_t *) _cairo_surface_create_in_error (status); + } + + return clone; +} + +static cairo_vg_surface_t * +_vg_clone_image_surface (cairo_vg_context_t *context, + cairo_surface_t *surface) +{ + cairo_image_surface_t *image; + void *image_extra; + cairo_status_t status; + VGImage vg_image; + VGImageFormat format; + cairo_rectangle_int_t extents; + cairo_vg_surface_t *clone; + + if (surface->backend->acquire_source_image == NULL) + return NULL; + + status = _cairo_surface_get_extents (surface, &extents); + if (status) + return NULL; + + if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) || + extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT)) + { + return NULL; + } + + status = _cairo_surface_acquire_source_image (surface, + &image, &image_extra); + if (unlikely (status)) + return (cairo_vg_surface_t *) _cairo_surface_create_in_error (status); + + format = _vg_format_from_pixman (image->pixman_format); + if (format == 0) + format = _vg_format_for_content (image->base.content); + + /* NONALIASED, FASTER, BETTER */ + vg_image = vgCreateImage (format, + image->width, image->height, + VG_IMAGE_QUALITY_FASTER); + clone = (cairo_vg_surface_t *) + _vg_surface_create_internal (context, vg_image, format, + image->width, image->height); + if (unlikely (clone->base.status)) + return clone; + + vgImageSubData (clone->image, + image->data, image->stride, + format, 0, 0, image->width, image->height); + + _cairo_surface_release_source_image (surface, image, image_extra); + + return clone; +} + +static void +_vg_surface_remove_from_cache (cairo_surface_t *abstract_surface) +{ + cairo_vg_surface_t *surface = (cairo_vg_surface_t *) abstract_surface; + + if (surface->snapshot_cache_entry.hash) { + cairo_vg_context_t *context; + + context = _vg_context_lock (surface->context); + _cairo_cache_remove (&context->snapshot_cache, + &surface->snapshot_cache_entry); + _vg_context_unlock (context); + + surface->snapshot_cache_entry.hash = 0; + } +} + +static cairo_status_t +_vg_setup_surface_source (cairo_vg_context_t *context, + const cairo_surface_pattern_t *spat) +{ + cairo_surface_t *snapshot; + cairo_vg_surface_t *clone; + cairo_status_t status; + + snapshot = _cairo_surface_has_snapshot (spat->surface, + &cairo_vg_surface_backend, + spat->surface->content); + if (snapshot != NULL) { + clone = (cairo_vg_surface_t *) cairo_surface_reference (snapshot); + goto DONE; + } + + if (_cairo_surface_is_recording (spat->surface)) + clone = _vg_clone_recording_surface (context, spat->surface); + else + clone = _vg_clone_image_surface (context, spat->surface); + if (clone == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (unlikely (clone->base.status)) + return clone->base.status; + + clone->snapshot_cache_entry.hash = clone->base.unique_id; + status = _cairo_cache_insert (&context->snapshot_cache, + &clone->snapshot_cache_entry); + if (unlikely (status)) { + clone->snapshot_cache_entry.hash = 0; + cairo_surface_destroy (&clone->base); + return status; + } + + status = _cairo_surface_attach_snapshot (spat->surface, &clone->base, + _vg_surface_remove_from_cache); + if (unlikely (status)) { + cairo_surface_destroy (&clone->base); + return status; + } + +DONE: + cairo_surface_destroy (&context->source->base); + context->source = clone; + + vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN); + + switch (spat->base.extend) { + case CAIRO_EXTEND_PAD: + vgSetParameteri (context->paint, + VG_PAINT_PATTERN_TILING_MODE, + VG_TILE_PAD); + break; + + case CAIRO_EXTEND_NONE: + vgSetParameteri (context->paint, + VG_PAINT_PATTERN_TILING_MODE, + VG_TILE_FILL); + { + VGfloat color[] = {0,0,0,0}; + vgSetfv (VG_TILE_FILL_COLOR, 4, color); + } + break; + + case CAIRO_EXTEND_REPEAT: + vgSetParameteri (context->paint, + VG_PAINT_PATTERN_TILING_MODE, + VG_TILE_REPEAT); + break; + + default: + ASSERT_NOT_REACHED; + case CAIRO_EXTEND_REFLECT: + vgSetParameteri (context->paint, + VG_PAINT_PATTERN_TILING_MODE, + VG_TILE_REFLECT); + break; + } + vgPaintPattern (context->paint, context->source->image); + + _vg_set_source_matrix (&spat->base); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +setup_source (cairo_vg_context_t *context, + const cairo_pattern_t *source) +{ + switch (source->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return _vg_setup_solid_source (context, + (cairo_solid_pattern_t *) source); + case CAIRO_PATTERN_TYPE_LINEAR: + return _vg_setup_linear_source (context, + (cairo_linear_pattern_t *) source); + case CAIRO_PATTERN_TYPE_RADIAL: + return _vg_setup_radial_source (context, + (cairo_radial_pattern_t *) source); + case CAIRO_PATTERN_TYPE_SURFACE: + return _vg_setup_surface_source (context, + (cairo_surface_pattern_t *) source); + default: + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_UNSUPPORTED; + } +} + +static cairo_int_status_t +_vg_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t*source, + cairo_path_fixed_t *path, + cairo_stroke_style_t *style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_vg_surface_t *surface = abstract_surface; + cairo_vg_context_t *context; + cairo_status_t status; + VGfloat state[9]; + VGfloat strokeTransform[9]; + vg_path_t vg_path; + + if (! _vg_is_supported_operator (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + context = _vg_context_lock (surface->context); + status = _vg_context_set_target (context, surface); + if (status) { + _vg_context_unlock (context); + return status; + } + + status = setup_source (context, source); + if (status) { + _vg_context_unlock (context); + return status; + } + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) { + _vg_context_unlock (context); + return status; + } + + vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD, + VG_PATH_DATATYPE_F, + 1, 0, 0, 0, + VG_PATH_CAPABILITY_ALL); + + vgGetMatrix (state); + _vg_matrix_from_cairo (strokeTransform, ctm); + vgMultMatrix (strokeTransform); + + vg_path.ctm_inverse = ctm_inverse; + + _vg_path_from_cairo (&vg_path, path); + + /* XXX DASH_PATTERN, DASH_PHASE */ + vgSetf (VG_STROKE_LINE_WIDTH, style->line_width); + vgSetf (VG_STROKE_MITER_LIMIT, style->miter_limit); + vgSetf (VG_STROKE_JOIN_STYLE, _vg_line_join_from_cairo (style->line_join)); + vgSetf (VG_STROKE_CAP_STYLE, _vg_line_cap_from_cairo (style->line_cap)); + + vgSeti (VG_BLEND_MODE, _vg_operator (op)); + + vgSetPaint (context->paint, VG_STROKE_PATH); + + vgDrawPath (vg_path.path, VG_STROKE_PATH); + + vgDestroyPath (vg_path.path); + + vgLoadMatrix (state); + + CHECK_VG_ERRORS(); + _vg_context_unlock (context); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_vg_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_vg_surface_t *surface = abstract_surface; + cairo_vg_context_t *context; + cairo_status_t status; + vg_path_t vg_path; + + if (op == CAIRO_OPERATOR_DEST) + return CAIRO_STATUS_SUCCESS; + + if (! _vg_is_supported_operator (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + context = _vg_context_lock (surface->context); + status = _vg_context_set_target (context, surface); + if (status) { + _vg_context_unlock (context); + return status; + } + + status = setup_source (context, source); + if (status) { + _vg_context_unlock (context); + return status; + } + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) { + _vg_context_unlock (context); + return status; + } + + vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD, + VG_PATH_DATATYPE_F, + 1, 0, + 0, 0, + VG_PATH_CAPABILITY_ALL); + vg_path.ctm_inverse = NULL; + + _vg_path_from_cairo (&vg_path, path); + + /* XXX tolerance */ + + vgSeti (VG_BLEND_MODE, _vg_operator (op)); + vgSetf (VG_FILL_RULE, _vg_fill_rule_from_cairo (fill_rule)); + vgSetf (VG_RENDERING_QUALITY, _vg_rendering_quality_from_cairo (antialias)); + + vgSetPaint (context->paint, VG_FILL_PATH); + + vgDrawPath (vg_path.path, VG_FILL_PATH); + + vgDestroyPath (vg_path.path); + + _vg_context_unlock (context); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_vg_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_vg_surface_t *surface = abstract_surface; + cairo_vg_context_t *context; + cairo_status_t status; + + if (op == CAIRO_OPERATOR_DEST) + return CAIRO_STATUS_SUCCESS; + + if (! _vg_is_supported_operator (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + context = _vg_context_lock (surface->context); + status = _vg_context_set_target (context, surface); + if (status) { + _vg_context_unlock (context); + return status; + } + + status = setup_source (context, source); + if (status) { + _vg_context_unlock (context); + return status; + } + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) { + _vg_context_unlock (context); + return status; + } + + vgSeti (VG_BLEND_MODE, _vg_operator (op)); + vgSetPaint (context->paint, VG_FILL_PATH); + + { /* creating a rectangular path that should cover the extent */ + VGubyte segs[] = { + VG_MOVE_TO_ABS, VG_LINE_TO_ABS, + VG_LINE_TO_ABS, VG_LINE_TO_ABS, + VG_CLOSE_PATH + }; + VGfloat data[] = { + 0, 0, + surface->width, 0, + surface->width, surface->height, + 0, surface->height + }; + VGPath fullext; + + fullext = vgCreatePath (VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, + 1,0,0,0, VG_PATH_CAPABILITY_ALL); + vgAppendPathData (fullext, sizeof(segs), segs, data); + + vgDrawPath (fullext, VG_FILL_PATH); + + vgDestroyPath (fullext); + } + + _vg_context_unlock (context); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_vg_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_vg_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (! _vg_is_supported_operator (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Handle paint-with-alpha to do fades cheaply */ + if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) mask; + cairo_vg_context_t *context = _vg_context_lock (surface->context); + double alpha = context->alpha; + + context->alpha = solid->color.alpha; + status = _vg_surface_paint (abstract_surface, op, source, clip); + context->alpha = alpha; + + _vg_context_unlock (context); + + return status; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static void +_vg_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + _cairo_font_options_init_default (options); + + cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); +} + +static cairo_int_status_t +_vg_surface_show_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_path_fixed_t path; + + if (num_glyphs <= 0) + return CAIRO_STATUS_SUCCESS; + + _cairo_path_fixed_init (&path); + + /* XXX Glyph cache! OpenVG font support in 1.1? */ + + status = _cairo_scaled_font_glyph_path (scaled_font, + glyphs, num_glyphs, + &path); + if (unlikely (status)) + goto BAIL; + + status = _vg_surface_fill (abstract_surface, + op, source, &path, + CAIRO_FILL_RULE_WINDING, + CAIRO_GSTATE_TOLERANCE_DEFAULT, + CAIRO_ANTIALIAS_SUBPIXEL, + clip); +BAIL: + _cairo_path_fixed_fini (&path); + return status; +} + +static inline int +multiply_alpha (int alpha, int color) +{ + int temp = alpha * color + 0x80; + return (temp + (temp >> 8)) >> 8; +} + +static void +premultiply_argb (uint8_t *data, + int width, + int height, + int stride) +{ + int i; + + while (height --) { + uint32_t *row = (uint32_t *) data; + + for (i = 0; i < width; i++) { + uint32_t p = row[i]; + uint8_t alpha; + + alpha = p >> 24; + if (alpha == 0) { + row[i] = 0; + } else if (alpha != 0xff) { + uint8_t r = multiply_alpha (alpha, (p >> 16) & 0xff); + uint8_t g = multiply_alpha (alpha, (p >> 8) & 0xff); + uint8_t b = multiply_alpha (alpha, (p >> 0) & 0xff); + row[i] = (alpha << 24) | (r << 16) | (g << 8) | (b << 0); + } + } + + data += stride; + } +} + +static cairo_int_status_t +_vg_get_image (cairo_vg_surface_t *surface, + int x, int y, + int width, int height, + cairo_image_surface_t **image_out) +{ + cairo_image_surface_t *image; + pixman_image_t *pixman_image; + pixman_format_code_t pixman_format; + cairo_bool_t needs_premultiply; + + pixman_format = _vg_format_to_pixman (surface->format, + &needs_premultiply); + if (pixman_format == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + pixman_image = pixman_image_create_bits (pixman_format, + width, height, + NULL, 0); + if (unlikely (pixman_image == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + vgFinish (); + CHECK_VG_ERRORS(); + + vgGetImageSubData (surface->image, + pixman_image_get_data (pixman_image), + pixman_image_get_stride (pixman_image), + surface->format, + x, y, width, height); + + image = (cairo_image_surface_t *) + _cairo_image_surface_create_for_pixman_image (pixman_image, + pixman_format); + if (unlikely (image->base.status)) { + pixman_image_unref (pixman_image); + return image->base.status; + } + + if (needs_premultiply) + premultiply_argb (image->data, width, height, image->stride); + + *image_out = image; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_vg_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_vg_surface_t *surface = abstract_surface; + + CHECK_VG_ERRORS(); + *image_extra = NULL; + return _vg_get_image (surface, + 0, 0, surface->width, surface->height, + image_out); +} + +static void +_vg_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_vg_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect_out, + void **image_extra) +{ + cairo_vg_surface_t *surface = abstract_surface; + + *image_rect_out = *interest_rect; + *image_extra = NULL; + return _vg_get_image (surface, + interest_rect->x, interest_rect->y, + interest_rect->width, interest_rect->height, + image_out); +} + +static void +unpremultiply_argb (uint8_t *data, + int width, + int height, + int stride) +{ + int i; + + while (height--) { + uint32_t *row = (uint32_t *) data; + + for (i = 0; i < width; i ++) { + uint32_t p = row[i]; + uint8_t alpha; + + alpha = p >> 24; + if (alpha == 0) { + row[i] = 0; + } else if (alpha != 0xff) { + uint8_t r = (((p >> 16) & 0xff) * 255 + alpha / 2) / alpha; + uint8_t g = (((p >> 8) & 0xff) * 255 + alpha / 2) / alpha; + uint8_t b = (((p >> 0) & 0xff) * 255 + alpha / 2) / alpha; + row[i] = (alpha << 24) | (r << 16) | (g << 8) | (b << 0); + } + } + + data += stride; + } +} + +static void +_vg_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int_t *image_rect, + void *image_extra) +{ + cairo_vg_surface_t *surface = abstract_surface; + cairo_bool_t needs_unpremultiply; + + _vg_format_to_pixman (surface->format, &needs_unpremultiply); + if (needs_unpremultiply) { + unpremultiply_argb (image->data, + image->width, image->height, + image->stride); + } + + vgImageSubData (surface->image, + image->data, image->stride, + surface->format, + image_rect->x, image_rect->y, + image_rect->width, image_rect->height); + + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_vg_surface_finish (void *abstract_surface) +{ + cairo_vg_surface_t *surface = abstract_surface; + cairo_vg_context_t *context = _vg_context_lock (surface->context); + + if (surface->snapshot_cache_entry.hash) { + _cairo_cache_remove (&context->snapshot_cache, + &surface->snapshot_cache_entry); + + surface->snapshot_cache_entry.hash = 0; + } + + _cairo_surface_clipper_reset (&surface->clipper); + + if (surface->own_image) + vgDestroyImage (surface->image); + + _vg_context_destroy_target (context, surface); + + _vg_context_unlock (context); + _vg_context_destroy (context); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t cairo_vg_surface_backend = { + CAIRO_SURFACE_TYPE_VG, + _vg_surface_create_similar, + _vg_surface_finish, + + _vg_surface_acquire_source_image, + _vg_surface_release_source_image, + _vg_surface_acquire_dest_image, + _vg_surface_release_dest_image, + + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + + NULL, /* copy_page */ + NULL, /* show_page */ + _vg_surface_get_extents, + NULL, /* old_show_glyphs */ + _vg_surface_get_font_options, /* get_font_options */ + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + + _vg_surface_paint, + _vg_surface_mask, + _vg_surface_stroke, + _vg_surface_fill, + _vg_surface_show_glyphs, + + NULL, /* snapshot */ + NULL, /* is_similar */ +}; + +static cairo_surface_t * +_vg_surface_create_internal (cairo_vg_context_t *context, + VGImage image, + VGImageFormat format, + int width, int height) +{ + cairo_vg_surface_t *surface; + + surface = malloc (sizeof (cairo_vg_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface->context = _vg_context_reference (context); + + surface->image = image; + surface->format = format; + + _cairo_surface_init (&surface->base, + &cairo_vg_surface_backend, + _vg_format_to_content (format)); + + surface->width = width; + surface->height = height; + + _cairo_surface_clipper_init (&surface->clipper, + _vg_surface_clipper_intersect_clip_path); + + surface->snapshot_cache_entry.hash = 0; + + surface->target_id = 0; + + CHECK_VG_ERRORS(); + return &surface->base; +} + +cairo_surface_t * +cairo_vg_surface_create_for_image (cairo_vg_context_t *context, + VGImage image, + VGImageFormat format, + int width, int height) +{ + cairo_bool_t premult; + + if (context->status) + return _cairo_surface_create_in_error (context->status); + + if (image == VG_INVALID_HANDLE) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + if (_vg_format_to_pixman (format, &premult) == 0) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + + return _vg_surface_create_internal (context, image, format, width, height); +} + +cairo_surface_t * +cairo_vg_surface_create (cairo_vg_context_t *context, + cairo_content_t content, + int width, + int height) +{ + VGImage image; + VGImageFormat format; + cairo_surface_t *surface; + + if (context->status) + return _cairo_surface_create_in_error (context->status); + + if (! CAIRO_CONTENT_VALID (content)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); + + if (width > vgGeti (VG_MAX_IMAGE_WIDTH) || + height > vgGeti (VG_MAX_IMAGE_HEIGHT)) + { + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + } + + + format = _vg_format_for_content (content); + image = vgCreateImage (format, width, height, VG_IMAGE_QUALITY_BETTER); + if (image == VG_INVALID_HANDLE) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface = _vg_surface_create_internal (context, + image, format, width, height); + if (unlikely (surface->status)) + return surface; + + ((cairo_vg_surface_t *) surface)->own_image = TRUE; + return surface; +} +slim_hidden_def (cairo_vg_surface_create); + +VGImage +cairo_vg_surface_get_image (cairo_surface_t *abstract_surface) +{ + cairo_vg_surface_t *surface; + + if (abstract_surface->backend != &cairo_vg_surface_backend) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return VG_INVALID_HANDLE; + } + + surface = (cairo_vg_surface_t *) abstract_surface; + return surface->image; +} + +int +cairo_vg_surface_get_width (cairo_surface_t *abstract_surface) +{ + cairo_vg_surface_t *surface; + + if (abstract_surface->backend != &cairo_vg_surface_backend) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + surface = (cairo_vg_surface_t *) abstract_surface; + return surface->width; +} + +int +cairo_vg_surface_get_height (cairo_surface_t *abstract_surface) +{ + cairo_vg_surface_t *surface; + + if (abstract_surface->backend != &cairo_vg_surface_backend) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + surface = (cairo_vg_surface_t *) abstract_surface; + return surface->height; +} + +VGImageFormat +cairo_vg_surface_get_format (cairo_surface_t *abstract_surface) +{ + cairo_vg_surface_t *surface; + + if (abstract_surface->backend != &cairo_vg_surface_backend) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + surface = (cairo_vg_surface_t *) abstract_surface; + return surface->format; +} + +/* GL specific context support :-( + * + * OpenVG like cairo defers creation of surface (and the necessary + * paraphernalia to the application. + */ + +static const cairo_vg_context_t _vg_context_nil = { + CAIRO_STATUS_NO_MEMORY, + CAIRO_REFERENCE_COUNT_INVALID +}; + +static const cairo_vg_context_t _vg_context_nil_invalid_visual = { + CAIRO_STATUS_INVALID_VISUAL, + CAIRO_REFERENCE_COUNT_INVALID +}; + +#if CAIRO_HAS_GLX_FUNCTIONS +#include + +static cairo_status_t +glx_create_target (cairo_vg_context_t *context, + cairo_vg_surface_t *surface) +{ + /* XXX hmm, magic required for creating an FBO points to VGImage! */ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_status_t +glx_set_target (cairo_vg_context_t *context, + cairo_vg_surface_t *surface) +{ +#if 0 + glXMakeContextCurrent (context->display, + (GLXDrawable) surface->target_id, + (GLXDrawable) surface->target_id, + context->context); +#else + return CAIRO_INT_STATUS_UNSUPPORTED; +#endif +} + +static void +glx_destroy_target (cairo_vg_context_t *context, + cairo_vg_surface_t *surface) +{ +} + +cairo_vg_context_t * +cairo_vg_context_create_for_glx (Display *dpy, GLXContext ctx) +{ + cairo_vg_context_t *context; + cairo_status_t status; + + context = malloc (sizeof (*context)); + if (unlikely (context == NULL)) + return (cairo_vg_context_t *) &_vg_context_nil; + + context->display = dpy; + context->context = ctx; + + context->create_target = glx_create_target; + context->set_target = glx_set_target; + context->destroy_target = glx_destroy_target; + + status = _vg_context_init (context); + if (unlikely (status)) { + free (context); + return (cairo_vg_context_t *) &_vg_context_nil; + } + + return context; +} +#endif + +#if CAIRO_HAS_EGL_FUNCTIONS +static cairo_status_t +egl_create_target (cairo_vg_context_t *context, + cairo_vg_surface_t *surface) +{ + EGLSurface *egl_surface; +#define RED 1 +#define GREEN 3 +#define BLUE 5 +#define ALPHA 7 + int attribs[] = { + EGL_RED_SIZE, 0, + EGL_GREEN_SIZE, 0, + EGL_BLUE_SIZE, 0, + EGL_ALPHA_SIZE, 0, + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT, + EGL_NONE + }; + pixman_format_code_t pixman_format; + EGLConfig config; + int num_configs = 0; + cairo_bool_t needs_premultiply; + + pixman_format = _vg_format_to_pixman (surface->format, &needs_premultiply); + if (pixman_format == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* XXX no control over pixel ordering! */ + attribs[RED] = PIXMAN_FORMAT_R (pixman_format); + attribs[GREEN] = PIXMAN_FORMAT_G (pixman_format); + attribs[BLUE] = PIXMAN_FORMAT_B (pixman_format); + attribs[ALPHA] = PIXMAN_FORMAT_A (pixman_format); + + if (! eglChooseConfig (context->display, + attribs, + &config, 1, &num_configs) || + num_configs != 1) + { + fprintf(stderr, "Error: eglChooseConfig() failed.\n"); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + egl_surface = + eglCreatePbufferFromClientBuffer (context->display, + EGL_OPENVG_IMAGE, + (EGLClientBuffer) surface->image, + config, + NULL); + surface->target_id = (unsigned long) egl_surface; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +egl_set_target (cairo_vg_context_t *context, + cairo_vg_surface_t *surface) +{ + if (! eglMakeCurrent (context->display, + (EGLSurface *) surface->target_id, + (EGLSurface *) surface->target_id, + context->context)) + { + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +egl_destroy_target (cairo_vg_context_t *context, + cairo_vg_surface_t *surface) +{ + eglDestroySurface (context->display, + (EGLSurface *) surface->target_id); +} + +cairo_vg_context_t * +cairo_vg_context_create_for_egl (EGLDisplay egl_display, + EGLContext egl_context) +{ + cairo_vg_context_t *context; + cairo_status_t status; + + context = malloc (sizeof (*context)); + if (unlikely (context == NULL)) + return (cairo_vg_context_t *) &_vg_context_nil; + + status = _vg_context_init (context); + if (unlikely (status)) { + free (context); + return (cairo_vg_context_t *) &_vg_context_nil; + } + + context->display = egl_display; + context->context = egl_context; + + context->create_target = egl_create_target; + context->set_target = egl_set_target; + context->destroy_target = egl_destroy_target; + + return context; +} +#endif + +cairo_status_t +cairo_vg_context_status (cairo_vg_context_t *context) +{ + return context->status; +} + +void +cairo_vg_context_destroy (cairo_vg_context_t *context) +{ + if (context == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&context->ref_count)) + return; + + _vg_context_destroy (context); +} diff --git a/gfx/cairo/cairo/src/cairo-vg.h b/gfx/cairo/cairo/src/cairo-vg.h new file mode 100644 index 000000000000..98392fc2ddb2 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-vg.h @@ -0,0 +1,103 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2007 * Mozilla Corporation + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Mozilla Corporation. + * + * Contributor(s): + * Vladimir Vukicevic + * Chris Wilson + */ + +#ifndef CAIRO_VG_H +#define CAIRO_VG_H + +#include "cairo.h" + +#if CAIRO_HAS_VG_SURFACE + +#include + +CAIRO_BEGIN_DECLS + +typedef struct _cairo_vg_context cairo_vg_context_t; + +#if CAIRO_HAS_GLX_FUNCTIONS +typedef struct __GLXcontextRec *GLXContext; +typedef struct _XDisplay Display; + +cairo_public cairo_vg_context_t * +cairo_vg_context_create_for_glx (Display *dpy, + GLXContext ctx); +#endif + +#if CAIRO_HAS_EGL_FUNCTIONS +#include + +cairo_public cairo_vg_context_t * +cairo_vg_context_create_for_egl (EGLDisplay egl_display, + EGLContext egl_context); +#endif + +cairo_public cairo_status_t +cairo_vg_context_status (cairo_vg_context_t *context); + +cairo_public void +cairo_vg_context_destroy (cairo_vg_context_t *context); + +cairo_public cairo_surface_t * +cairo_vg_surface_create (cairo_vg_context_t *context, + cairo_content_t content, int width, int height); + +cairo_public cairo_surface_t * +cairo_vg_surface_create_for_image (cairo_vg_context_t *context, + VGImage image, + VGImageFormat format, + int width, int height); + +cairo_public VGImage +cairo_vg_surface_get_image (cairo_surface_t *abstract_surface); + +cairo_public VGImageFormat +cairo_vg_surface_get_format (cairo_surface_t *abstract_surface); + +cairo_public int +cairo_vg_surface_get_height (cairo_surface_t *abstract_surface); + +cairo_public int +cairo_vg_surface_get_width (cairo_surface_t *abstract_surface); + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_VG_SURFACE*/ +# error Cairo was not compiled with support for the OpenVG backend +#endif /* CAIRO_HAS_VG_SURFACE*/ + +#endif /* CAIRO_VG_H */ diff --git a/gfx/cairo/cairo/src/cairo-wideint-private.h b/gfx/cairo/cairo/src/cairo-wideint-private.h index f5aac284818f..d1322e342154 100644 --- a/gfx/cairo/cairo/src/cairo-wideint-private.h +++ b/gfx/cairo/cairo/src/cairo-wideint-private.h @@ -51,6 +51,9 @@ #if !HAVE_UINT64_T +cairo_uquorem64_t I +_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den); + cairo_uint64_t I _cairo_uint32_to_uint64 (uint32_t i); #define _cairo_uint64_to_uint32(a) ((a).lo) cairo_uint64_t I _cairo_uint64_add (cairo_uint64_t a, cairo_uint64_t b); @@ -90,6 +93,16 @@ int I _cairo_int64_cmp (cairo_int64_t a, cairo_int64_t b); #else +static inline cairo_uquorem64_t +_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den) +{ + cairo_uquorem64_t qr; + + qr.quo = num / den; + qr.rem = num % den; + return qr; +} + #define _cairo_uint32_to_uint64(i) ((uint64_t) (i)) #define _cairo_uint64_to_uint32(i) ((uint32_t) (i)) #define _cairo_uint64_add(a,b) ((a) + (b)) @@ -147,11 +160,40 @@ int I _cairo_int64_cmp (cairo_int64_t a, cairo_int64_t b); * a function which returns both for the 'native' type as well */ -cairo_uquorem64_t I -_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den); +static inline cairo_quorem64_t +_cairo_int64_divrem (cairo_int64_t num, cairo_int64_t den) +{ + int num_neg = _cairo_int64_negative (num); + int den_neg = _cairo_int64_negative (den); + cairo_uquorem64_t uqr; + cairo_quorem64_t qr; -cairo_quorem64_t I -_cairo_int64_divrem (cairo_int64_t num, cairo_int64_t den); + if (num_neg) + num = _cairo_int64_negate (num); + if (den_neg) + den = _cairo_int64_negate (den); + uqr = _cairo_uint64_divrem (num, den); + if (num_neg) + qr.rem = _cairo_int64_negate (uqr.rem); + else + qr.rem = uqr.rem; + if (num_neg != den_neg) + qr.quo = (cairo_int64_t) _cairo_int64_negate (uqr.quo); + else + qr.quo = (cairo_int64_t) uqr.quo; + return qr; +} + +static inline int32_t +_cairo_int64_32_div (cairo_int64_t num, int32_t den) +{ +#if !HAVE_UINT64_T + return _cairo_int64_to_int32 + (_cairo_int64_divrem (num, _cairo_int32_to_int64 (den)).quo); +#else + return num / den; +#endif +} /* * 128-bit datatypes. Again, provide two implementations in @@ -235,6 +277,7 @@ int I _cairo_int128_cmp (cairo_int128_t a, cairo_int128_t b); #define _cairo_int128_sub(a,b) ((a) - (b)) #define _cairo_int128_mul(a,b) ((a) * (b)) #define _cairo_int64x64_128_mul(a,b) ((int128_t) (a) * (b)) +#define _cairo_int64x32_128_mul(a, b) _cairo_int64x64_128_mul(a, _cairo_int32_to_int64(b)) #define _cairo_int128_lt(a,b) ((a) < (b)) #define _cairo_int128_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1) #define _cairo_int128_is_zero(a) ((a) == 0) diff --git a/gfx/cairo/cairo/src/cairo-wideint-type-private.h b/gfx/cairo/cairo/src/cairo-wideint-type-private.h index 3d241502a338..c7363c02969b 100644 --- a/gfx/cairo/cairo/src/cairo-wideint-type-private.h +++ b/gfx/cairo/cairo/src/cairo-wideint-type-private.h @@ -121,6 +121,12 @@ typedef struct _cairo_quorem64 { cairo_int64_t rem; } cairo_quorem64_t; +/* gcc has a non-standard name. */ +#if HAVE___UINT128_T && !HAVE_UINT128_T +typedef __uint128_t uint128_t; +typedef __int128_t int128_t; +#define HAVE_UINT128_T 1 +#endif #if !HAVE_UINT128_T diff --git a/gfx/cairo/cairo/src/cairo-wideint.c b/gfx/cairo/cairo/src/cairo-wideint.c index 45655938679b..2c70d8662e89 100644 --- a/gfx/cairo/cairo/src/cairo-wideint.c +++ b/gfx/cairo/cairo/src/cairo-wideint.c @@ -37,19 +37,51 @@ #if HAVE_UINT64_T +#define uint64_lo32(i) ((i) & 0xffffffff) +#define uint64_hi32(i) ((i) >> 32) +#define uint64_lo(i) ((i) & 0xffffffff) +#define uint64_hi(i) ((i) >> 32) +#define uint64_shift32(i) ((i) << 32) +#define uint64_carry32 (((uint64_t) 1) << 32) + #define _cairo_uint32s_to_uint64(h,l) ((uint64_t) (h) << 32 | (l)) -cairo_uquorem64_t -_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den) -{ - cairo_uquorem64_t qr; +#else - qr.quo = num / den; - qr.rem = num % den; - return qr; +#define uint64_lo32(i) ((i).lo) +#define uint64_hi32(i) ((i).hi) + +static cairo_uint64_t +uint64_lo (cairo_uint64_t i) +{ + cairo_uint64_t s; + + s.lo = i.lo; + s.hi = 0; + return s; } -#else +static cairo_uint64_t +uint64_hi (cairo_uint64_t i) +{ + cairo_uint64_t s; + + s.lo = i.hi; + s.hi = 0; + return s; +} + +static cairo_uint64_t +uint64_shift32 (cairo_uint64_t i) +{ + cairo_uint64_t s; + + s.lo = 0; + s.hi = i.lo; + return s; +} + +static const cairo_uint64_t uint64_carry32 = { 0, 1 }; cairo_uint64_t _cairo_uint32_to_uint64 (uint32_t i) @@ -317,32 +349,7 @@ _cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den) #endif /* !HAVE_UINT64_T */ -cairo_quorem64_t -_cairo_int64_divrem (cairo_int64_t num, cairo_int64_t den) -{ - int num_neg = _cairo_int64_negative (num); - int den_neg = _cairo_int64_negative (den); - cairo_uquorem64_t uqr; - cairo_quorem64_t qr; - - if (num_neg) - num = _cairo_int64_negate (num); - if (den_neg) - den = _cairo_int64_negate (den); - uqr = _cairo_uint64_divrem (num, den); - if (num_neg) - qr.rem = _cairo_int64_negate (uqr.rem); - else - qr.rem = uqr.rem; - if (num_neg != den_neg) - qr.quo = (cairo_int64_t) _cairo_int64_negate (uqr.quo); - else - qr.quo = (cairo_int64_t) uqr.quo; - return qr; -} - #if HAVE_UINT128_T - cairo_uquorem128_t _cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den) { @@ -419,54 +426,6 @@ _cairo_uint128_sub (cairo_uint128_t a, cairo_uint128_t b) return s; } -#if HAVE_UINT64_T - -#define uint64_lo32(i) ((i) & 0xffffffff) -#define uint64_hi32(i) ((i) >> 32) -#define uint64_lo(i) ((i) & 0xffffffff) -#define uint64_hi(i) ((i) >> 32) -#define uint64_shift32(i) ((i) << 32) -#define uint64_carry32 (((uint64_t) 1) << 32) - -#else - -#define uint64_lo32(i) ((i).lo) -#define uint64_hi32(i) ((i).hi) - -static cairo_uint64_t -uint64_lo (cairo_uint64_t i) -{ - cairo_uint64_t s; - - s.lo = i.lo; - s.hi = 0; - return s; -} - -static cairo_uint64_t -uint64_hi (cairo_uint64_t i) -{ - cairo_uint64_t s; - - s.lo = i.hi; - s.hi = 0; - return s; -} - -static cairo_uint64_t -uint64_shift32 (cairo_uint64_t i) -{ - cairo_uint64_t s; - - s.lo = 0; - s.hi = i.lo; - return s; -} - -static const cairo_uint64_t uint64_carry32 = { 0, 1 }; - -#endif - cairo_uint128_t _cairo_uint64x64_128_mul (cairo_uint64_t a, cairo_uint64_t b) { diff --git a/gfx/cairo/cairo/src/cairo-win32-font.c b/gfx/cairo/cairo/src/cairo-win32-font.c index 8c41f8cc35a6..95d9da34956e 100644 --- a/gfx/cairo/cairo/src/cairo-win32-font.c +++ b/gfx/cairo/cairo/src/cairo-win32-font.c @@ -1373,6 +1373,7 @@ _cairo_win32_scaled_font_show_glyphs (void *abstract_font, unsigned int height, cairo_glyph_t *glyphs, int num_glyphs, + cairo_region_t *clip_region, int *remaining_glyphs) { cairo_win32_scaled_font_t *scaled_font = abstract_font; @@ -1394,15 +1395,17 @@ _cairo_win32_scaled_font_show_glyphs (void *abstract_font, */ COLORREF new_color; + status = _cairo_win32_surface_set_clip_region (surface, clip_region); + if (unlikely (status)) + return status; + new_color = RGB (((int)solid_pattern->color.red_short) >> 8, ((int)solid_pattern->color.green_short) >> 8, ((int)solid_pattern->color.blue_short) >> 8); - status = _draw_glyphs_on_surface (surface, scaled_font, new_color, - 0, 0, - glyphs, num_glyphs); - - return status; + return _draw_glyphs_on_surface (surface, scaled_font, new_color, + 0, 0, + glyphs, num_glyphs); } else { /* Otherwise, we need to draw using software fallbacks. We create a mask * surface by drawing the the glyphs onto a DIB, black-on-white then @@ -1447,10 +1450,6 @@ _cairo_win32_scaled_font_show_glyphs (void *abstract_font, _invert_argb32_mask (tmp_surface); mask_surface = &tmp_surface->base; - - /* XXX: Hacky, should expose this in cairo_image_surface */ - pixman_image_set_component_alpha (((cairo_image_surface_t *)tmp_surface->image)->pixman_image, TRUE); - } else { mask_surface = _compute_a8_mask (tmp_surface); cairo_surface_destroy (&tmp_surface->base); @@ -1464,6 +1463,10 @@ _cairo_win32_scaled_font_show_glyphs (void *abstract_font, * destination */ _cairo_pattern_init_for_surface (&mask, mask_surface); + cairo_surface_destroy (mask_surface); + + if (scaled_font->quality == CLEARTYPE_QUALITY) + mask.base.has_component_alpha = TRUE; status = _cairo_surface_composite (op, pattern, &mask.base, @@ -1471,12 +1474,11 @@ _cairo_win32_scaled_font_show_glyphs (void *abstract_font, source_x, source_y, 0, 0, dest_x, dest_y, - width, height); + width, height, + clip_region); _cairo_pattern_fini (&mask.base); - cairo_surface_destroy (mask_surface); - return status; } } @@ -1828,7 +1830,10 @@ _cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font free (buffer); CLEANUP_FONT: - cairo_win32_scaled_font_done_font (&scaled_font->base); + if (scaled_font->base.options.hint_style == CAIRO_HINT_STYLE_NONE) + _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base); + else + cairo_win32_scaled_font_done_font (&scaled_font->base); CLEANUP_PATH: if (status != CAIRO_STATUS_SUCCESS) diff --git a/gfx/cairo/cairo/src/cairo-win32-printing-surface.c b/gfx/cairo/cairo/src/cairo-win32-printing-surface.c index 9025bfa7b1e7..173c93345459 100644 --- a/gfx/cairo/cairo/src/cairo-win32-printing-surface.c +++ b/gfx/cairo/cairo/src/cairo-win32-printing-surface.c @@ -50,9 +50,10 @@ #include "cairo-clip-private.h" #include "cairo-win32-private.h" -#include "cairo-meta-surface-private.h" +#include "cairo-recording-surface-private.h" #include "cairo-scaled-font-subsets-private.h" #include "cairo-image-info-private.h" +#include "cairo-surface-clipper-private.h" #include @@ -122,13 +123,49 @@ _cairo_win32_printing_surface_init_image_support (cairo_win32_surface_t *surface surface->flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_PNG; } +/* When creating an EMF file, ExtTextOut with ETO_GLYPH_INDEX does not + * work unless the GDI function GdiInitializeLanguagePack() has been + * called. + * + * http://m-a-tech.blogspot.com/2009/04/emf-buffer-idiocracy.html + * + * The only information I could find on the how to use this + * undocumented function is the use in: + * + * http://src.chromium.org/viewvc/chrome/trunk/src/chrome/renderer/render_process.cc?view=markup + * + * to solve the same problem. The above code first checks if LPK.DLL + * is already loaded. If it is not it calls + * GdiInitializeLanguagePack() using the prototype + * BOOL GdiInitializeLanguagePack (int) + * and argument 0. + */ +static void +_cairo_win32_printing_surface_init_language_pack (cairo_win32_surface_t *surface) +{ + typedef BOOL (WINAPI *gdi_init_lang_pack_func_t)(int); + gdi_init_lang_pack_func_t gdi_init_lang_pack; + HMODULE module; + + if (GetModuleHandleW (L"LPK.DLL")) + return; + + module = GetModuleHandleW (L"GDI32.DLL"); + if (module) { + gdi_init_lang_pack = (gdi_init_lang_pack_func_t) + GetProcAddress (module, "GdiInitializeLanguagePack"); + if (gdi_init_lang_pack) + gdi_init_lang_pack (0); + } +} + static cairo_int_status_t analyze_surface_pattern_transparency (cairo_surface_pattern_t *pattern) { cairo_image_surface_t *image; void *image_extra; cairo_int_status_t status; - int x, y; + cairo_image_transparency_t transparency; status = _cairo_surface_acquire_source_image (pattern->surface, &image, @@ -136,38 +173,20 @@ analyze_surface_pattern_transparency (cairo_surface_pattern_t *pattern) if (status) return status; - if (image->base.status) - return image->base.status; - - if (image->format == CAIRO_FORMAT_RGB24) { + transparency = _cairo_image_analyze_transparency (image); + switch (transparency) { + case CAIRO_IMAGE_UNKNOWN: + ASSERT_NOT_REACHED; + case CAIRO_IMAGE_IS_OPAQUE: status = CAIRO_STATUS_SUCCESS; - goto RELEASE_SOURCE; - } + break; - if (image->format != CAIRO_FORMAT_ARGB32) { - /* If the surface does not support the image format, assume - * that it does have alpha. The image will be converted to - * rgb24 when the surface blends the image into the page - * color to remove the transparency. */ + case CAIRO_IMAGE_HAS_BILEVEL_ALPHA: + case CAIRO_IMAGE_HAS_ALPHA: status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; - goto RELEASE_SOURCE; + break; } - for (y = 0; y < image->height; y++) { - int a; - uint32_t *pixel = (uint32_t *) (image->data + y * image->stride); - - for (x = 0; x < image->width; x++, pixel++) { - a = (*pixel & 0xff000000) >> 24; - if (a != 255) { - status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; - goto RELEASE_SOURCE; - } - } - } - status = CAIRO_STATUS_SUCCESS; - -RELEASE_SOURCE: _cairo_surface_release_source_image (pattern->surface, image, image_extra); return status; @@ -176,9 +195,7 @@ RELEASE_SOURCE: static cairo_bool_t surface_pattern_supported (const cairo_surface_pattern_t *pattern) { - cairo_extend_t extend; - - if (_cairo_surface_is_meta (pattern->surface)) + if (_cairo_surface_is_recording (pattern->surface)) return TRUE; if (cairo_surface_get_type (pattern->surface) != CAIRO_SURFACE_TYPE_WIN32 && @@ -187,19 +204,7 @@ surface_pattern_supported (const cairo_surface_pattern_t *pattern) return FALSE; } - extend = cairo_pattern_get_extend ((cairo_pattern_t*)&pattern->base); - switch (extend) { - case CAIRO_EXTEND_NONE: - case CAIRO_EXTEND_REPEAT: - case CAIRO_EXTEND_REFLECT: - /* There's no point returning FALSE for EXTEND_PAD, as the image - * surface does not currently implement it either */ - case CAIRO_EXTEND_PAD: - return TRUE; - } - - ASSERT_NOT_REACHED; - return FALSE; + return TRUE; } static cairo_bool_t @@ -233,8 +238,8 @@ _cairo_win32_printing_surface_analyze_operation (cairo_win32_surface_t *surface, if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; - if ( _cairo_surface_is_meta (surface_pattern->surface)) - return CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN; + if ( _cairo_surface_is_recording (surface_pattern->surface)) + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; } if (op == CAIRO_OPERATOR_SOURCE || @@ -349,11 +354,13 @@ _cairo_win32_printing_surface_get_ctm_clip_box (cairo_win32_surface_t *surface, XFORM xform; _cairo_matrix_to_win32_xform (&surface->ctm, &xform); + if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY)) + return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:ModifyWorldTransform"); + GetClipBox (surface->dc, clip); + + _cairo_matrix_to_win32_xform (&surface->gdi_ctm, &xform); if (!SetWorldTransform (surface->dc, &xform)) return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:SetWorldTransform"); - GetClipBox (surface->dc, clip); - if (!ModifyWorldTransform (surface->dc, &xform, MWT_IDENTITY)) - return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:ModifyWorldTransform"); return CAIRO_STATUS_SUCCESS; } @@ -377,20 +384,21 @@ _cairo_win32_printing_surface_paint_solid_pattern (cairo_win32_surface_t *surfac } static cairo_status_t -_cairo_win32_printing_surface_paint_meta_pattern (cairo_win32_surface_t *surface, - cairo_surface_pattern_t *pattern) +_cairo_win32_printing_surface_paint_recording_pattern (cairo_win32_surface_t *surface, + cairo_surface_pattern_t *pattern) { cairo_content_t old_content; cairo_matrix_t old_ctm; cairo_bool_t old_has_ctm; - cairo_rectangle_int_t meta_extents; + cairo_rectangle_int_t recording_extents; cairo_status_t status; cairo_extend_t extend; cairo_matrix_t p2d; XFORM xform; int x_tile, y_tile, left, right, top, bottom; RECT clip; - cairo_surface_t *meta_surface = pattern->surface; + cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) pattern->surface; + cairo_box_t bbox; extend = cairo_pattern_get_extend (&pattern->base); @@ -406,19 +414,21 @@ _cairo_win32_printing_surface_paint_meta_pattern (cairo_win32_surface_t *surfa SaveDC (surface->dc); _cairo_matrix_to_win32_xform (&p2d, &xform); - status = _cairo_surface_get_extents (meta_surface, &meta_extents); + status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL); if (status) return status; + _cairo_box_round_to_rectangle (&bbox, &recording_extents); + status = _cairo_win32_printing_surface_get_ctm_clip_box (surface, &clip); if (status) return status; if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) { - left = (int) floor((double)clip.left/meta_extents.width); - right = (int) ceil((double)clip.right/meta_extents.width); - top = (int) floor((double)clip.top/meta_extents.height); - bottom = (int) ceil((double)clip.bottom/meta_extents.height); + left = floor (clip.left / _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x)); + right = ceil (clip.right / _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x)); + top = floor (clip.top / _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y)); + bottom = ceil (clip.bottom / _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y)); } else { left = 0; right = 1; @@ -427,7 +437,7 @@ _cairo_win32_printing_surface_paint_meta_pattern (cairo_win32_surface_t *surfa } old_content = surface->content; - if (cairo_surface_get_content (meta_surface) == CAIRO_CONTENT_COLOR) { + if (recording_surface->base.content == CAIRO_CONTENT_COLOR) { cairo_pattern_t *source; cairo_solid_pattern_t black; @@ -447,15 +457,15 @@ _cairo_win32_printing_surface_paint_meta_pattern (cairo_win32_surface_t *surfa SaveDC (surface->dc); m = p2d; cairo_matrix_translate (&m, - x_tile*meta_extents.width, - y_tile*meta_extents.height); + x_tile*recording_extents.width, + y_tile*recording_extents.height); if (extend == CAIRO_EXTEND_REFLECT) { if (x_tile % 2) { - cairo_matrix_translate (&m, meta_extents.width, 0); + cairo_matrix_translate (&m, recording_extents.width, 0); cairo_matrix_scale (&m, -1, 1); } if (y_tile % 2) { - cairo_matrix_translate (&m, 0, meta_extents.height); + cairo_matrix_translate (&m, 0, recording_extents.height); cairo_matrix_scale (&m, 1, -1); } } @@ -470,18 +480,18 @@ _cairo_win32_printing_surface_paint_meta_pattern (cairo_win32_surface_t *surfa cairo_matrix_transform_point (&surface->ctm, &x, &y); MoveToEx (surface->dc, (int) x, (int) y, NULL); - x = meta_extents.width; + x = recording_extents.width; y = 0; cairo_matrix_transform_point (&surface->ctm, &x, &y); LineTo (surface->dc, (int) x, (int) y); - x = meta_extents.width; - y = meta_extents.height; + x = recording_extents.width; + y = recording_extents.height; cairo_matrix_transform_point (&surface->ctm, &x, &y); LineTo (surface->dc, (int) x, (int) y); x = 0; - y = meta_extents.height; + y = recording_extents.height; cairo_matrix_transform_point (&surface->ctm, &x, &y); LineTo (surface->dc, (int) x, (int) y); @@ -490,8 +500,9 @@ _cairo_win32_printing_surface_paint_meta_pattern (cairo_win32_surface_t *surfa SelectClipPath (surface->dc, RGN_AND); SaveDC (surface->dc); /* Allow clip path to be reset during replay */ - status = _cairo_meta_surface_replay_region (meta_surface, &surface->base, - CAIRO_META_REGION_NATIVE); + status = _cairo_recording_surface_replay_region (&recording_surface->base, + &surface->base, + CAIRO_RECORDING_REGION_NATIVE); assert (status != CAIRO_INT_STATUS_UNSUPPORTED); /* Restore both the clip save and our earlier path SaveDC */ RestoreDC (surface->dc, -2); @@ -594,7 +605,6 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf cairo_extend_t extend; cairo_image_surface_t *image; void *image_extra; - cairo_surface_t *opaque_surface; cairo_image_surface_t *opaque_image = NULL; BITMAPINFO bi; cairo_matrix_t m; @@ -651,13 +661,15 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf &mime_size, &mime_info); } - if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) + if (_cairo_status_is_error (status)) return status; use_mime = (status == CAIRO_STATUS_SUCCESS); if (!use_mime && image->format != CAIRO_FORMAT_RGB24) { - cairo_surface_pattern_t opaque_pattern; + cairo_surface_t *opaque_surface; + cairo_surface_pattern_t image_pattern; + cairo_solid_pattern_t background_pattern; opaque_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, image->width, @@ -667,36 +679,27 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf goto CLEANUP_OPAQUE_IMAGE; } - _cairo_pattern_init_for_surface (&opaque_pattern, &image->base); - - status = _cairo_surface_fill_rectangle (opaque_surface, - CAIRO_OPERATOR_SOURCE, - background_color, - 0, 0, - image->width, image->height); - if (status) { - _cairo_pattern_fini (&opaque_pattern.base); + _cairo_pattern_init_solid (&background_pattern, + background_color, + CAIRO_CONTENT_COLOR); + status = _cairo_surface_paint (opaque_surface, + CAIRO_OPERATOR_SOURCE, + &background_pattern.base, + NULL); + if (status) goto CLEANUP_OPAQUE_IMAGE; - } - status = _cairo_surface_composite (CAIRO_OPERATOR_OVER, - &opaque_pattern.base, - NULL, - opaque_surface, - 0, 0, - 0, 0, - 0, 0, - image->width, - image->height); - if (status) { - _cairo_pattern_fini (&opaque_pattern.base); + _cairo_pattern_init_for_surface (&image_pattern, &image->base); + status = _cairo_surface_paint (opaque_surface, + CAIRO_OPERATOR_OVER, + &image_pattern.base, + NULL); + _cairo_pattern_fini (&image_pattern.base); + if (status) goto CLEANUP_OPAQUE_IMAGE; - } - _cairo_pattern_fini (&opaque_pattern.base); opaque_image = (cairo_image_surface_t *) opaque_surface; } else { - opaque_surface = &image->base; opaque_image = image; } @@ -717,7 +720,7 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf /* _cairo_pattern_set_matrix guarantees invertibility */ assert (status == CAIRO_STATUS_SUCCESS); - cairo_matrix_multiply (&m, &m, &surface->ctm); + cairo_matrix_multiply (&m, &m, &surface->gdi_ctm); SaveDC (surface->dc); _cairo_matrix_to_win32_xform (&m, &xform); @@ -730,10 +733,10 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf GetClipBox (surface->dc, &clip); if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) { - left = (int) floor((double)clip.left/opaque_image->width); - right = (int) ceil((double)clip.right/opaque_image->width); - top = (int) floor((double)clip.top/opaque_image->height); - bottom = (int) ceil((double)clip.bottom/opaque_image->height); + left = floor ( clip.left / (double) opaque_image->width); + right = ceil (clip.right / (double) opaque_image->width); + top = floor (clip.top / (double) opaque_image->height); + bottom = ceil (clip.bottom / (double) opaque_image->height); } else { left = 0; right = 1; @@ -767,7 +770,7 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf CLEANUP_OPAQUE_IMAGE: if (opaque_image != image) - cairo_surface_destroy (opaque_surface); + cairo_surface_destroy (&opaque_image->base); CLEANUP_IMAGE: _cairo_surface_release_source_image (pattern->surface, image, image_extra); @@ -778,9 +781,9 @@ static cairo_status_t _cairo_win32_printing_surface_paint_surface_pattern (cairo_win32_surface_t *surface, cairo_surface_pattern_t *pattern) { - if (_cairo_surface_is_meta (pattern->surface)) { - return _cairo_win32_printing_surface_paint_meta_pattern (surface, - pattern); + if (_cairo_surface_is_recording (pattern->surface)) { + return _cairo_win32_printing_surface_paint_recording_pattern (surface, + pattern); } else { return _cairo_win32_printing_surface_paint_image_pattern (surface, pattern); @@ -850,8 +853,8 @@ _cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_surface_t *surfa GetClipBox (surface->dc, &clip); if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) { - range_start = (int) floor(clip.left/d); - range_stop = (int) ceil(clip.right/d); + range_start = floor (clip.left / d); + range_stop = ceil (clip.right / d); } else { range_start = 0; range_stop = 1; @@ -1085,17 +1088,15 @@ _cairo_win32_printing_surface_emit_path (cairo_win32_surface_t *surface, cairo_path_fixed_t *path) { win32_path_info_t path_info; - cairo_status_t status; path_info.surface = surface; - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_win32_printing_surface_path_move_to, - _cairo_win32_printing_surface_path_line_to, - _cairo_win32_printing_surface_path_curve_to, - _cairo_win32_printing_surface_path_close_path, - &path_info); - return status; + return _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_win32_printing_surface_path_move_to, + _cairo_win32_printing_surface_path_line_to, + _cairo_win32_printing_surface_path_curve_to, + _cairo_win32_printing_surface_path_close_path, + &path_info); } static cairo_int_status_t @@ -1109,14 +1110,16 @@ _cairo_win32_printing_surface_show_page (void *abstract_surface) return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_win32_printing_surface_intersect_clip_path (void *abstract_surface, +static cairo_status_t +_cairo_win32_printing_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { - cairo_win32_surface_t *surface = abstract_surface; + cairo_win32_surface_t *surface = cairo_container_of (clipper, + cairo_win32_surface_t, + clipper); cairo_status_t status; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) @@ -1164,10 +1167,15 @@ static cairo_int_status_t _cairo_win32_printing_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_win32_surface_t *surface = abstract_surface; cairo_solid_pattern_t clear; + cairo_status_t status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (status) + return status; if (op == CAIRO_OPERATOR_CLEAR) { _cairo_win32_printing_surface_init_clear_color (surface, &clear); @@ -1242,7 +1250,7 @@ _cairo_win32_printing_surface_stroke (void *abstract_surface, cairo_matrix_t *stroke_ctm_inverse, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_win32_surface_t *surface = abstract_surface; cairo_int_status_t status; @@ -1258,6 +1266,10 @@ _cairo_win32_printing_surface_stroke (void *abstract_surface, cairo_matrix_t mat; double scale; + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (status) + return status; + if (op == CAIRO_OPERATOR_CLEAR) { _cairo_win32_printing_surface_init_clear_color (surface, &clear); source = (cairo_pattern_t*) &clear; @@ -1332,7 +1344,7 @@ _cairo_win32_printing_surface_stroke (void *abstract_surface, xform.eDx = 0.0f; xform.eDy = 0.0f; - if (!SetWorldTransform (surface->dc, &xform)) + if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY)) return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform"); if (source->type == CAIRO_PATTERN_TYPE_SOLID) { @@ -1344,7 +1356,8 @@ _cairo_win32_printing_surface_stroke (void *abstract_surface, return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectClipPath"); /* Return to device space to paint the pattern */ - if (!ModifyWorldTransform (surface->dc, &xform, MWT_IDENTITY)) + _cairo_matrix_to_win32_xform (&surface->gdi_ctm, &xform); + if (!SetWorldTransform (surface->dc, &xform)) return _cairo_win32_print_gdi_error ("_win32_surface_stroke:ModifyWorldTransform"); status = _cairo_win32_printing_surface_paint_pattern (surface, source); } @@ -1364,12 +1377,16 @@ _cairo_win32_printing_surface_fill (void *abstract_surface, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_win32_surface_t *surface = abstract_surface; cairo_int_status_t status; cairo_solid_pattern_t clear; + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (status) + return status; + if (op == CAIRO_OPERATOR_CLEAR) { _cairo_win32_printing_surface_init_clear_color (surface, &clear); source = (cairo_pattern_t*) &clear; @@ -1423,8 +1440,8 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip, + int *remaining_glyphs) { cairo_win32_surface_t *surface = abstract_surface; cairo_status_t status = CAIRO_STATUS_SUCCESS; @@ -1435,6 +1452,10 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac cairo_bool_t old_has_ctm; cairo_solid_pattern_t clear; + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (status) + return status; + if (op == CAIRO_OPERATOR_CLEAR) { _cairo_win32_printing_surface_init_clear_color (surface, &clear); source = (cairo_pattern_t*) &clear; @@ -1458,6 +1479,7 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac else return _cairo_win32_printing_surface_analyze_operation (surface, op, source); } +#endif /* For non win32 fonts we need to check that each glyph has a * path available. If a path is not available, @@ -1473,7 +1495,6 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac if (status) return status; } -#endif return _cairo_win32_printing_surface_analyze_operation (surface, op, source); } @@ -1535,10 +1556,11 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac glyphs = type1_glyphs; } - if (surface->has_ctm) { + if (surface->has_ctm || surface->has_gdi_ctm) { + cairo_matrix_multiply (&ctm, &surface->ctm, &surface->gdi_ctm); for (i = 0; i < num_glyphs; i++) - cairo_matrix_transform_point (&surface->ctm, &glyphs[i].x, &glyphs[i].y); - cairo_matrix_multiply (&ctm, &scaled_font->ctm, &surface->ctm); + cairo_matrix_transform_point (&ctm, &glyphs[i].x, &glyphs[i].y); + cairo_matrix_multiply (&ctm, &scaled_font->ctm, &ctm); scaled_font = cairo_scaled_font_create (scaled_font->font_face, &scaled_font->font_matrix, &ctm, @@ -1547,8 +1569,8 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac status = _cairo_win32_surface_show_glyphs (surface, op, source, glyphs, num_glyphs, scaled_font, - remaining_glyphs, - extents); + clip, + remaining_glyphs); if (surface->has_ctm) cairo_scaled_font_destroy (scaled_font); @@ -1607,7 +1629,12 @@ _cairo_win32_printing_surface_create_similar (void *abstract_surface, int width, int height) { - return cairo_meta_surface_create (content, width, height); + cairo_rectangle_t extents; + + extents.x = extents.y = 0; + extents.width = width; + extents.height = height; + return cairo_recording_surface_create (content, &extents); } static cairo_int_status_t @@ -1621,29 +1648,77 @@ _cairo_win32_printing_surface_start_page (void *abstract_surface) SaveDC (surface->dc); /* Save application context first, before doing MWT */ + /* As the logical coordinates used by GDI functions (eg LineTo) + * are integers we need to do some additional work to prevent + * rounding errors. For example the obvious way to paint a recording + * pattern is to: + * + * SaveDC() + * transform the device context DC by the pattern to device matrix + * replay the recording surface + * RestoreDC() + * + * The problem here is that if the pattern to device matrix is + * [100 0 0 100 0 0], coordinates in the recording pattern such as + * (1.56, 2.23) which correspond to (156, 223) in device space + * will be rounded to (100, 200) due to (1.56, 2.23) being + * truncated to integers. + * + * This is solved by saving the current GDI CTM in surface->ctm, + * switch the GDI CTM to identity, and transforming all + * coordinates by surface->ctm before passing them to GDI. When + * painting a recording pattern, surface->ctm is transformed by the + * pattern to device matrix. + * + * For printing device contexts where 1 unit is 1 dpi, switching + * the GDI CTM to identity maximises the possible resolution of + * coordinates. + * + * If the device context is an EMF file, using an identity + * transform often provides insufficent resolution. The workaround + * is to set the GDI CTM to a scale < 1 eg [1.0/16 0 0 1/0/16 0 0] + * and scale the cairo CTM by [16 0 0 16 0 0]. The + * SetWorldTransform function call to scale the GDI CTM by 1.0/16 + * will be recorded in the EMF followed by all the graphics + * functions by their coordinateds multiplied by 16. + * + * To support allowing the user to set a GDI CTM with scale < 1, + * we avoid switching to an identity CTM if the CTM xx and yy is < 1. + */ SetGraphicsMode (surface->dc, GM_ADVANCED); GetWorldTransform(surface->dc, &xform); - surface->ctm.xx = xform.eM11; - surface->ctm.xy = xform.eM21; - surface->ctm.yx = xform.eM12; - surface->ctm.yy = xform.eM22; - surface->ctm.x0 = xform.eDx; - surface->ctm.y0 = xform.eDy; - surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm); + if (xform.eM11 < 1 && xform.eM22 < 1) { + cairo_matrix_init_identity (&surface->ctm); + surface->gdi_ctm.xx = xform.eM11; + surface->gdi_ctm.xy = xform.eM21; + surface->gdi_ctm.yx = xform.eM12; + surface->gdi_ctm.yy = xform.eM22; + surface->gdi_ctm.x0 = xform.eDx; + surface->gdi_ctm.y0 = xform.eDy; + } else { + surface->ctm.xx = xform.eM11; + surface->ctm.xy = xform.eM21; + surface->ctm.yx = xform.eM12; + surface->ctm.yy = xform.eM22; + surface->ctm.x0 = xform.eDx; + surface->ctm.y0 = xform.eDy; + cairo_matrix_init_identity (&surface->gdi_ctm); + if (!ModifyWorldTransform (surface->dc, NULL, MWT_IDENTITY)) + return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_start_page:ModifyWorldTransform"); + } + surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm); + surface->has_gdi_ctm = !_cairo_matrix_is_identity (&surface->gdi_ctm); inverse_ctm = surface->ctm; status = cairo_matrix_invert (&inverse_ctm); if (status) return status; - x_res = (double) GetDeviceCaps(surface->dc, LOGPIXELSX); - y_res = (double) GetDeviceCaps(surface->dc, LOGPIXELSY); + x_res = GetDeviceCaps (surface->dc, LOGPIXELSX); + y_res = GetDeviceCaps (surface->dc, LOGPIXELSY); cairo_matrix_transform_distance (&inverse_ctm, &x_res, &y_res); _cairo_surface_set_resolution (&surface->base, x_res, y_res); - if (!ModifyWorldTransform (surface->dc, NULL, MWT_IDENTITY)) - return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_start_page:ModifyWorldTransform"); - SaveDC (surface->dc); /* Then save Cairo's known-good clip state, so the clip path can be reset */ return CAIRO_STATUS_SUCCESS; @@ -1698,6 +1773,9 @@ cairo_win32_printing_surface_create (HDC hdc) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } + _cairo_surface_clipper_init (&surface->clipper, + _cairo_win32_printing_surface_clipper_intersect_clip_path); + surface->image = NULL; surface->format = CAIRO_FORMAT_RGB24; surface->content = CAIRO_CONTENT_COLOR_ALPHA; @@ -1725,13 +1803,13 @@ cairo_win32_printing_surface_create (HDC hdc) _cairo_win32_printing_surface_init_ps_mode (surface); _cairo_win32_printing_surface_init_image_support (surface); - _cairo_surface_init (&surface->base, &cairo_win32_printing_surface_backend, + _cairo_win32_printing_surface_init_language_pack (surface); + _cairo_surface_init (&surface->base, + &cairo_win32_printing_surface_backend, CAIRO_CONTENT_COLOR_ALPHA); paginated = _cairo_paginated_surface_create (&surface->base, CAIRO_CONTENT_COLOR_ALPHA, - surface->extents.width, - surface->extents.height, &cairo_win32_surface_paginated_backend); /* paginated keeps the only reference to surface now, drop ours */ @@ -1754,7 +1832,7 @@ static const cairo_surface_backend_t cairo_win32_printing_surface_backend = { NULL, /* release_source_image */ NULL, /* acquire_dest_image */ NULL, /* release_dest_image */ - _cairo_win32_surface_clone_similar, + NULL, /* clone_similar */ NULL, /* composite */ NULL, /* fill_rectangles */ NULL, /* composite_trapezoids */ @@ -1762,8 +1840,6 @@ static const cairo_surface_backend_t cairo_win32_printing_surface_backend = { NULL, /* check_span_renderer */ NULL, /* copy_page */ _cairo_win32_printing_surface_show_page, - NULL, /* set_clip_region */ - _cairo_win32_printing_surface_intersect_clip_path, _cairo_win32_surface_get_extents, NULL, /* old_show_glyphs */ _cairo_win32_printing_surface_get_font_options, @@ -1779,7 +1855,6 @@ static const cairo_surface_backend_t cairo_win32_printing_surface_backend = { _cairo_win32_printing_surface_show_glyphs, NULL, /* snapshot */ NULL, /* is_similar */ - NULL, /* reset */ NULL, /* fill_stroke */ }; diff --git a/gfx/cairo/cairo/src/cairo-win32-private.h b/gfx/cairo/cairo/src/cairo-win32-private.h index ca5ea5fe157d..f0dfbc56a64e 100644 --- a/gfx/cairo/cairo/src/cairo-win32-private.h +++ b/gfx/cairo/cairo/src/cairo-win32-private.h @@ -38,6 +38,7 @@ #include "cairo-win32.h" #include "cairoint.h" +#include "cairo-surface-clipper-private.h" #ifndef SHADEBLENDCAPS #define SHADEBLENDCAPS 120 @@ -81,6 +82,10 @@ typedef struct _cairo_win32_surface { cairo_rectangle_int_t clip_rect; HRGN initial_clip_rgn; cairo_bool_t had_simple_clip; + cairo_region_t *clip_region; + + /* For path clipping to the printing-surface */ + cairo_surface_clipper_t clipper; /* Surface DC flags */ uint32_t flags; @@ -91,6 +96,8 @@ typedef struct _cairo_win32_surface { cairo_bool_t path_empty; cairo_bool_t has_ctm; cairo_matrix_t ctm; + cairo_bool_t has_gdi_ctm; + cairo_matrix_t gdi_ctm; HBRUSH brush, old_brush; cairo_scaled_font_subsets_t *font_subsets; } cairo_win32_surface_t; @@ -140,13 +147,17 @@ _cairo_surface_is_win32_printing (cairo_surface_t *surface); cairo_status_t _cairo_win32_surface_finish (void *abstract_surface); -cairo_int_status_t +cairo_bool_t _cairo_win32_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle); uint32_t _cairo_win32_flags_for_dc (HDC dc); +cairo_status_t +_cairo_win32_surface_set_clip_region (void *abstract_surface, + cairo_region_t *region); + cairo_int_status_t _cairo_win32_surface_show_glyphs (void *surface, cairo_operator_t op, @@ -154,8 +165,8 @@ _cairo_win32_surface_show_glyphs (void *surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip, + int *remaining_glyphs); cairo_surface_t * _cairo_win32_surface_create_similar (void *abstract_src, @@ -223,14 +234,14 @@ inline BOOL ModifyWorldTransform(HDC hdc, CONST XFORM * lpxf, DWORD mode) { retu #ifdef CAIRO_HAS_DWRITE_FONT CAIRO_BEGIN_DECLS -cairo_public cairo_int_status_t -cairo_dwrite_show_glyphs_on_surface(void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_rectangle_int_t *extents); +cairo_int_status_t +_cairo_dwrite_show_glyphs_on_surface(void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip); CAIRO_END_DECLS diff --git a/gfx/cairo/cairo/src/cairo-win32-surface.c b/gfx/cairo/cairo/src/cairo-win32-surface.c index 9ab47655ebc4..d4575a37ac87 100644 --- a/gfx/cairo/cairo/src/cairo-win32-surface.c +++ b/gfx/cairo/cairo/src/cairo-win32-surface.c @@ -51,7 +51,9 @@ #include "cairo-paginated-private.h" #include "cairo-win32-private.h" #include "cairo-scaled-font-subsets-private.h" +#include "cairo-surface-fallback-private.h" +#include #include #if defined(__MINGW32__) && !defined(ETO_PDY) @@ -337,6 +339,8 @@ _cairo_win32_surface_create_for_dc (HDC original_dc, if (surface == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + surface->clip_region = NULL; + status = _create_dc_and_bitmap (surface, original_dc, format, width, height, &bits, &rowstride); @@ -428,60 +432,6 @@ _cairo_win32_surface_create_similar (void *abstract_src, return _cairo_win32_surface_create_similar_internal (abstract_src, content, width, height, FALSE); } -cairo_status_t -_cairo_win32_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - cairo_content_t content, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out) -{ - cairo_content_t src_content; - cairo_surface_t *new_surface; - cairo_status_t status; - cairo_surface_pattern_t pattern; - - src_content = src->content & content; - new_surface = - _cairo_win32_surface_create_similar_internal (abstract_surface, - src_content, - width, height, - FALSE); - if (new_surface == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = new_surface->status; - if (status) - return status; - - _cairo_pattern_init_for_surface (&pattern, src); - - status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, - &pattern.base, - NULL, - new_surface, - src_x, src_y, - 0, 0, - 0, 0, - width, height); - - _cairo_pattern_fini (&pattern.base); - - if (status == CAIRO_STATUS_SUCCESS) { - *clone_offset_x = src_x; - *clone_offset_y = src_y; - *clone_out = new_surface; - } else - cairo_surface_destroy (new_surface); - - return status; -} - - cairo_status_t _cairo_win32_surface_finish (void *abstract_surface) { @@ -617,7 +567,7 @@ _cairo_win32_surface_acquire_source_image (void *abstract_sur void **image_extra) { cairo_win32_surface_t *surface = abstract_surface; - cairo_win32_surface_t *local = NULL; + cairo_win32_surface_t *local; cairo_status_t status; if (!surface->image && !surface->is_dib && surface->bitmap && @@ -634,7 +584,6 @@ _cairo_win32_surface_acquire_source_image (void *abstract_sur if (surface->image) { *image_out = (cairo_image_surface_t *)surface->image; *image_extra = NULL; - return CAIRO_STATUS_SUCCESS; } @@ -646,7 +595,6 @@ _cairo_win32_surface_acquire_source_image (void *abstract_sur *image_out = (cairo_image_surface_t *)local->image; *image_extra = local; - return CAIRO_STATUS_SUCCESS; } @@ -671,61 +619,28 @@ _cairo_win32_surface_acquire_dest_image (void *abstract_surfa cairo_win32_surface_t *surface = abstract_surface; cairo_win32_surface_t *local = NULL; cairo_status_t status; - RECT clip_box; - int x1, y1, x2, y2; if (surface->image) { GdiFlush(); - image_rect->x = 0; - image_rect->y = 0; - image_rect->width = surface->extents.width; - image_rect->height = surface->extents.height; - - *image_out = (cairo_image_surface_t *)surface->image; + *image_out = (cairo_image_surface_t *) surface->image; *image_extra = NULL; - - return CAIRO_STATUS_SUCCESS; - } - - if (GetClipBox (surface->dc, &clip_box) == ERROR) - return _cairo_win32_print_gdi_error ("_cairo_win3_surface_acquire_dest_image"); - - x1 = clip_box.left; - x2 = clip_box.right; - y1 = clip_box.top; - y2 = clip_box.bottom; - - if (interest_rect->x > x1) - x1 = interest_rect->x; - if (interest_rect->y > y1) - y1 = interest_rect->y; - if ((int) (interest_rect->x + interest_rect->width) < x2) - x2 = interest_rect->x + interest_rect->width; - if ((int) (interest_rect->y + interest_rect->height) < y2) - y2 = interest_rect->y + interest_rect->height; - - if (x1 >= x2 || y1 >= y2) { - *image_out = NULL; - *image_extra = NULL; - + *image_rect = surface->extents; return CAIRO_STATUS_SUCCESS; } status = _cairo_win32_surface_get_subimage (abstract_surface, - x1, y1, x2 - x1, y2 - y1, + interest_rect->x, + interest_rect->y, + interest_rect->width, + interest_rect->height, &local); if (status) return status; - *image_out = (cairo_image_surface_t *)local->image; + *image_out = (cairo_image_surface_t *) local->image; *image_extra = local; - - image_rect->x = x1; - image_rect->y = y1; - image_rect->width = x2 - x1; - image_rect->height = y2 - y1; - + *image_rect = *interest_rect; return CAIRO_STATUS_SUCCESS; } @@ -753,6 +668,107 @@ _cairo_win32_surface_release_dest_image (void *abstract_surfa cairo_surface_destroy ((cairo_surface_t *)local); } +cairo_status_t +_cairo_win32_surface_set_clip_region (void *abstract_surface, + cairo_region_t *region) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + if (surface->clip_region == region) + return CAIRO_STATUS_SUCCESS; + + cairo_region_destroy (surface->clip_region); + surface->clip_region = cairo_region_reference (region); + + /* The semantics we want is that any clip set by cairo combines + * is intersected with the clip on device context that the + * surface was created for. To implement this, we need to + * save the original clip when first setting a clip on surface. + */ + + /* Clear any clip set by cairo, return to the original first */ + status = _cairo_win32_restore_initial_clip (surface); + + /* Then combine any new region with it */ + if (region) { + cairo_rectangle_int_t extents; + int num_rects; + RGNDATA *data; + size_t data_size; + RECT *rects; + int i; + HRGN gdi_region; + + /* Create a GDI region for the cairo region */ + + cairo_region_get_extents (region, &extents); + num_rects = cairo_region_num_rectangles (region); + /* XXX see notes in _cairo_win32_save_initial_clip -- + * this code will interact badly with a HDC which had an initial + * world transform -- we should probably manually transform the + * region rects, because SelectClipRgn takes device units, not + * logical units (unlike IntersectClipRect). + */ + + data_size = sizeof (RGNDATAHEADER) + num_rects * sizeof (RECT); + data = malloc (data_size); + if (!data) + return _cairo_error(CAIRO_STATUS_NO_MEMORY); + rects = (RECT *)data->Buffer; + + data->rdh.dwSize = sizeof (RGNDATAHEADER); + data->rdh.iType = RDH_RECTANGLES; + data->rdh.nCount = num_rects; + data->rdh.nRgnSize = num_rects * sizeof (RECT); + data->rdh.rcBound.left = extents.x; + data->rdh.rcBound.top = extents.y; + data->rdh.rcBound.right = extents.x + extents.width; + data->rdh.rcBound.bottom = extents.y + extents.height; + + for (i = 0; i < num_rects; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (region, i, &rect); + + rects[i].left = rect.x; + rects[i].top = rect.y; + rects[i].right = rect.x + rect.width; + rects[i].bottom = rect.y + rect.height; + } + + gdi_region = ExtCreateRegion (NULL, data_size, data); + free (data); + + if (!gdi_region) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + +#ifndef WINCE + /* AND the new region into our DC */ + if (ExtSelectClipRgn (surface->dc, gdi_region, RGN_AND) == ERROR) + status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_set_clip_region"); +#else + // The ExtSelectClipRgn function combines the specified + // region with the current clipping region using the + // specified mode. Here we do similar using basic + // functions available on WINCE. + { + HRGN currentClip, newClip; + GetClipRgn(surface->dc, ¤tClip); + + if (CombineRgn(newClip, currentClip, gdi_region, RGN_AND) != ERROR) { + SelectClipRgn(surface->dc, newClip); + DeleteObject(newClip); + } + } +#endif + + DeleteObject (gdi_region); + } + + return status; +} + #if !defined(AC_SRC_OVER) #define AC_SRC_OVER 0x00 #pragma pack(1) @@ -949,7 +965,8 @@ _cairo_win32_surface_composite (cairo_operator_t op, int dst_x, int dst_y, unsigned int width, - unsigned int height) + unsigned int height, + cairo_region_t *clip_region) { cairo_win32_surface_t *dst = abstract_dst; cairo_win32_surface_t *src; @@ -1090,7 +1107,7 @@ _cairo_win32_surface_composite (cairo_operator_t op, fflush (stderr); #endif - /* If the src recangle doesn't wholly lie within the src extents, + /* If the src rectangle doesn't wholly lie within the src extents, * fudge things. We really need to do fixup on the unpainted * region -- e.g. the SOURCE operator is broken for areas outside * of the extents, because it won't clear that area to transparent @@ -1210,6 +1227,10 @@ _cairo_win32_surface_composite (cairo_operator_t op, fflush (stderr); #endif + status = _cairo_win32_surface_set_clip_region (dst, clip_region); + if (status) + return status; + /* If we need to repeat, we turn the repeated blit into * a bunch of piece-by-piece blits. */ @@ -1343,7 +1364,8 @@ UNSUPPORTED: src_x, src_y, mask_x, mask_y, dst_x, dst_y, - width, height); + width, height, + clip_region); } return CAIRO_INT_STATUS_UNSUPPORTED; @@ -1447,6 +1469,10 @@ _cairo_win32_surface_fill_rectangles (void *abstract_surface, if (surface->format != CAIRO_FORMAT_RGB24) return CAIRO_INT_STATUS_UNSUPPORTED; + status = _cairo_win32_surface_set_clip_region (surface, NULL); + if (status) + return status; + /* Optimize for no destination alpha (surface->pixman_image is non-NULL for all * surfaces with alpha.) */ @@ -1492,150 +1518,20 @@ _cairo_win32_surface_fill_rectangles (void *abstract_surface, return status; } -static cairo_int_status_t -_cairo_win32_surface_set_clip_region (void *abstract_surface, - cairo_region_t *region) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - /* If we are in-memory, then we set the clip on the image surface - * as well as on the underlying GDI surface. - */ - if (surface->image) { - unsigned int serial; - - serial = _cairo_surface_allocate_clip_serial (surface->image); - status = _cairo_surface_set_clip_region (surface->image, region, serial); - if (status) - return status; - } - - /* The semantics we want is that any clip set by cairo combines - * is intersected with the clip on device context that the - * surface was created for. To implement this, we need to - * save the original clip when first setting a clip on surface. - */ - - /* Clear any clip set by cairo, return to the original first */ - status = _cairo_win32_restore_initial_clip (surface); - - /* Then combine any new region with it */ - if (region) { - cairo_rectangle_int_t extents; - int num_rects; - RGNDATA *data; - size_t data_size; - RECT *rects; - int i; - HRGN gdi_region; - cairo_rectangle_int_t rect0; - - /* Create a GDI region for the cairo region */ - - cairo_region_get_extents (region, &extents); - num_rects = cairo_region_num_rectangles (region); - - if (num_rects == 1) - cairo_region_get_rectangle (region, 0, &rect0); - - if (num_rects == 1 && - rect0.x == 0 && - rect0.y == 0 && - rect0.width == surface->extents.width && - rect0.width == surface->extents.height) - { - gdi_region = NULL; - - SelectClipRgn (surface->dc, NULL); - IntersectClipRect (surface->dc, - rect0.x, - rect0.y, - rect0.x + rect0.width, - rect0.y + rect0.height); - } else { - /* XXX see notes in _cairo_win32_save_initial_clip -- - * this code will interact badly with a HDC which had an initial - * world transform -- we should probably manually transform the - * region rects, because SelectClipRgn takes device units, not - * logical units (unlike IntersectClipRect). - */ - - data_size = sizeof (RGNDATAHEADER) + num_rects * sizeof (RECT); - data = malloc (data_size); - if (!data) - return _cairo_error(CAIRO_STATUS_NO_MEMORY); - rects = (RECT *)data->Buffer; - - data->rdh.dwSize = sizeof (RGNDATAHEADER); - data->rdh.iType = RDH_RECTANGLES; - data->rdh.nCount = num_rects; - data->rdh.nRgnSize = num_rects * sizeof (RECT); - data->rdh.rcBound.left = extents.x; - data->rdh.rcBound.top = extents.y; - data->rdh.rcBound.right = extents.x + extents.width; - data->rdh.rcBound.bottom = extents.y + extents.height; - - for (i = 0; i < num_rects; i++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (region, i, &rect); - - rects[i].left = rect.x; - rects[i].top = rect.y; - rects[i].right = rect.x + rect.width; - rects[i].bottom = rect.y + rect.height; - } - - gdi_region = ExtCreateRegion (NULL, data_size, data); - free (data); - - if (!gdi_region) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - /* AND the new region into our DC */ - -#ifndef WINCE - if (ExtSelectClipRgn (surface->dc, gdi_region, RGN_AND) == ERROR) - status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_set_clip_region"); -#else - // The ExtSelectClipRgn function combines the specified - // region with the current clipping region using the - // specified mode. Here we do similar using basic - // functions available on WINCE. - { - HRGN currentClip, newClip; - GetClipRgn(surface->dc, ¤tClip); - - if (CombineRgn(newClip, currentClip, gdi_region, RGN_AND) != ERROR) { - SelectClipRgn(surface->dc, newClip); - DeleteObject(newClip); - } - } -#endif - - DeleteObject (gdi_region); - } - } - - return status; -} - -cairo_int_status_t +cairo_bool_t _cairo_win32_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_win32_surface_t *surface = abstract_surface; *rectangle = surface->extents; - - return CAIRO_STATUS_SUCCESS; + return TRUE; } static cairo_status_t _cairo_win32_surface_flush (void *abstract_surface) { - return _cairo_surface_reset_clip (abstract_surface); + return _cairo_win32_surface_set_clip_region (abstract_surface, NULL); } #define STACK_GLYPH_SIZE 256 @@ -1647,13 +1543,13 @@ _cairo_win32_surface_show_glyphs (void *surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip, + int *remaining_glyphs) { #if defined(CAIRO_HAS_WIN32_FONT) && !defined(WINCE) if (scaled_font->backend->type == CAIRO_FONT_TYPE_DWRITE) { #ifdef CAIRO_HAS_DWRITE_FONT - return cairo_dwrite_show_glyphs_on_surface(surface, op, source, glyphs, num_glyphs, scaled_font, extents); + return _cairo_dwrite_show_glyphs_on_surface(surface, op, source, glyphs, num_glyphs, scaled_font, clip); #endif } else { cairo_win32_surface_t *dst = surface; @@ -1693,11 +1589,19 @@ _cairo_win32_surface_show_glyphs (void *surface, /* If we have a fallback mask clip set on the dst, we have * to go through the fallback path, but only if we're not * doing this for printing */ - if (dst->base.clip && - !(dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) && - (dst->base.clip->mode != CAIRO_CLIP_MODE_REGION || - dst->base.clip->surface != NULL)) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (clip != NULL) { + if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) == 0) { + cairo_region_t *clip_region; + cairo_status_t status; + + status = _cairo_clip_get_region (clip, &clip_region); + assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); + if (status) + return status; + + _cairo_win32_surface_set_clip_region (surface, clip_region); + } + } solid_pattern = (cairo_solid_pattern_t *)source; color = RGB(((int)solid_pattern->color.red_short) >> 8, @@ -1835,6 +1739,7 @@ cairo_win32_surface_create (HDC hdc) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } + surface->clip_region = NULL; surface->image = NULL; surface->format = format; @@ -2042,19 +1947,6 @@ _cairo_win32_surface_is_similar (void *surface_a, return a->dc == b->dc; } -static cairo_status_t -_cairo_win32_surface_reset (void *abstract_surface) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_status_t status; - - status = _cairo_win32_surface_set_clip_region (surface, NULL); - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; -} - typedef struct _cairo_win32_surface_span_renderer { cairo_span_renderer_t base; @@ -2062,21 +1954,27 @@ typedef struct _cairo_win32_surface_span_renderer { const cairo_pattern_t *pattern; cairo_antialias_t antialias; + uint8_t *mask_data; + uint32_t mask_stride; + cairo_image_surface_t *mask; cairo_win32_surface_t *dst; + cairo_region_t *clip_region; cairo_composite_rectangles_t composite_rectangles; } cairo_win32_surface_span_renderer_t; static cairo_status_t -_cairo_win32_surface_span_renderer_render_row ( +_cairo_win32_surface_span_renderer_render_rows ( void *abstract_renderer, int y, + int height, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_win32_surface_span_renderer_t *renderer = abstract_renderer; - _cairo_image_surface_span_render_row (y, spans, num_spans, renderer->mask, &renderer->composite_rectangles); + while (height--) + _cairo_image_surface_span_render_row (y++, spans, num_spans, renderer->mask_data, renderer->mask_stride); return CAIRO_STATUS_SUCCESS; } @@ -2116,18 +2014,20 @@ _cairo_win32_surface_span_renderer_finish (void *abstract_renderer) rects->src.y, 0, 0, /* mask.x, mask.y */ rects->dst.x, rects->dst.y, - rects->width, rects->height); + rects->width, rects->height, + renderer->clip_region); } else { /* otherwise go through the fallback_composite path which * will do the appropriate surface acquisition */ status = _cairo_surface_fallback_composite ( renderer->op, - renderer->pattern, mask_pattern, dst, + renderer->pattern, mask_pattern, &dst->base, rects->src.x, rects->src.y, 0, 0, /* mask.x, mask.y */ rects->dst.x, rects->dst.y, - rects->width, rects->height); + rects->width, rects->height, + renderer->clip_region); } cairo_pattern_destroy (mask_pattern); @@ -2142,14 +2042,12 @@ static cairo_bool_t _cairo_win32_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) + cairo_antialias_t antialias) { (void) op; (void) pattern; (void) abstract_dst; (void) antialias; - (void) rects; return TRUE; } @@ -2158,26 +2056,27 @@ _cairo_win32_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) + const cairo_composite_rectangles_t *rects, + cairo_region_t *clip_region) { cairo_win32_surface_t *dst = abstract_dst; - cairo_win32_surface_span_renderer_t *renderer - = calloc(1, sizeof(*renderer)); + cairo_win32_surface_span_renderer_t *renderer; cairo_status_t status; int width = rects->width; int height = rects->height; + renderer = calloc(1, sizeof(*renderer)); if (renderer == NULL) return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY); renderer->base.destroy = _cairo_win32_surface_span_renderer_destroy; renderer->base.finish = _cairo_win32_surface_span_renderer_finish; - renderer->base.render_row = - _cairo_win32_surface_span_renderer_render_row; + renderer->base.render_rows = _cairo_win32_surface_span_renderer_render_rows; renderer->op = op; renderer->pattern = pattern; renderer->antialias = antialias; renderer->dst = dst; + renderer->clip_region = clip_region; renderer->composite_rectangles = *rects; @@ -2193,6 +2092,9 @@ _cairo_win32_surface_create_span_renderer (cairo_operator_t op, _cairo_win32_surface_span_renderer_destroy (renderer); return _cairo_span_renderer_create_in_error (status); } + + renderer->mask_data = renderer->mask->data - rects->mask.x - rects->mask.y * renderer->mask->stride; + renderer->mask_stride = renderer->mask->stride; return &renderer->base; } @@ -2205,7 +2107,7 @@ static const cairo_surface_backend_t cairo_win32_surface_backend = { _cairo_win32_surface_release_source_image, _cairo_win32_surface_acquire_dest_image, _cairo_win32_surface_release_dest_image, - _cairo_win32_surface_clone_similar, + NULL, /* clone similar */ _cairo_win32_surface_composite, _cairo_win32_surface_fill_rectangles, NULL, /* composite_trapezoids */ @@ -2213,8 +2115,6 @@ static const cairo_surface_backend_t cairo_win32_surface_backend = { _cairo_win32_surface_check_span_renderer, NULL, /* copy_page */ NULL, /* show_page */ - _cairo_win32_surface_set_clip_region, - NULL, /* intersect_clip_path */ _cairo_win32_surface_get_extents, NULL, /* old_show_glyphs */ NULL, /* get_font_options */ @@ -2231,8 +2131,6 @@ static const cairo_surface_backend_t cairo_win32_surface_backend = { NULL, /* snapshot */ _cairo_win32_surface_is_similar, - - _cairo_win32_surface_reset }; /* Notes: @@ -2343,7 +2241,7 @@ void _cairo_win32_debug_dump_hrgn (HRGN rgn, char *header) { RGNDATA *rd; - int z; + unsigned int z; if (header) fprintf (stderr, "%s\n", header); @@ -2356,11 +2254,17 @@ _cairo_win32_debug_dump_hrgn (HRGN rgn, char *header) rd = (RGNDATA*) malloc(z); z = GetRegionData(rgn, z, rd); - fprintf (stderr, " %d rects, bounds: %d %d %d %d\n", rd->rdh.nCount, rd->rdh.rcBound.left, rd->rdh.rcBound.top, rd->rdh.rcBound.right - rd->rdh.rcBound.left, rd->rdh.rcBound.bottom - rd->rdh.rcBound.top); + fprintf (stderr, " %ld rects, bounds: %ld %ld %ld %ld\n", + rd->rdh.nCount, + rd->rdh.rcBound.left, + rd->rdh.rcBound.top, + rd->rdh.rcBound.right - rd->rdh.rcBound.left, + rd->rdh.rcBound.bottom - rd->rdh.rcBound.top); for (z = 0; z < rd->rdh.nCount; z++) { RECT r = ((RECT*)rd->Buffer)[z]; - fprintf (stderr, " [%d]: [%d %d %d %d]\n", z, r.left, r.top, r.right - r.left, r.bottom - r.top); + fprintf (stderr, " [%d]: [%ld %ld %ld %ld]\n", + z, r.left, r.top, r.right - r.left, r.bottom - r.top); } free(rd); diff --git a/gfx/cairo/cairo/src/cairo-xcb-surface.c b/gfx/cairo/cairo/src/cairo-xcb-surface.c index a7340cf947de..8cd82f023f63 100644 --- a/gfx/cairo/cairo/src/cairo-xcb-surface.c +++ b/gfx/cairo/cairo/src/cairo-xcb-surface.c @@ -38,6 +38,8 @@ #include "cairo-xcb.h" #include "cairo-xcb-xrender.h" #include "cairo-clip-private.h" +#include "cairo-list-private.h" +#include "cairo-freelist-private.h" #include #define AllPlanes ((unsigned long)~0L) @@ -75,11 +77,20 @@ typedef struct cairo_xcb_surface { cairo_bool_t have_clip_rects; xcb_rectangle_t *clip_rects; int num_clip_rects; + cairo_region_t *clip_region; xcb_render_picture_t src_picture, dst_picture; xcb_render_pictforminfo_t xrender_format; + + cairo_list_t to_be_checked; + cairo_freepool_t cookie_pool; } cairo_xcb_surface_t; +typedef struct _cairo_xcb_cookie { + cairo_list_t link; + xcb_void_cookie_t xcb; +} cairo_xcb_cookie_t; + #define CAIRO_SURFACE_RENDER_AT_LEAST(surface, major, minor) \ (((surface)->render_major > major) || \ (((surface)->render_major == major) && ((surface)->render_minor >= minor))) @@ -104,7 +115,7 @@ typedef struct cairo_xcb_surface { #define CAIRO_SURFACE_RENDER_HAS_REPEAT_PAD(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10) #define CAIRO_SURFACE_RENDER_HAS_REPEAT_REFLECT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10) -static void +static cairo_status_t _cairo_xcb_surface_ensure_gc (cairo_xcb_surface_t *surface); static int @@ -123,6 +134,22 @@ _CAIRO_FORMAT_DEPTH (cairo_format_t format) } } +static cairo_status_t +_cairo_xcb_add_cookie_to_be_checked (cairo_xcb_surface_t *surface, + xcb_void_cookie_t xcb) +{ + cairo_xcb_cookie_t *cookie; + + cookie = _cairo_freepool_alloc (&surface->cookie_pool); + if (unlikely (cookie == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + cookie->xcb = xcb; + cairo_list_add_tail (&cookie->link, &surface->to_be_checked); + + return CAIRO_STATUS_SUCCESS; +} + static xcb_render_pictforminfo_t * _CAIRO_FORMAT_TO_XRENDER_FORMAT(xcb_connection_t *dpy, cairo_format_t format) { @@ -166,6 +193,109 @@ _xcb_render_format_to_content (xcb_render_pictforminfo_t *xrender_format) return CAIRO_CONTENT_COLOR; } +static cairo_status_t +_cairo_xcb_surface_set_gc_clip_rects (cairo_xcb_surface_t *surface) +{ + if (surface->have_clip_rects) { + xcb_void_cookie_t cookie; + + cookie = xcb_set_clip_rectangles_checked (surface->dpy, + XCB_CLIP_ORDERING_YX_SORTED, surface->gc, + 0, 0, + surface->num_clip_rects, + surface->clip_rects); + + return _cairo_xcb_add_cookie_to_be_checked (surface, cookie); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xcb_surface_set_picture_clip_rects (cairo_xcb_surface_t *surface) +{ + if (surface->have_clip_rects) { + xcb_void_cookie_t cookie; + + cookie = xcb_render_set_picture_clip_rectangles_checked (surface->dpy, + surface->dst_picture, + 0, 0, + surface->num_clip_rects, + surface->clip_rects); + + return _cairo_xcb_add_cookie_to_be_checked (surface, cookie); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xcb_surface_set_clip_region (void *abstract_surface, + cairo_region_t *region) +{ + cairo_xcb_surface_t *surface = abstract_surface; + + if (region == surface->clip_region) + return CAIRO_STATUS_SUCCESS; + + cairo_region_destroy (surface->clip_region); + region = cairo_region_reference (region); + + if (surface->clip_rects) { + free (surface->clip_rects); + surface->clip_rects = NULL; + } + + surface->have_clip_rects = FALSE; + surface->num_clip_rects = 0; + + if (region == NULL) { + uint32_t none[] = { XCB_NONE }; + if (surface->gc) + xcb_change_gc (surface->dpy, surface->gc, XCB_GC_CLIP_MASK, none); + + if (surface->xrender_format.id != XCB_NONE && surface->dst_picture) + xcb_render_change_picture (surface->dpy, surface->dst_picture, + XCB_RENDER_CP_CLIP_MASK, none); + } else { + xcb_rectangle_t *rects = NULL; + int n_rects, i; + + n_rects = cairo_region_num_rectangles (region); + + if (n_rects > 0) { + rects = _cairo_malloc_ab (n_rects, sizeof(xcb_rectangle_t)); + if (rects == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else { + rects = NULL; + } + + for (i = 0; i < n_rects; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (region, i, &rect); + + rects[i].x = rect.x; + rects[i].y = rect.y; + rects[i].width = rect.width; + rects[i].height = rect.height; + } + + surface->have_clip_rects = TRUE; + surface->clip_rects = rects; + surface->num_clip_rects = n_rects; + + if (surface->gc) + _cairo_xcb_surface_set_gc_clip_rects (surface); + + if (surface->dst_picture) + _cairo_xcb_surface_set_picture_clip_rects (surface); + } + + return CAIRO_STATUS_SUCCESS; +} + static cairo_surface_t * _cairo_xcb_surface_create_similar (void *abstract_src, cairo_content_t content, @@ -210,6 +340,7 @@ static cairo_status_t _cairo_xcb_surface_finish (void *abstract_surface) { cairo_xcb_surface_t *surface = abstract_surface; + if (surface->dst_picture != XCB_NONE) xcb_render_free_picture (surface->dpy, surface->dst_picture); @@ -223,6 +354,9 @@ _cairo_xcb_surface_finish (void *abstract_surface) xcb_free_gc (surface->dpy, surface->gc); free (surface->clip_rects); + cairo_region_destroy (surface->clip_region); + + _cairo_freepool_fini (&surface->cookie_pool); surface->dpy = NULL; @@ -329,12 +463,15 @@ _get_image_surface (cairo_xcb_surface_t *surface, if (surface->use_pixmap == 0) { xcb_generic_error_t *error; - imagerep = xcb_get_image_reply(surface->dpy, - xcb_get_image(surface->dpy, XCB_IMAGE_FORMAT_Z_PIXMAP, - surface->drawable, - extents.x, extents.y, - extents.width, extents.height, - AllPlanes), &error); + + imagerep = xcb_get_image_reply (surface->dpy, + xcb_get_image (surface->dpy, + XCB_IMAGE_FORMAT_Z_PIXMAP, + surface->drawable, + extents.x, extents.y, + extents.width, extents.height, + AllPlanes), + &error); /* If we get an error, the surface must have been a window, * so retry with the safe code path. @@ -357,27 +494,48 @@ _get_image_surface (cairo_xcb_surface_t *surface, * temporary pixmap */ xcb_pixmap_t pixmap; + cairo_xcb_cookie_t *cookies[2]; + cairo_status_t status; + + status = _cairo_xcb_surface_ensure_gc (surface); + if (unlikely (status)) + return status; + + status = _cairo_freepool_alloc_array (&surface->cookie_pool, + ARRAY_LENGTH (cookies), + (void **) cookies); + if (unlikely (status)) + return status; + pixmap = xcb_generate_id (surface->dpy); - xcb_create_pixmap (surface->dpy, - surface->depth, - pixmap, - surface->drawable, - extents.width, extents.height); - _cairo_xcb_surface_ensure_gc (surface); + cookies[0]->xcb = xcb_create_pixmap_checked (surface->dpy, + surface->depth, + pixmap, + surface->drawable, + extents.width, extents.height); + cairo_list_add_tail (&cookies[0]->link, &surface->to_be_checked); - xcb_copy_area (surface->dpy, surface->drawable, pixmap, surface->gc, - extents.x, extents.y, 0, 0, extents.width, extents.height); + cookies[1]->xcb = xcb_copy_area_checked (surface->dpy, + surface->drawable, + pixmap, surface->gc, + extents.x, extents.y, + 0, 0, + extents.width, extents.height); + cairo_list_add_tail (&cookies[1]->link, &surface->to_be_checked); + + imagerep = xcb_get_image_reply (surface->dpy, + xcb_get_image (surface->dpy, + XCB_IMAGE_FORMAT_Z_PIXMAP, + pixmap, + extents.x, extents.y, + extents.width, extents.height, + AllPlanes), + 0); - imagerep = xcb_get_image_reply(surface->dpy, - xcb_get_image(surface->dpy, XCB_IMAGE_FORMAT_Z_PIXMAP, - pixmap, - extents.x, extents.y, - extents.width, extents.height, - AllPlanes), 0); xcb_free_pixmap (surface->dpy, pixmap); } - if (!imagerep) + if (unlikely (imagerep == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); bpp = _bits_per_pixel(surface->dpy, imagerep->depth); @@ -462,63 +620,57 @@ _get_image_surface (cairo_xcb_surface_t *surface, return _cairo_error (CAIRO_STATUS_NO_MEMORY); } -static void +static cairo_status_t _cairo_xcb_surface_ensure_src_picture (cairo_xcb_surface_t *surface) { if (!surface->src_picture) { - surface->src_picture = xcb_generate_id(surface->dpy); - xcb_render_create_picture (surface->dpy, - surface->src_picture, - surface->drawable, - surface->xrender_format.id, - 0, NULL); + xcb_void_cookie_t cookie; + + surface->src_picture = xcb_generate_id (surface->dpy); + cookie = xcb_render_create_picture_checked (surface->dpy, + surface->src_picture, + surface->drawable, + surface->xrender_format.id, + 0, NULL); + + return _cairo_xcb_add_cookie_to_be_checked (surface, cookie); } + + return CAIRO_STATUS_SUCCESS; } -static void -_cairo_xcb_surface_set_picture_clip_rects (cairo_xcb_surface_t *surface) -{ - if (surface->have_clip_rects) - xcb_render_set_picture_clip_rectangles (surface->dpy, surface->dst_picture, - 0, 0, - surface->num_clip_rects, - surface->clip_rects); -} - -static void -_cairo_xcb_surface_set_gc_clip_rects (cairo_xcb_surface_t *surface) -{ - if (surface->have_clip_rects) - xcb_set_clip_rectangles(surface->dpy, XCB_CLIP_ORDERING_YX_SORTED, surface->gc, - 0, 0, - surface->num_clip_rects, - surface->clip_rects ); -} - -static void +static cairo_status_t _cairo_xcb_surface_ensure_dst_picture (cairo_xcb_surface_t *surface) { if (!surface->dst_picture) { - surface->dst_picture = xcb_generate_id(surface->dpy); - xcb_render_create_picture (surface->dpy, - surface->dst_picture, - surface->drawable, - surface->xrender_format.id, - 0, NULL); - _cairo_xcb_surface_set_picture_clip_rects (surface); + xcb_void_cookie_t cookie; + + surface->dst_picture = xcb_generate_id (surface->dpy); + cookie = xcb_render_create_picture_checked (surface->dpy, + surface->dst_picture, + surface->drawable, + surface->xrender_format.id, + 0, NULL); + + return _cairo_xcb_add_cookie_to_be_checked (surface, cookie); } + return CAIRO_STATUS_SUCCESS; } -static void +static cairo_status_t _cairo_xcb_surface_ensure_gc (cairo_xcb_surface_t *surface) { + xcb_void_cookie_t cookie; + if (surface->gc) - return; + return CAIRO_STATUS_SUCCESS; surface->gc = xcb_generate_id(surface->dpy); - xcb_create_gc (surface->dpy, surface->gc, surface->drawable, 0, 0); - _cairo_xcb_surface_set_gc_clip_rects(surface); + cookie = xcb_create_gc_checked (surface->dpy, surface->gc, surface->drawable, 0, 0); + _cairo_xcb_surface_set_gc_clip_rects (surface); + + return _cairo_xcb_add_cookie_to_be_checked (surface, cookie); } static cairo_status_t @@ -534,6 +686,7 @@ _draw_image_surface (cairo_xcb_surface_t *surface, int bpp, bpl; uint32_t data_len; uint8_t *data, left_pad=0; + xcb_void_cookie_t cookie; /* equivalent of XPutImage(..., src_x,src_y, dst_x,dst_y, width,height); */ /* XXX: assumes image and surface formats and depths are the same */ @@ -581,17 +734,17 @@ _draw_image_surface (cairo_xcb_surface_t *surface, } } _cairo_xcb_surface_ensure_gc (surface); - xcb_put_image (surface->dpy, XCB_IMAGE_FORMAT_Z_PIXMAP, - surface->drawable, surface->gc, - width, height, - dst_x, dst_y, - left_pad, image->depth, - data_len, data); + cookie = xcb_put_image_checked (surface->dpy, XCB_IMAGE_FORMAT_Z_PIXMAP, + surface->drawable, surface->gc, + width, height, + dst_x, dst_y, + left_pad, image->depth, + data_len, data); if (data < image->data || data >= image->data + image->height * bpl) - free(data); + free (data); - return CAIRO_STATUS_SUCCESS; + return _cairo_xcb_add_cookie_to_be_checked (surface, cookie); } static cairo_status_t @@ -748,9 +901,7 @@ _cairo_xcb_surface_set_matrix (cairo_xcb_surface_t *surface, cairo_matrix_t *matrix) { xcb_render_transform_t xtransform; - - if (!surface->src_picture) - return CAIRO_STATUS_SUCCESS; + xcb_void_cookie_t cookie; xtransform.matrix11 = _cairo_fixed_16_16_from_double (matrix->xx); xtransform.matrix12 = _cairo_fixed_16_16_from_double (matrix->xy); @@ -778,9 +929,10 @@ _cairo_xcb_surface_set_matrix (cairo_xcb_surface_t *surface, return CAIRO_INT_STATUS_UNSUPPORTED; } - xcb_render_set_picture_transform (surface->dpy, surface->src_picture, xtransform); - - return CAIRO_STATUS_SUCCESS; + cookie = xcb_render_set_picture_transform_checked (surface->dpy, + surface->src_picture, + xtransform); + return _cairo_xcb_add_cookie_to_be_checked (surface, cookie); } static cairo_status_t @@ -788,9 +940,7 @@ _cairo_xcb_surface_set_filter (cairo_xcb_surface_t *surface, cairo_filter_t filter) { const char *render_filter; - - if (!surface->src_picture) - return CAIRO_STATUS_SUCCESS; + xcb_void_cookie_t cookie; if (!CAIRO_SURFACE_RENDER_HAS_FILTERS (surface)) { @@ -822,24 +972,50 @@ _cairo_xcb_surface_set_filter (cairo_xcb_surface_t *surface, break; } - xcb_render_set_picture_filter(surface->dpy, surface->src_picture, - strlen(render_filter), render_filter, 0, NULL); + cookie = xcb_render_set_picture_filter_checked (surface->dpy, surface->src_picture, + strlen(render_filter), render_filter, + 0, NULL); - return CAIRO_STATUS_SUCCESS; + return _cairo_xcb_add_cookie_to_be_checked (surface, cookie); } static cairo_status_t -_cairo_xcb_surface_set_repeat (cairo_xcb_surface_t *surface, int repeat) +_cairo_xcb_surface_set_repeat (cairo_xcb_surface_t *surface, cairo_extend_t extend) { uint32_t mask = XCB_RENDER_CP_REPEAT; - uint32_t pa[] = { repeat }; + uint32_t pa[1]; + xcb_void_cookie_t cookie; - if (!surface->src_picture) - return CAIRO_STATUS_SUCCESS; + switch (extend) { + case CAIRO_EXTEND_NONE: + pa[0] = XCB_RENDER_REPEAT_NONE; + break; - xcb_render_change_picture (surface->dpy, surface->src_picture, mask, pa); + case CAIRO_EXTEND_REPEAT: + pa[0] = XCB_RENDER_REPEAT_NORMAL; + break; - return CAIRO_STATUS_SUCCESS; + case CAIRO_EXTEND_REFLECT: + if (!CAIRO_SURFACE_RENDER_HAS_REPEAT_REFLECT(surface)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + pa[0] = XCB_RENDER_REPEAT_REFLECT; + break; + + case CAIRO_EXTEND_PAD: + if (!CAIRO_SURFACE_RENDER_HAS_REPEAT_PAD(surface)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + pa[0] = XCB_RENDER_REPEAT_PAD; + break; + + default: + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cookie = xcb_render_change_picture_checked (surface->dpy, surface->src_picture, + mask, pa); + return _cairo_xcb_add_cookie_to_be_checked (surface, cookie); } static cairo_int_status_t @@ -848,32 +1024,17 @@ _cairo_xcb_surface_set_attributes (cairo_xcb_surface_t *surface, { cairo_int_status_t status; - _cairo_xcb_surface_ensure_src_picture (surface); + status = _cairo_xcb_surface_ensure_src_picture (surface); + if (status) + return status; status = _cairo_xcb_surface_set_matrix (surface, &attributes->matrix); if (status) return status; - switch (attributes->extend) { - case CAIRO_EXTEND_NONE: - _cairo_xcb_surface_set_repeat (surface, XCB_RENDER_REPEAT_NONE); - break; - case CAIRO_EXTEND_REPEAT: - _cairo_xcb_surface_set_repeat (surface, XCB_RENDER_REPEAT_NORMAL); - break; - case CAIRO_EXTEND_REFLECT: - if (!CAIRO_SURFACE_RENDER_HAS_REPEAT_REFLECT(surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; - _cairo_xcb_surface_set_repeat (surface, XCB_RENDER_REPEAT_REFLECT); - break; - case CAIRO_EXTEND_PAD: - if (!CAIRO_SURFACE_RENDER_HAS_REPEAT_PAD(surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; - _cairo_xcb_surface_set_repeat (surface, XCB_RENDER_REPEAT_PAD); - break; - default: - return CAIRO_INT_STATUS_UNSUPPORTED; - } + status = _cairo_xcb_surface_set_repeat (surface, attributes->extend); + if (status) + return status; status = _cairo_xcb_surface_set_filter (surface, attributes->filter); if (status) @@ -1124,7 +1285,8 @@ _cairo_xcb_surface_composite (cairo_operator_t op, int dst_x, int dst_y, unsigned int width, - unsigned int height) + unsigned int height, + cairo_region_t *clip_region) { cairo_surface_attributes_t src_attr, mask_attr; cairo_xcb_surface_t *dst = abstract_dst; @@ -1134,6 +1296,7 @@ _cairo_xcb_surface_composite (cairo_operator_t op, composite_operation_t operation; int itx, ity; cairo_bool_t is_integer_translation; + xcb_void_cookie_t cookie; if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -1145,7 +1308,6 @@ _cairo_xcb_surface_composite (cairo_operator_t op, status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern, &dst->base, - CAIRO_CONTENT_COLOR_ALPHA, src_x, src_y, mask_x, mask_y, width, height, @@ -1163,6 +1325,10 @@ _cairo_xcb_surface_composite (cairo_operator_t op, goto BAIL; } + status = _cairo_xcb_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + goto BAIL; + status = _cairo_xcb_surface_set_attributes (src, &src_attr); if (status) goto BAIL; @@ -1170,49 +1336,55 @@ _cairo_xcb_surface_composite (cairo_operator_t op, switch (operation) { case DO_RENDER: - _cairo_xcb_surface_ensure_dst_picture (dst); + status = _cairo_xcb_surface_ensure_dst_picture (dst); + if (unlikely (status)) + goto BAIL; + if (mask) { status = _cairo_xcb_surface_set_attributes (mask, &mask_attr); - if (status) + if (unlikely (status)) goto BAIL; - xcb_render_composite (dst->dpy, - _render_operator (op), - src->src_picture, - mask->src_picture, - dst->dst_picture, - src_x + src_attr.x_offset, - src_y + src_attr.y_offset, - mask_x + mask_attr.x_offset, - mask_y + mask_attr.y_offset, - dst_x, dst_y, - width, height); + cookie = xcb_render_composite_checked (dst->dpy, + _render_operator (op), + src->src_picture, + mask->src_picture, + dst->dst_picture, + src_x + src_attr.x_offset, + src_y + src_attr.y_offset, + mask_x + mask_attr.x_offset, + mask_y + mask_attr.y_offset, + dst_x, dst_y, + width, height); } else { static xcb_render_picture_t maskpict = { XCB_NONE }; - xcb_render_composite (dst->dpy, - _render_operator (op), - src->src_picture, - maskpict, - dst->dst_picture, - src_x + src_attr.x_offset, - src_y + src_attr.y_offset, - 0, 0, - dst_x, dst_y, - width, height); + cookie = xcb_render_composite_checked (dst->dpy, + _render_operator (op), + src->src_picture, + maskpict, + dst->dst_picture, + src_x + src_attr.x_offset, + src_y + src_attr.y_offset, + 0, 0, + dst_x, dst_y, + width, height); } break; case DO_XCOPYAREA: - _cairo_xcb_surface_ensure_gc (dst); - xcb_copy_area (dst->dpy, - src->drawable, - dst->drawable, - dst->gc, - src_x + src_attr.x_offset, - src_y + src_attr.y_offset, - dst_x, dst_y, - width, height); + status = _cairo_xcb_surface_ensure_gc (dst); + if (unlikely (status)) + return status; + + cookie = xcb_copy_area_checked (dst->dpy, + src->drawable, + dst->drawable, + dst->gc, + src_x + src_attr.x_offset, + src_y + src_attr.y_offset, + dst_x, dst_y, + width, height); break; case DO_XTILE: @@ -1224,7 +1396,10 @@ _cairo_xcb_surface_composite (cairo_operator_t op, * _recategorize_composite_operation. */ - _cairo_xcb_surface_ensure_gc (dst); + status = _cairo_xcb_surface_ensure_gc (dst); + if (unlikely (status)) + return status; + is_integer_translation = _cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity); assert (is_integer_translation == TRUE); @@ -1240,7 +1415,10 @@ _cairo_xcb_surface_composite (cairo_operator_t op, xcb_rectangle_t rect = { dst_x, dst_y, width, height }; xcb_change_gc( dst->dpy, dst->gc, mask, values ); - xcb_poly_fill_rectangle(dst->dpy, dst->drawable, dst->gc, 1, &rect); + cookie = xcb_poly_fill_rectangle_checked (dst->dpy, + dst->drawable, + dst->gc, + 1, &rect); } break; @@ -1249,7 +1427,11 @@ _cairo_xcb_surface_composite (cairo_operator_t op, ASSERT_NOT_REACHED; } - if (!_cairo_operator_bounded_by_source (op)) + status = _cairo_xcb_add_cookie_to_be_checked (dst, cookie); + if (unlikely (status)) + goto BAIL; + + if (!_cairo_operator_bounded_by_source (op)) { status = _cairo_surface_composite_fixup_unbounded (&dst->base, &src_attr, src->width, src->height, mask ? &mask_attr : NULL, @@ -1257,7 +1439,9 @@ _cairo_xcb_surface_composite (cairo_operator_t op, mask ? mask->height : 0, src_x, src_y, mask_x, mask_y, - dst_x, dst_y, width, height); + dst_x, dst_y, width, height, + clip_region); + } BAIL: if (mask) @@ -1279,6 +1463,8 @@ _cairo_xcb_surface_fill_rectangles (void *abstract_surface, xcb_render_color_t render_color; xcb_rectangle_t static_xrects[16]; xcb_rectangle_t *xrects = static_xrects; + cairo_status_t status; + xcb_void_cookie_t cookie; int i; if (!CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLE (surface)) @@ -1289,6 +1475,9 @@ _cairo_xcb_surface_fill_rectangles (void *abstract_surface, render_color.blue = color->blue_short; render_color.alpha = color->alpha_short; + status = _cairo_xcb_surface_set_clip_region (surface, NULL); + assert (status == CAIRO_STATUS_SUCCESS); + if (num_rects > ARRAY_LENGTH(static_xrects)) { xrects = _cairo_malloc_ab (num_rects, sizeof(xcb_rectangle_t)); if (xrects == NULL) @@ -1302,45 +1491,74 @@ _cairo_xcb_surface_fill_rectangles (void *abstract_surface, xrects[i].height = rects[i].height; } - _cairo_xcb_surface_ensure_dst_picture (surface); - xcb_render_fill_rectangles (surface->dpy, - _render_operator (op), - surface->dst_picture, - render_color, num_rects, xrects); + status = _cairo_xcb_surface_ensure_dst_picture (surface); + if (unlikely (status)) { + if (xrects != static_xrects) + free (xrects); + return status; + } + + cookie = xcb_render_fill_rectangles_checked (surface->dpy, + _render_operator (op), + surface->dst_picture, + render_color, num_rects, xrects); if (xrects != static_xrects) - free(xrects); + free (xrects); - return CAIRO_STATUS_SUCCESS; + return _cairo_xcb_add_cookie_to_be_checked (surface, cookie); } /* Creates an A8 picture of size @width x @height, initialized with @color */ -static xcb_render_picture_t +static cairo_status_t _create_a8_picture (cairo_xcb_surface_t *surface, xcb_render_color_t *color, int width, int height, - cairo_bool_t repeat) + cairo_bool_t repeat, + xcb_render_picture_t *out) { uint32_t values[] = { TRUE }; uint32_t mask = repeat ? XCB_RENDER_CP_REPEAT : 0; - xcb_pixmap_t pixmap = xcb_generate_id (surface->dpy); - xcb_render_picture_t picture = xcb_generate_id (surface->dpy); - - xcb_render_pictforminfo_t *format - = _CAIRO_FORMAT_TO_XRENDER_FORMAT (surface->dpy, CAIRO_FORMAT_A8); + xcb_pixmap_t pixmap; + xcb_render_picture_t picture; + xcb_render_pictforminfo_t *format; xcb_rectangle_t rect = { 0, 0, width, height }; - xcb_create_pixmap (surface->dpy, 8, pixmap, surface->drawable, - width <= 0 ? 1 : width, - height <= 0 ? 1 : height); - xcb_render_create_picture (surface->dpy, picture, pixmap, format->id, mask, values); - xcb_render_fill_rectangles (surface->dpy, XCB_RENDER_PICT_OP_SRC, picture, *color, 1, &rect); + cairo_xcb_cookie_t *cookie[3]; + cairo_status_t status; + + status = _cairo_freepool_alloc_array (&surface->cookie_pool, + ARRAY_LENGTH (cookie), + (void **) cookie); + if (unlikely (status)) + return status; + + pixmap = xcb_generate_id (surface->dpy); + picture = xcb_generate_id (surface->dpy); + + cookie[0]->xcb = xcb_create_pixmap_checked (surface->dpy, 8, pixmap, surface->drawable, + width <= 0 ? 1 : width, + height <= 0 ? 1 : height); + cairo_list_add_tail (&cookie[0]->link, &surface->to_be_checked); + + format = _CAIRO_FORMAT_TO_XRENDER_FORMAT (surface->dpy, CAIRO_FORMAT_A8); + cookie[1]->xcb = xcb_render_create_picture_checked (surface->dpy, + picture, pixmap, format->id, + mask, values); + cairo_list_add_tail (&cookie[1]->link, &surface->to_be_checked); + + cookie[2]->xcb = xcb_render_fill_rectangles_checked (surface->dpy, + XCB_RENDER_PICT_OP_SRC, + picture, *color, 1, &rect); + cairo_list_add_tail (&cookie[2]->link, &surface->to_be_checked); + xcb_free_pixmap (surface->dpy, pixmap); - return picture; + *out = picture; + return CAIRO_STATUS_SUCCESS; } /* Creates a temporary mask for the trapezoids covering the area @@ -1361,6 +1579,8 @@ _create_trapezoid_mask (cairo_xcb_surface_t *dst, xcb_render_color_t solid = { 0xffff, 0xffff, 0xffff, 0xffff }; xcb_render_picture_t mask_picture, solid_picture; xcb_render_trapezoid_t *offset_traps; + xcb_void_cookie_t cookie; + cairo_status_t status; int i; /* This would be considerably simpler using XRenderAddTraps(), but since @@ -1370,12 +1590,22 @@ _create_trapezoid_mask (cairo_xcb_surface_t *dst, * optimization that avoids creating another intermediate surface on * the servers that have XRenderAddTraps(). */ - mask_picture = _create_a8_picture (dst, &transparent, width, height, FALSE); - solid_picture = _create_a8_picture (dst, &solid, width, height, TRUE); + status = _create_a8_picture (dst, &transparent, width, height, FALSE, &mask_picture); + if (unlikely (status)) + return status; + + status = _create_a8_picture (dst, &solid, 1, 1, TRUE, &solid_picture); + if (unlikely (status)) { + xcb_render_free_picture (dst->dpy, mask_picture); + return status; + } offset_traps = _cairo_malloc_ab (num_traps, sizeof (xcb_render_trapezoid_t)); - if (offset_traps == NULL) + if (offset_traps == NULL) { + xcb_render_free_picture (dst->dpy, solid_picture); + xcb_render_free_picture (dst->dpy, mask_picture); return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } for (i = 0; i < num_traps; i++) { offset_traps[i].top = _cairo_fixed_to_16_16(traps[i].top) - 0x10000 * dst_y; @@ -1390,15 +1620,21 @@ _create_trapezoid_mask (cairo_xcb_surface_t *dst, offset_traps[i].right.p2.y = _cairo_fixed_to_16_16(traps[i].right.p2.y) - 0x10000 * dst_y; } - xcb_render_trapezoids (dst->dpy, XCB_RENDER_PICT_OP_ADD, - solid_picture, mask_picture, - pict_format->id, - 0, 0, - num_traps, offset_traps); + cookie = xcb_render_trapezoids_checked (dst->dpy, XCB_RENDER_PICT_OP_ADD, + solid_picture, mask_picture, + pict_format->id, + 0, 0, + num_traps, offset_traps); xcb_render_free_picture (dst->dpy, solid_picture); free (offset_traps); + status = _cairo_xcb_add_cookie_to_be_checked (dst, cookie); + if (unlikely (status)) { + xcb_render_free_picture (dst->dpy, mask_picture); + return status; + } + *mask_picture_out = mask_picture; return CAIRO_STATUS_SUCCESS; } @@ -1415,7 +1651,8 @@ _cairo_xcb_surface_composite_trapezoids (cairo_operator_t op, unsigned int width, unsigned int height, cairo_trapezoid_t *traps, - int num_traps) + int num_traps, + cairo_region_t *clip_region) { cairo_surface_attributes_t attributes; cairo_xcb_surface_t *dst = abstract_dst; @@ -1435,7 +1672,6 @@ _cairo_xcb_surface_composite_trapezoids (cairo_operator_t op, return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_pattern_acquire_surface (pattern, &dst->base, - CAIRO_CONTENT_COLOR_ALPHA, src_x, src_y, width, height, CAIRO_PATTERN_ACQUIRE_NO_REFLECT, (cairo_surface_t **) &src, @@ -1474,12 +1710,21 @@ _cairo_xcb_surface_composite_trapezoids (cairo_operator_t op, render_src_x = src_x + render_reference_x - dst_x; render_src_y = src_y + render_reference_y - dst_y; - _cairo_xcb_surface_ensure_dst_picture (dst); + status = _cairo_xcb_surface_ensure_dst_picture (dst); + if (unlikely (status)) + goto BAIL; + + status = _cairo_xcb_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + goto BAIL; + status = _cairo_xcb_surface_set_attributes (src, &attributes); if (status) goto BAIL; if (!_cairo_operator_bounded_by_mask (op)) { + xcb_void_cookie_t cookie; + /* xcb_render_composite+trapezoids() creates a mask only large enough for the * trapezoids themselves, but if the operator is unbounded, then we need * to actually composite all the way out to the bounds, so we create @@ -1498,29 +1743,34 @@ _cairo_xcb_surface_composite_trapezoids (cairo_operator_t op, if (status) goto BAIL; - xcb_render_composite (dst->dpy, - _render_operator (op), - src->src_picture, - mask_picture, - dst->dst_picture, - src_x + attributes.x_offset, - src_y + attributes.y_offset, - 0, 0, - dst_x, dst_y, - width, height); - + cookie = xcb_render_composite_checked (dst->dpy, + _render_operator (op), + src->src_picture, + mask_picture, + dst->dst_picture, + src_x + attributes.x_offset, + src_y + attributes.y_offset, + 0, 0, + dst_x, dst_y, + width, height); xcb_render_free_picture (dst->dpy, mask_picture); + status = _cairo_xcb_add_cookie_to_be_checked (dst, cookie); + if (unlikely (status)) + goto BAIL; + status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base, &attributes, src->width, src->height, width, height, src_x, src_y, 0, 0, - dst_x, dst_y, width, height); + dst_x, dst_y, width, height, + clip_region); } else { - xcb_render_trapezoid_t xtraps_stack[16]; + xcb_render_trapezoid_t xtraps_stack[CAIRO_STACK_ARRAY_LENGTH (xcb_render_trapezoid_t)]; xcb_render_trapezoid_t *xtraps = xtraps_stack; + xcb_void_cookie_t cookie; int i; if (num_traps > ARRAY_LENGTH(xtraps_stack)) { @@ -1544,16 +1794,18 @@ _cairo_xcb_surface_composite_trapezoids (cairo_operator_t op, xtraps[i].right.p2.y = _cairo_fixed_to_16_16(traps[i].right.p2.y); } - xcb_render_trapezoids (dst->dpy, - _render_operator (op), - src->src_picture, dst->dst_picture, - render_format->id, - render_src_x + attributes.x_offset, - render_src_y + attributes.y_offset, - num_traps, xtraps); + cookie = xcb_render_trapezoids_checked (dst->dpy, + _render_operator (op), + src->src_picture, dst->dst_picture, + render_format->id, + render_src_x + attributes.x_offset, + render_src_y + attributes.y_offset, + num_traps, xtraps); if (xtraps != xtraps_stack) - free(xtraps); + free (xtraps); + + status = _cairo_xcb_add_cookie_to_be_checked (dst, cookie); } BAIL: @@ -1562,68 +1814,7 @@ _cairo_xcb_surface_composite_trapezoids (cairo_operator_t op, return status; } -static cairo_int_status_t -_cairo_xcb_surface_set_clip_region (void *abstract_surface, - cairo_region_t *region) -{ - cairo_xcb_surface_t *surface = abstract_surface; - - if (surface->clip_rects) { - free (surface->clip_rects); - surface->clip_rects = NULL; - } - - surface->have_clip_rects = FALSE; - surface->num_clip_rects = 0; - - if (region == NULL) { - uint32_t none[] = { XCB_NONE }; - if (surface->gc) - xcb_change_gc (surface->dpy, surface->gc, XCB_GC_CLIP_MASK, none); - - if (surface->xrender_format.id != XCB_NONE && surface->dst_picture) - xcb_render_change_picture (surface->dpy, surface->dst_picture, - XCB_RENDER_CP_CLIP_MASK, none); - } else { - xcb_rectangle_t *rects = NULL; - int n_rects, i; - - n_rects = cairo_region_num_rectangles (region); - - if (n_rects > 0) { - rects = _cairo_malloc_ab (n_rects, sizeof(xcb_rectangle_t)); - if (rects == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } else { - rects = NULL; - } - - for (i = 0; i < n_rects; i++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (region, i, &rect); - - rects[i].x = rect.x; - rects[i].y = rect.y; - rects[i].width = rect.width; - rects[i].height = rect.height; - } - - surface->have_clip_rects = TRUE; - surface->clip_rects = rects; - surface->num_clip_rects = n_rects; - - if (surface->gc) - _cairo_xcb_surface_set_gc_clip_rects (surface); - - if (surface->dst_picture) - _cairo_xcb_surface_set_picture_clip_rects (surface); - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t +static cairo_bool_t _cairo_xcb_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { @@ -1635,7 +1826,42 @@ _cairo_xcb_surface_get_extents (void *abstract_surface, rectangle->width = surface->width; rectangle->height = surface->height; - return CAIRO_STATUS_SUCCESS; + return TRUE; +} + +static cairo_status_t +_cairo_xcb_surface_flush (void *abstract_surface) +{ + cairo_xcb_surface_t *surface = abstract_surface; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + while (! cairo_list_is_empty (&surface->to_be_checked)) { + cairo_xcb_cookie_t *cookie; + xcb_generic_error_t *error; + + cookie = cairo_list_first_entry (&surface->to_be_checked, + cairo_xcb_cookie_t, + link); + + error = xcb_request_check (surface->dpy, cookie->xcb); + if (error != NULL) { +#if 0 + /* XXX */ + fprintf (stderr, "Delayed error detected: %d, major=%d, minor=%d, seqno=%d\n", + error->error_code, + error->major_code, + error->minor_code, + error->sequence); +#endif + if (status == CAIRO_STATUS_SUCCESS) + status = _cairo_error (CAIRO_STATUS_WRITE_ERROR); /* XXX CAIRO_STATUS_CONNECTION_ERROR */ + } + + cairo_list_del (&cookie->link); + _cairo_freepool_free (&surface->cookie_pool, cookie); + } + + return status; } /* XXX: _cairo_xcb_surface_get_font_options */ @@ -1654,8 +1880,8 @@ _cairo_xcb_surface_show_glyphs (void *abstract_dst, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip, + int *remaining_glyphs); static cairo_bool_t _cairo_xcb_surface_is_similar (void *surface_a, @@ -1682,44 +1908,33 @@ _cairo_xcb_surface_is_similar (void *surface_a, return a->xrender_format.id == xrender_format->id; } -static cairo_status_t -_cairo_xcb_surface_reset (void *abstract_surface) -{ - cairo_xcb_surface_t *surface = abstract_surface; - cairo_status_t status; - - status = _cairo_xcb_surface_set_clip_region (surface, NULL); - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; -} - - /* XXX: move this to the bottom of the file, XCB and Xlib */ static const cairo_surface_backend_t cairo_xcb_surface_backend = { CAIRO_SURFACE_TYPE_XCB, + _cairo_xcb_surface_create_similar, _cairo_xcb_surface_finish, _cairo_xcb_surface_acquire_source_image, _cairo_xcb_surface_release_source_image, + _cairo_xcb_surface_acquire_dest_image, _cairo_xcb_surface_release_dest_image, + _cairo_xcb_surface_clone_similar, _cairo_xcb_surface_composite, _cairo_xcb_surface_fill_rectangles, _cairo_xcb_surface_composite_trapezoids, NULL, /* create_span_renderer */ NULL, /* check_span_renderer */ + NULL, /* copy_page */ NULL, /* show_page */ - _cairo_xcb_surface_set_clip_region, - NULL, /* intersect_clip_path */ + _cairo_xcb_surface_get_extents, NULL, /* old_show_glyphs */ NULL, /* get_font_options */ - NULL, /* flush */ + _cairo_xcb_surface_flush, NULL, /* mark_dirty_rectangle */ _cairo_xcb_surface_scaled_font_fini, _cairo_xcb_surface_scaled_glyph_fini, @@ -1733,8 +1948,6 @@ static const cairo_surface_backend_t cairo_xcb_surface_backend = { _cairo_xcb_surface_snapshot, _cairo_xcb_surface_is_similar, - - _cairo_xcb_surface_reset }; /** @@ -1853,8 +2066,13 @@ _cairo_xcb_surface_create_internal (xcb_connection_t *dpy, surface->have_clip_rects = FALSE; surface->clip_rects = NULL; surface->num_clip_rects = 0; + surface->clip_region = NULL; - return (cairo_surface_t *) surface; + cairo_list_init (&surface->to_be_checked); + _cairo_freepool_init (&surface->cookie_pool, + sizeof (cairo_xcb_cookie_t)); + + return &surface->base; } static xcb_screen_t * @@ -2037,10 +2255,15 @@ _cairo_xcb_surface_font_init (xcb_connection_t *dpy, font_private->format = format; font_private->xrender_format = _CAIRO_FORMAT_TO_XRENDER_FORMAT(dpy, format); font_private->glyphset = xcb_generate_id(dpy); - xcb_render_create_glyph_set (dpy, font_private->glyphset, font_private->xrender_format->id); + + /* XXX checking, adding to CloseDisplay */ + xcb_render_create_glyph_set (dpy, + font_private->glyphset, + font_private->xrender_format->id); scaled_font->surface_private = font_private; scaled_font->surface_backend = &cairo_xcb_surface_backend; + return CAIRO_STATUS_SUCCESS; } @@ -2064,8 +2287,8 @@ _cairo_xcb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, if (font_private != NULL && scaled_glyph->surface_private != NULL) { xcb_render_glyph_t glyph_index = _cairo_scaled_glyph_index(scaled_glyph); xcb_render_free_glyphs (font_private->dpy, - font_private->glyphset, - 1, &glyph_index); + font_private->glyphset, + 1, &glyph_index); } } @@ -2078,9 +2301,9 @@ _native_byte_order_lsb (void) } static cairo_status_t -_cairo_xcb_surface_add_glyph (xcb_connection_t *dpy, - cairo_scaled_font_t *scaled_font, - cairo_scaled_glyph_t *scaled_glyph) +_cairo_xcb_surface_add_glyph (cairo_xcb_surface_t *dst, + cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) { xcb_render_glyphinfo_t glyph_info; xcb_render_glyph_t glyph_index; @@ -2088,10 +2311,11 @@ _cairo_xcb_surface_add_glyph (xcb_connection_t *dpy, cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_xcb_surface_font_private_t *font_private; cairo_image_surface_t *glyph_surface = scaled_glyph->surface; + xcb_void_cookie_t cookie; if (scaled_font->surface_private == NULL) { - status = _cairo_xcb_surface_font_init (dpy, scaled_font, - glyph_surface->format); + status = _cairo_xcb_surface_font_init (dst->dpy, scaled_font, + glyph_surface->format); if (status) return status; } @@ -2102,29 +2326,28 @@ _cairo_xcb_surface_add_glyph (xcb_connection_t *dpy, * format. */ if (glyph_surface->format != font_private->format) { - cairo_t *cr; + cairo_surface_pattern_t pattern; cairo_surface_t *tmp_surface; - double x_offset, y_offset; tmp_surface = cairo_image_surface_create (font_private->format, glyph_surface->width, glyph_surface->height); - cr = cairo_create (tmp_surface); - cairo_surface_get_device_offset (&glyph_surface->base, &x_offset, &y_offset); - cairo_set_source_surface (cr, &glyph_surface->base, x_offset, y_offset); - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_paint (cr); - - status = cairo_status (cr); - - cairo_destroy (cr); + status = tmp_surface->status; + if (unlikely (status)) + goto BAIL; tmp_surface->device_transform = glyph_surface->base.device_transform; tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; + _cairo_pattern_init_for_surface (&pattern, &glyph_surface->base); + status = _cairo_surface_paint (tmp_surface, + CAIRO_OPERATOR_SOURCE, &pattern.base, + NULL); + _cairo_pattern_fini (&pattern.base); + glyph_surface = (cairo_image_surface_t *) tmp_surface; - if (status) + if (unlikely (status)) goto BAIL; } @@ -2142,7 +2365,7 @@ _cairo_xcb_surface_add_glyph (xcb_connection_t *dpy, switch (scaled_glyph->surface->format) { case CAIRO_FORMAT_A1: /* local bitmaps are always stored with bit == byte */ - if (_native_byte_order_lsb() != (xcb_get_setup(dpy)->bitmap_format_bit_order == XCB_IMAGE_ORDER_LSB_FIRST)) { + if (_native_byte_order_lsb() != (xcb_get_setup(dst->dpy)->bitmap_format_bit_order == XCB_IMAGE_ORDER_LSB_FIRST)) { int c = glyph_surface->stride * glyph_surface->height; unsigned char *d; unsigned char *new, *n; @@ -2168,7 +2391,7 @@ _cairo_xcb_surface_add_glyph (xcb_connection_t *dpy, case CAIRO_FORMAT_A8: break; case CAIRO_FORMAT_ARGB32: - if (_native_byte_order_lsb() != (xcb_get_setup(dpy)->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST)) { + if (_native_byte_order_lsb() != (xcb_get_setup(dst->dpy)->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST)) { unsigned int c = glyph_surface->stride * glyph_surface->height; unsigned char *d; unsigned char *new, *n; @@ -2202,14 +2425,16 @@ _cairo_xcb_surface_add_glyph (xcb_connection_t *dpy, glyph_index = _cairo_scaled_glyph_index (scaled_glyph); - xcb_render_add_glyphs (dpy, font_private->glyphset, - 1, &glyph_index, &glyph_info, - glyph_surface->stride * glyph_surface->height, - data); + cookie = xcb_render_add_glyphs_checked (dst->dpy, font_private->glyphset, + 1, &glyph_index, &glyph_info, + glyph_surface->stride * glyph_surface->height, + data); if (data != glyph_surface->data) free (data); + status = _cairo_xcb_add_cookie_to_be_checked (dst, cookie); + BAIL: if (glyph_surface != scaled_glyph->surface) cairo_surface_destroy (&glyph_surface->base); @@ -2230,6 +2455,7 @@ _cairo_xcb_surface_show_glyphs_8 (cairo_xcb_surface_t *dst, { cairo_xcb_surface_font_private_t *font_private = scaled_font->surface_private; xcb_render_util_composite_text_stream_t *stream; + xcb_void_cookie_t cookie; int i; int thisX, thisY; int lastX = 0, lastY = 0; @@ -2246,18 +2472,18 @@ _cairo_xcb_surface_show_glyphs_8 (cairo_xcb_surface_t *dst, lastY = thisY; } - xcb_render_util_composite_text (dst->dpy, - _render_operator (op), - src->src_picture, - dst->dst_picture, - font_private->xrender_format->id, - src_x_offset + _cairo_lround (glyphs[0].x), - src_y_offset + _cairo_lround (glyphs[0].y), - stream); + cookie = xcb_render_util_composite_text_checked (dst->dpy, + _render_operator (op), + src->src_picture, + dst->dst_picture, + font_private->xrender_format->id, + src_x_offset + _cairo_lround (glyphs[0].x), + src_y_offset + _cairo_lround (glyphs[0].y), + stream); xcb_render_util_composite_text_free (stream); - return CAIRO_STATUS_SUCCESS; + return _cairo_xcb_add_cookie_to_be_checked (dst, cookie); } static cairo_status_t @@ -2271,6 +2497,7 @@ _cairo_xcb_surface_show_glyphs_16 (cairo_xcb_surface_t *dst, { cairo_xcb_surface_font_private_t *font_private = scaled_font->surface_private; xcb_render_util_composite_text_stream_t *stream; + xcb_void_cookie_t cookie; int i; int thisX, thisY; int lastX = 0, lastY = 0; @@ -2287,18 +2514,18 @@ _cairo_xcb_surface_show_glyphs_16 (cairo_xcb_surface_t *dst, lastY = thisY; } - xcb_render_util_composite_text (dst->dpy, - _render_operator (op), - src->src_picture, - dst->dst_picture, - font_private->xrender_format->id, - src_x_offset + _cairo_lround (glyphs[0].x), - src_y_offset + _cairo_lround (glyphs[0].y), - stream); + cookie = xcb_render_util_composite_text_checked (dst->dpy, + _render_operator (op), + src->src_picture, + dst->dst_picture, + font_private->xrender_format->id, + src_x_offset + _cairo_lround (glyphs[0].x), + src_y_offset + _cairo_lround (glyphs[0].y), + stream); xcb_render_util_composite_text_free (stream); - return CAIRO_STATUS_SUCCESS; + return _cairo_xcb_add_cookie_to_be_checked (dst, cookie); } static cairo_status_t @@ -2312,6 +2539,7 @@ _cairo_xcb_surface_show_glyphs_32 (cairo_xcb_surface_t *dst, { cairo_xcb_surface_font_private_t *font_private = scaled_font->surface_private; xcb_render_util_composite_text_stream_t *stream; + xcb_void_cookie_t cookie; int i; int thisX, thisY; int lastX = 0, lastY = 0; @@ -2328,18 +2556,18 @@ _cairo_xcb_surface_show_glyphs_32 (cairo_xcb_surface_t *dst, lastY = thisY; } - xcb_render_util_composite_text (dst->dpy, - _render_operator (op), - src->src_picture, - dst->dst_picture, - font_private->xrender_format->id, - src_x_offset + _cairo_lround (glyphs[0].x), - src_y_offset + _cairo_lround (glyphs[0].y), - stream); + cookie = xcb_render_util_composite_text_checked (dst->dpy, + _render_operator (op), + src->src_picture, + dst->dst_picture, + font_private->xrender_format->id, + src_x_offset + _cairo_lround (glyphs[0].x), + src_y_offset + _cairo_lround (glyphs[0].y), + stream); xcb_render_util_composite_text_free (stream); - return CAIRO_STATUS_SUCCESS; + return _cairo_xcb_add_cookie_to_be_checked (dst, cookie); } typedef cairo_status_t (*cairo_xcb_surface_show_glyphs_func_t) @@ -2363,8 +2591,6 @@ _cairo_xcb_surface_owns_font (cairo_xcb_surface_t *dst, return TRUE; } - - static cairo_status_t _cairo_xcb_surface_emit_glyphs (cairo_xcb_surface_t *dst, cairo_glyph_t *glyphs, @@ -2409,14 +2635,18 @@ _cairo_xcb_surface_emit_glyphs (cairo_xcb_surface_t *dst, if (scaled_glyph->surface->width && scaled_glyph->surface->height) { output_glyphs[o++] = glyphs[i]; if (scaled_glyph->surface_private == NULL) { - _cairo_xcb_surface_add_glyph (dst->dpy, scaled_font, scaled_glyph); + _cairo_xcb_surface_add_glyph (dst, scaled_font, scaled_glyph); scaled_glyph->surface_private = (void *) 1; } } } num_glyphs = o; - _cairo_xcb_surface_ensure_dst_picture (dst); + status = _cairo_xcb_surface_ensure_dst_picture (dst); + if (status) { + free (output_glyphs); + return status; + } max_chunk_size = xcb_get_maximum_request_length (dst->dpy); if (max_index < 256) { @@ -2463,8 +2693,8 @@ _cairo_xcb_surface_show_glyphs (void *abstract_dst, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip, + int *remaining_glyphs) { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_xcb_surface_t *dst = abstract_dst; @@ -2474,6 +2704,7 @@ _cairo_xcb_surface_show_glyphs (void *abstract_dst, cairo_xcb_surface_t *src = NULL; cairo_solid_pattern_t solid_pattern; + cairo_region_t *clip_region = NULL; if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT (dst) || dst->xrender_format.id == XCB_NONE) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -2497,10 +2728,12 @@ _cairo_xcb_surface_show_glyphs (void *abstract_dst, * fallback clip masking, we have to go through the full * fallback path. */ - if (dst->base.clip && - (dst->base.clip->mode != CAIRO_CLIP_MODE_REGION || - dst->base.clip->surface != NULL)) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); + if (status) + return status; + } operation = _categorize_composite_operation (dst, op, src_pattern, TRUE); if (operation == DO_UNSUPPORTED) @@ -2531,7 +2764,6 @@ _cairo_xcb_surface_show_glyphs (void *abstract_dst, if (src_pattern->type == CAIRO_PATTERN_TYPE_SOLID) { status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, - CAIRO_CONTENT_COLOR_ALPHA, 0, 0, 1, 1, CAIRO_PATTERN_ACQUIRE_NONE, (cairo_surface_t **) &src, @@ -2542,12 +2774,12 @@ _cairo_xcb_surface_show_glyphs (void *abstract_dst, status = _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs, - &glyph_extents); + &glyph_extents, + NULL); if (status) goto BAIL; status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, - CAIRO_CONTENT_COLOR_ALPHA, glyph_extents.x, glyph_extents.y, glyph_extents.width, glyph_extents.height, CAIRO_PATTERN_ACQUIRE_NO_REFLECT, @@ -2564,6 +2796,10 @@ _cairo_xcb_surface_show_glyphs (void *abstract_dst, goto BAIL; } + status = _cairo_xcb_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + goto BAIL; + status = _cairo_xcb_surface_set_attributes (src, &attributes); if (status) goto BAIL; @@ -2571,7 +2807,7 @@ _cairo_xcb_surface_show_glyphs (void *abstract_dst, /* Send all unsent glyphs to the server, and count the max of the glyph indices */ _cairo_scaled_font_freeze_cache (scaled_font); - if (_cairo_xcb_surface_owns_font (dst, scaled_font)) + if (_cairo_xcb_surface_owns_font (dst, scaled_font)) { status = _cairo_xcb_surface_emit_glyphs (dst, glyphs, num_glyphs, scaled_font, @@ -2579,8 +2815,9 @@ _cairo_xcb_surface_show_glyphs (void *abstract_dst, src, &attributes, remaining_glyphs); - else + } else { status = CAIRO_INT_STATUS_UNSUPPORTED; + } _cairo_scaled_font_thaw_cache (scaled_font); BAIL: diff --git a/gfx/cairo/cairo/src/cairo-xlib-display.c b/gfx/cairo/cairo/src/cairo-xlib-display.c index a789efb01679..566d9fb8bf03 100644 --- a/gfx/cairo/cairo/src/cairo-xlib-display.c +++ b/gfx/cairo/cairo/src/cairo-xlib-display.c @@ -38,8 +38,32 @@ #include "cairo-xlib-private.h" #include "cairo-xlib-xrender-private.h" +#include "cairo-freelist-private.h" + #include /* For XESetCloseDisplay */ +struct _cairo_xlib_display { + cairo_xlib_display_t *next; + cairo_reference_count_t ref_count; + cairo_mutex_t mutex; + + Display *display; + cairo_xlib_screen_t *screens; + + int render_major; + int render_minor; + XRenderPictFormat *cached_xrender_formats[CAIRO_FORMAT_A1 + 1]; + + cairo_xlib_job_t *workqueue; + cairo_freelist_t wq_freelist; + + cairo_xlib_hook_t *close_display_hooks; + unsigned int buggy_gradients :1; + unsigned int buggy_pad_reflect :1; + unsigned int buggy_repeat :1; + unsigned int closed :1; +}; + typedef int (*cairo_xlib_error_func_t) (Display *display, XErrorEvent *event); @@ -71,14 +95,14 @@ _cairo_xlib_remove_close_display_hook_internal (cairo_xlib_display_t *display, static void _cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display) { - cairo_xlib_screen_info_t *screen; + cairo_xlib_screen_t *screen; cairo_xlib_hook_t *hook; /* call all registered shutdown routines */ CAIRO_MUTEX_LOCK (display->mutex); for (screen = display->screens; screen != NULL; screen = screen->next) - _cairo_xlib_screen_info_close_display (screen); + _cairo_xlib_screen_close_display (screen); while (TRUE) { hook = display->close_display_hooks; @@ -99,7 +123,7 @@ _cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display) static void _cairo_xlib_display_discard_screens (cairo_xlib_display_t *display) { - cairo_xlib_screen_info_t *screens; + cairo_xlib_screen_t *screens; CAIRO_MUTEX_LOCK (display->mutex); screens = display->screens; @@ -107,10 +131,10 @@ _cairo_xlib_display_discard_screens (cairo_xlib_display_t *display) CAIRO_MUTEX_UNLOCK (display->mutex); while (screens != NULL) { - cairo_xlib_screen_info_t *screen = screens; + cairo_xlib_screen_t *screen = screens; screens = screen->next; - _cairo_xlib_screen_info_destroy (screen); + _cairo_xlib_screen_destroy (screen); } } @@ -213,7 +237,7 @@ _cairo_xlib_display_get (Display *dpy, cairo_xlib_display_t *display; cairo_xlib_display_t **prev; XExtCodes *codes; - int render_major, render_minor; + const char *env; cairo_status_t status = CAIRO_STATUS_SUCCESS; static int buggy_repeat_force = -1; @@ -259,13 +283,23 @@ _cairo_xlib_display_get (Display *dpy, * add our hook. For now, that means Render, so we call into its * QueryVersion function to ensure it gets initialized. */ - Status s = XRenderQueryVersion (dpy, &render_major, &render_minor); - if (s == 0) { - /* XRenderQueryVersion failed, possibly because the server - * doesn't have the RENDER extension. Don't leave the version - * numbers uninitialised. See #548793. - */ - render_major = render_minor = 0; + display->render_major = display->render_minor = -1; + XRenderQueryVersion (dpy, &display->render_major, &display->render_minor); + env = getenv ("CAIRO_DEBUG"); + if (env != NULL && (env = strstr (env, "xrender-version=")) != NULL) { + int max_render_major, max_render_minor; + + env += sizeof ("xrender-version=") - 1; + if (sscanf (env, "%d.%d", &max_render_major, &max_render_minor) != 2) + max_render_major = max_render_minor = -1; + + if (max_render_major < display->render_major || + (max_render_major == display->render_major && + max_render_minor < display->render_minor)) + { + display->render_major = max_render_major; + display->render_minor = max_render_minor; + } } codes = XAddExtension (dpy); @@ -288,13 +322,19 @@ _cairo_xlib_display_get (Display *dpy, display->close_display_hooks = NULL; display->closed = FALSE; - display->render_major = render_major; - display->render_minor = render_minor; memset (display->cached_xrender_formats, 0, sizeof (display->cached_xrender_formats)); + /* Prior to Render 0.10, there is no protocol support for gradients and + * we call function stubs instead, which would silently consume the drawing. + */ +#if RENDER_MAJOR == 0 && RENDER_MINOR < 10 + display->buggy_gradients = TRUE; +#else + display->buggy_gradients = FALSE; +#endif + display->buggy_pad_reflect = FALSE; display->buggy_repeat = FALSE; - display->buggy_pad_reflect = TRUE; /* This buggy_repeat condition is very complicated because there * are multiple X server code bases (with multiple versioning @@ -338,24 +378,39 @@ _cairo_xlib_display_get (Display *dpy, * exactly when second the bug started, but since bug 1 is * present through 6.8.2 and bug 2 is present in 6.9.0 it seems * safest to just blacklist all old-versioning-scheme X servers, - * (just using VendorRelase < 70000000), as buggy_repeat=TRUE. + * (just using VendorRelease < 70000000), as buggy_repeat=TRUE. */ if (strstr (ServerVendor (dpy), "X.Org") != NULL) { if (VendorRelease (dpy) >= 60700000) { if (VendorRelease (dpy) < 70000000) display->buggy_repeat = TRUE; + + /* We know that gradients simply do not work in early Xorg servers */ + if (VendorRelease (dpy) < 70200000) + display->buggy_gradients = TRUE; + + /* And the extended repeat modes were not fixed until much later */ + display->buggy_pad_reflect = TRUE; } else { if (VendorRelease (dpy) < 10400000) display->buggy_repeat = TRUE; - if (VendorRelease (dpy) >= 10699000) - display->buggy_pad_reflect = FALSE; + + /* Too many bugs in the early drivers */ + if (VendorRelease (dpy) < 10699000) + display->buggy_pad_reflect = TRUE; } } else if (strstr (ServerVendor (dpy), "XFree86") != NULL) { if (VendorRelease (dpy) <= 40500000) display->buggy_repeat = TRUE; + display->buggy_gradients = TRUE; + display->buggy_pad_reflect = TRUE; } + /* gradients don't seem to work */ + display->buggy_gradients = TRUE; + + /* XXX workaround; see https://bugzilla.mozilla.org/show_bug.cgi?id=413583 */ /* If buggy_repeat_force == -1, then initialize. * - set to -2, meaning "nothing was specified", and we trust the above detection. @@ -486,6 +541,12 @@ _cairo_xlib_display_notify (cairo_xlib_display_t *display) cairo_xlib_job_t *jobs, *job, *freelist; Display *dpy = display->display; + /* Optimistic atomic pointer read -- don't care if it is wrong due to + * contention as we will check again very shortly. + */ + if (display->workqueue == NULL) + return; + CAIRO_MUTEX_LOCK (display->mutex); jobs = display->workqueue; while (jobs != NULL) { @@ -537,6 +598,12 @@ _cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display, { XRenderPictFormat *xrender_format; +#if ! ATOMIC_OP_NEEDS_MEMORY_BARRIER + xrender_format = display->cached_xrender_formats[format]; + if (likely (xrender_format != NULL)) + return xrender_format; +#endif + CAIRO_MUTEX_LOCK (display->mutex); xrender_format = display->cached_xrender_formats[format]; if (xrender_format == NULL) { @@ -562,3 +629,95 @@ _cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display, return xrender_format; } + +Display * +_cairo_xlib_display_get_dpy (cairo_xlib_display_t *display) +{ + return display->display; +} + +void +_cairo_xlib_display_remove_screen (cairo_xlib_display_t *display, + cairo_xlib_screen_t *screen) +{ + cairo_xlib_screen_t **prev; + cairo_xlib_screen_t *list; + + CAIRO_MUTEX_LOCK (display->mutex); + for (prev = &display->screens; (list = *prev); prev = &list->next) { + if (list == screen) { + *prev = screen->next; + break; + } + } + CAIRO_MUTEX_UNLOCK (display->mutex); +} + +cairo_status_t +_cairo_xlib_display_get_screen (cairo_xlib_display_t *display, + Screen *screen, + cairo_xlib_screen_t **out) +{ + cairo_xlib_screen_t *info = NULL, **prev; + + CAIRO_MUTEX_LOCK (display->mutex); + if (display->closed) { + CAIRO_MUTEX_UNLOCK (display->mutex); + return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); + } + + for (prev = &display->screens; (info = *prev); prev = &(*prev)->next) { + if (info->screen == screen) { + /* + * MRU the list + */ + if (prev != &display->screens) { + *prev = info->next; + info->next = display->screens; + display->screens = info; + } + break; + } + } + CAIRO_MUTEX_UNLOCK (display->mutex); + + *out = info; + return CAIRO_STATUS_SUCCESS; +} + + +void +_cairo_xlib_display_add_screen (cairo_xlib_display_t *display, + cairo_xlib_screen_t *screen) +{ + CAIRO_MUTEX_LOCK (display->mutex); + screen->next = display->screens; + display->screens = screen; + CAIRO_MUTEX_UNLOCK (display->mutex); +} + +void +_cairo_xlib_display_get_xrender_version (cairo_xlib_display_t *display, + int *major, int *minor) +{ + *major = display->render_major; + *minor = display->render_minor; +} + +cairo_bool_t +_cairo_xlib_display_has_repeat (cairo_xlib_display_t *display) +{ + return ! display->buggy_repeat; +} + +cairo_bool_t +_cairo_xlib_display_has_reflect (cairo_xlib_display_t *display) +{ + return ! display->buggy_pad_reflect; +} + +cairo_bool_t +_cairo_xlib_display_has_gradients (cairo_xlib_display_t *display) +{ + return ! display->buggy_gradients; +} diff --git a/gfx/cairo/cairo/src/cairo-xlib-private.h b/gfx/cairo/cairo/src/cairo-xlib-private.h index 4995bc6f985d..afdf6a6bddb4 100644 --- a/gfx/cairo/cairo/src/cairo-xlib-private.h +++ b/gfx/cairo/cairo/src/cairo-xlib-private.h @@ -41,11 +41,13 @@ #include "cairo-xlib-xrender-private.h" #include "cairo-compiler-private.h" -#include "cairo-freelist-private.h" #include "cairo-mutex-private.h" #include "cairo-reference-count-private.h" +#include "cairo-types-private.h" typedef struct _cairo_xlib_display cairo_xlib_display_t; +typedef struct _cairo_xlib_screen cairo_xlib_screen_t; + typedef struct _cairo_xlib_hook cairo_xlib_hook_t; typedef struct _cairo_xlib_job cairo_xlib_job_t; typedef void (*cairo_xlib_notify_func) (Display *, void *); @@ -56,27 +58,6 @@ struct _cairo_xlib_hook { void (*func) (cairo_xlib_display_t *display, void *data); }; -struct _cairo_xlib_display { - cairo_xlib_display_t *next; - cairo_reference_count_t ref_count; - cairo_mutex_t mutex; - - Display *display; - cairo_xlib_screen_info_t *screens; - - int render_major; - int render_minor; - XRenderPictFormat *cached_xrender_formats[CAIRO_FORMAT_A1 + 1]; - - cairo_xlib_job_t *workqueue; - cairo_freelist_t wq_freelist; - - cairo_xlib_hook_t *close_display_hooks; - unsigned int buggy_repeat :1; - unsigned int buggy_pad_reflect :1; - unsigned int closed :1; -}; - /* size of color cube */ #define CUBE_SIZE 6 /* size of gray ramp */ @@ -91,8 +72,8 @@ typedef struct _cairo_xlib_visual_info { uint8_t gray8_to_pseudocolor[256]; } cairo_xlib_visual_info_t; -struct _cairo_xlib_screen_info { - cairo_xlib_screen_info_t *next; +struct _cairo_xlib_screen { + cairo_xlib_screen_t *next; cairo_reference_count_t ref_count; cairo_mutex_t mutex; @@ -103,8 +84,8 @@ struct _cairo_xlib_screen_info { cairo_bool_t has_font_options; cairo_font_options_t font_options; - GC gc[9]; - unsigned int gc_needs_clip_reset; + GC gc[4]; + cairo_atomic_int_t gc_depths; /* 4 x uint8_t */ cairo_array_t visuals; }; @@ -114,9 +95,32 @@ _cairo_xlib_display_get (Display *display, cairo_xlib_display_t **out); cairo_private cairo_xlib_display_t * _cairo_xlib_display_reference (cairo_xlib_display_t *info); + cairo_private void _cairo_xlib_display_destroy (cairo_xlib_display_t *info); +cairo_private void +_cairo_xlib_display_lock (cairo_xlib_display_t *display); + +cairo_private void +_cairo_xlib_display_unlock (cairo_xlib_display_t *display); + +cairo_private Display * +_cairo_xlib_display_get_dpy (cairo_xlib_display_t *info); + +cairo_private void +_cairo_xlib_display_add_screen (cairo_xlib_display_t *display, + cairo_xlib_screen_t *screen); + +cairo_private cairo_status_t +_cairo_xlib_display_get_screen (cairo_xlib_display_t *display, + Screen *screen, + cairo_xlib_screen_t **out); + +cairo_private void +_cairo_xlib_display_remove_screen (cairo_xlib_display_t *display, + cairo_xlib_screen_t *screen); + cairo_private void _cairo_xlib_add_close_display_hook (cairo_xlib_display_t *display, cairo_xlib_hook_t *hook); @@ -135,39 +139,51 @@ _cairo_xlib_display_queue_resource (cairo_xlib_display_t *display, cairo_private void _cairo_xlib_display_notify (cairo_xlib_display_t *display); +cairo_private void +_cairo_xlib_display_get_xrender_version (cairo_xlib_display_t *display, + int *major, int *minor); + +cairo_private cairo_bool_t +_cairo_xlib_display_has_repeat (cairo_xlib_display_t *display); + +cairo_private cairo_bool_t +_cairo_xlib_display_has_reflect (cairo_xlib_display_t *display); + +cairo_private cairo_bool_t +_cairo_xlib_display_has_gradients (cairo_xlib_display_t *display); + cairo_private XRenderPictFormat * _cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display, cairo_format_t format); cairo_private cairo_status_t -_cairo_xlib_screen_info_get (cairo_xlib_display_t *display, - Screen *screen, - cairo_xlib_screen_info_t **out); +_cairo_xlib_screen_get (Display *dpy, + Screen *screen, + cairo_xlib_screen_t **out); -cairo_private cairo_xlib_screen_info_t * -_cairo_xlib_screen_info_reference (cairo_xlib_screen_info_t *info); +cairo_private cairo_xlib_screen_t * +_cairo_xlib_screen_reference (cairo_xlib_screen_t *info); cairo_private void -_cairo_xlib_screen_info_destroy (cairo_xlib_screen_info_t *info); +_cairo_xlib_screen_destroy (cairo_xlib_screen_t *info); cairo_private void -_cairo_xlib_screen_info_close_display (cairo_xlib_screen_info_t *info); +_cairo_xlib_screen_close_display (cairo_xlib_screen_t *info); cairo_private GC -_cairo_xlib_screen_get_gc (cairo_xlib_screen_info_t *info, +_cairo_xlib_screen_get_gc (cairo_xlib_screen_t *info, int depth, - unsigned int *need_reset); + Drawable drawable); -cairo_private cairo_status_t -_cairo_xlib_screen_put_gc (cairo_xlib_screen_info_t *info, +cairo_private void +_cairo_xlib_screen_put_gc (cairo_xlib_screen_t *info, int depth, - GC gc, - cairo_bool_t reset_clip); + GC gc); cairo_private cairo_font_options_t * -_cairo_xlib_screen_get_font_options (cairo_xlib_screen_info_t *info); +_cairo_xlib_screen_get_font_options (cairo_xlib_screen_t *info); cairo_private cairo_status_t -_cairo_xlib_screen_get_visual_info (cairo_xlib_screen_info_t *info, +_cairo_xlib_screen_get_visual_info (cairo_xlib_screen_t *info, Visual *visual, cairo_xlib_visual_info_t **out); diff --git a/gfx/cairo/cairo/src/cairo-xlib-screen.c b/gfx/cairo/cairo/src/cairo-xlib-screen.c index 8f1e94971914..09a2a81077d6 100644 --- a/gfx/cairo/cairo/src/cairo-xlib-screen.c +++ b/gfx/cairo/cairo/src/cairo-xlib-screen.c @@ -144,7 +144,7 @@ get_integer_default (Display *dpy, static void _cairo_xlib_init_screen_font_options (Display *dpy, - cairo_xlib_screen_info_t *info) + cairo_xlib_screen_t *info) { cairo_bool_t xft_hinting; cairo_bool_t xft_antialias; @@ -254,8 +254,8 @@ _cairo_xlib_init_screen_font_options (Display *dpy, cairo_font_options_set_hint_metrics (&info->font_options, CAIRO_HINT_METRICS_ON); } -cairo_xlib_screen_info_t * -_cairo_xlib_screen_info_reference (cairo_xlib_screen_info_t *info) +cairo_xlib_screen_t * +_cairo_xlib_screen_reference (cairo_xlib_screen_t *info) { assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&info->ref_count)); @@ -265,48 +265,50 @@ _cairo_xlib_screen_info_reference (cairo_xlib_screen_info_t *info) } void -_cairo_xlib_screen_info_close_display (cairo_xlib_screen_info_t *info) +_cairo_xlib_screen_close_display (cairo_xlib_screen_t *info) { cairo_xlib_visual_info_t **visuals; + Display *dpy; + cairo_atomic_int_t old; int i; CAIRO_MUTEX_LOCK (info->mutex); + + dpy = _cairo_xlib_display_get_dpy (info->display); + +#if HAS_ATOMIC_OPS + do { + old = _cairo_atomic_int_get (&info->gc_depths); + } while (_cairo_atomic_int_cmpxchg (&info->gc_depths, old, 0) != old); +#else + old = info->gc_depths; + info->gc_depths = 0; +#endif + for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { - if (info->gc[i] != NULL) { - XFreeGC (info->display->display, info->gc[i]); - info->gc[i] = NULL; - } + if ((old >> (8*i)) & 0xff) + XFreeGC (dpy, info->gc[i]); } visuals = _cairo_array_index (&info->visuals, 0); for (i = 0; i < _cairo_array_num_elements (&info->visuals); i++) - _cairo_xlib_visual_info_destroy (info->display->display, visuals[i]); + _cairo_xlib_visual_info_destroy (dpy, visuals[i]); _cairo_array_truncate (&info->visuals, 0); CAIRO_MUTEX_UNLOCK (info->mutex); } void -_cairo_xlib_screen_info_destroy (cairo_xlib_screen_info_t *info) +_cairo_xlib_screen_destroy (cairo_xlib_screen_t *info) { - cairo_xlib_screen_info_t **prev; - cairo_xlib_screen_info_t *list; - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&info->ref_count)); if (! _cairo_reference_count_dec_and_test (&info->ref_count)) return; - CAIRO_MUTEX_LOCK (info->display->mutex); - for (prev = &info->display->screens; (list = *prev); prev = &list->next) { - if (list == info) { - *prev = info->next; - break; - } - } - CAIRO_MUTEX_UNLOCK (info->display->mutex); + _cairo_xlib_display_remove_screen (info->display, info); - _cairo_xlib_screen_info_close_display (info); + _cairo_xlib_screen_close_display (info); _cairo_xlib_display_destroy (info->display); @@ -318,146 +320,222 @@ _cairo_xlib_screen_info_destroy (cairo_xlib_screen_info_t *info) } cairo_status_t -_cairo_xlib_screen_info_get (cairo_xlib_display_t *display, - Screen *screen, - cairo_xlib_screen_info_t **out) +_cairo_xlib_screen_get (Display *dpy, + Screen *screen, + cairo_xlib_screen_t **out) { - cairo_xlib_screen_info_t *info = NULL, **prev; + cairo_xlib_display_t *display; + cairo_xlib_screen_t *info; + cairo_status_t status; - CAIRO_MUTEX_LOCK (display->mutex); - if (display->closed) { - CAIRO_MUTEX_UNLOCK (display->mutex); - return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); - } - - for (prev = &display->screens; (info = *prev); prev = &(*prev)->next) { - if (info->screen == screen) { - /* - * MRU the list - */ - if (prev != &display->screens) { - *prev = info->next; - info->next = display->screens; - display->screens = info; - } - break; - } - } - CAIRO_MUTEX_UNLOCK (display->mutex); + status = _cairo_xlib_display_get (dpy, &display); + if (likely (status == CAIRO_STATUS_SUCCESS)) + status = _cairo_xlib_display_get_screen (display, screen, &info); + if (unlikely (status)) + goto CLEANUP_DISPLAY; if (info != NULL) { - info = _cairo_xlib_screen_info_reference (info); - } else { - info = malloc (sizeof (cairo_xlib_screen_info_t)); - if (unlikely (info == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - CAIRO_REFERENCE_COUNT_INIT (&info->ref_count, 2); /* Add one for display cache */ - CAIRO_MUTEX_INIT (info->mutex); - info->display = _cairo_xlib_display_reference (display); - info->screen = screen; - info->has_render = FALSE; - info->has_font_options = FALSE; - memset (info->gc, 0, sizeof (info->gc)); - info->gc_needs_clip_reset = 0; - - _cairo_array_init (&info->visuals, - sizeof (cairo_xlib_visual_info_t*)); - - if (screen) { - Display *dpy = display->display; - int event_base, error_base; - - info->has_render = (XRenderQueryExtension (dpy, &event_base, &error_base) && - (XRenderFindVisualFormat (dpy, DefaultVisual (dpy, DefaultScreen (dpy))) != 0)); - } - - /* Small window of opportunity for two screen infos for the same - * Screen - just wastes a little bit of memory but should not cause - * any corruption. - */ - CAIRO_MUTEX_LOCK (display->mutex); - info->next = display->screens; - display->screens = info; - CAIRO_MUTEX_UNLOCK (display->mutex); + *out = _cairo_xlib_screen_reference (info); + goto CLEANUP_DISPLAY; } + info = malloc (sizeof (cairo_xlib_screen_t)); + if (unlikely (info == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_DISPLAY; + } + + CAIRO_REFERENCE_COUNT_INIT (&info->ref_count, 2); /* Add one for display cache */ + CAIRO_MUTEX_INIT (info->mutex); + info->display = display; + info->screen = screen; + info->has_render = FALSE; + info->has_font_options = FALSE; + info->gc_depths = 0; + memset (info->gc, 0, sizeof (info->gc)); + + _cairo_array_init (&info->visuals, + sizeof (cairo_xlib_visual_info_t*)); + + if (screen) { + int event_base, error_base; + + info->has_render = + XRenderQueryExtension (dpy, &event_base, &error_base) && + (XRenderFindVisualFormat (dpy, DefaultVisual (dpy, DefaultScreen (dpy))) != 0); + } + + /* Small window of opportunity for two screen infos for the same + * Screen - just wastes a little bit of memory but should not cause + * any corruption. + */ + _cairo_xlib_display_add_screen (display, info); + *out = info; return CAIRO_STATUS_SUCCESS; + + CLEANUP_DISPLAY: + _cairo_xlib_display_destroy (display); + return status; } -static int -depth_to_index (int depth) -{ - switch(depth){ - case 1: return 1; - case 8: return 2; - case 12: return 3; - case 15: return 4; - case 16: return 5; - case 24: return 6; - case 30: return 7; - case 32: return 8; - } - return 0; -} - +#if HAS_ATOMIC_OPS GC -_cairo_xlib_screen_get_gc (cairo_xlib_screen_info_t *info, +_cairo_xlib_screen_get_gc (cairo_xlib_screen_t *info, int depth, - unsigned int *dirty) + Drawable drawable) { + XGCValues gcv; + cairo_atomic_int_t old, new; + int i; GC gc; - cairo_bool_t needs_reset; - depth = depth_to_index (depth); + do { + gc = NULL; + old = info->gc_depths; + if (old == 0) + break; + + if (((old >> 0) & 0xff) == depth) + i = 0; + else if (((old >> 8) & 0xff) == depth) + i = 1; + else if (((old >> 16) & 0xff) == depth) + i = 2; + else if (((old >> 24) & 0xff) == depth) + i = 3; + else + break; + + gc = info->gc[i]; + new = old & ~(0xff << (8*i)); + } while (_cairo_atomic_int_cmpxchg (&info->gc_depths, old, new) != old); + + if (likely (gc != NULL)) { + (void) _cairo_atomic_ptr_cmpxchg (&info->gc[i], gc, NULL); + return gc; + } + + gcv.graphics_exposures = False; + gcv.fill_style = FillTiled; + return XCreateGC (_cairo_xlib_display_get_dpy (info->display), + drawable, + GCGraphicsExposures | GCFillStyle, &gcv); +} + +void +_cairo_xlib_screen_put_gc (cairo_xlib_screen_t *info, + int depth, + GC gc) +{ + int i, old, new; + + do { + do { + i = -1; + old = info->gc_depths; + + if (((old >> 0) & 0xff) == 0) + i = 0; + else if (((old >> 8) & 0xff) == 0) + i = 1; + else if (((old >> 16) & 0xff) == 0) + i = 2; + else if (((old >> 24) & 0xff) == 0) + i = 3; + else + goto out; + + new = old | (depth << (8*i)); + } while (_cairo_atomic_ptr_cmpxchg (&info->gc[i], NULL, gc) != NULL); + } while (_cairo_atomic_int_cmpxchg (&info->gc_depths, old, new) != old); + + return; + +out: + if (unlikely (_cairo_xlib_display_queue_work (info->display, + (cairo_xlib_notify_func) XFreeGC, + gc, + NULL))) + { + /* leak the server side resource... */ + XFree ((char *) gc); + } +} +#else +GC +_cairo_xlib_screen_get_gc (cairo_xlib_screen_t *info, + int depth, + Drawable drawable) +{ + GC gc = NULL; + int i; CAIRO_MUTEX_LOCK (info->mutex); - gc = info->gc[depth]; - info->gc[depth] = NULL; - needs_reset = info->gc_needs_clip_reset & (1 << depth); - info->gc_needs_clip_reset &= ~(1 << depth); + for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { + if (((info->gc_depths >> (8*i)) & 0xff) == depth) { + info->gc_depths &= ~(0xff << (8*i)); + gc = info->gc[i]; + break; + } + } CAIRO_MUTEX_UNLOCK (info->mutex); - if (needs_reset) - *dirty |= CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC; + if (gc == NULL) { + XGCValues gcv; + + gcv.graphics_exposures = False; + gcv.fill_style = FillTiled; + gc = XCreateGC (_cairo_xlib_display_get_dpy (info->display), + drawable, + GCGraphicsExposures | GCFillStyle, &gcv); + } return gc; } -cairo_status_t -_cairo_xlib_screen_put_gc (cairo_xlib_screen_info_t *info, int depth, GC gc, cairo_bool_t reset_clip) +void +_cairo_xlib_screen_put_gc (cairo_xlib_screen_t *info, + int depth, + GC gc) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; - GC oldgc; - - depth = depth_to_index (depth); + int i; CAIRO_MUTEX_LOCK (info->mutex); - oldgc = info->gc[depth]; - info->gc[depth] = gc; - if (reset_clip) - info->gc_needs_clip_reset |= 1 << depth; - else - info->gc_needs_clip_reset &= ~(1 << depth); - CAIRO_MUTEX_UNLOCK (info->mutex); - - if (oldgc != NULL) { - status = _cairo_xlib_display_queue_work (info->display, - (cairo_xlib_notify_func) XFreeGC, - oldgc, - NULL); + for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { + if (((info->gc_depths >> (8*i)) & 0xff) == 0) + break; } - return status; + if (i == ARRAY_LENGTH (info->gc)) { + cairo_status_t status; + + /* perform random substitution to ensure fair caching over depths */ + i = rand () % ARRAY_LENGTH (info->gc); + status = + _cairo_xlib_display_queue_work (info->display, + (cairo_xlib_notify_func) XFreeGC, + info->gc[i], + NULL); + if (unlikely (status)) { + /* leak the server side resource... */ + XFree ((char *) info->gc[i]); + } + } + + info->gc[i] = gc; + info->gc_depths &= ~(0xff << (8*i)); + info->gc_depths |= depth << (8*i); + CAIRO_MUTEX_UNLOCK (info->mutex); } +#endif cairo_status_t -_cairo_xlib_screen_get_visual_info (cairo_xlib_screen_info_t *info, +_cairo_xlib_screen_get_visual_info (cairo_xlib_screen_t *info, Visual *visual, cairo_xlib_visual_info_t **out) { - Display *dpy = info->display->display; + Display *dpy = _cairo_xlib_display_get_dpy (info->display); cairo_xlib_visual_info_t **visuals, *ret = NULL; cairo_status_t status; int i, n_visuals; @@ -513,19 +591,19 @@ _cairo_xlib_screen_get_visual_info (cairo_xlib_screen_info_t *info, } cairo_font_options_t * -_cairo_xlib_screen_get_font_options (cairo_xlib_screen_info_t *info) +_cairo_xlib_screen_get_font_options (cairo_xlib_screen_t *info) { if (info->has_font_options) return &info->font_options; CAIRO_MUTEX_LOCK (info->mutex); if (! info->has_font_options) { - Display *dpy = info->display->display; - _cairo_font_options_init_default (&info->font_options); - if (info->screen != NULL) - _cairo_xlib_init_screen_font_options (dpy, info); + if (info->screen != NULL) { + _cairo_xlib_init_screen_font_options (_cairo_xlib_display_get_dpy (info->display), + info); + } info->has_font_options = TRUE; } diff --git a/gfx/cairo/cairo/src/cairo-xlib-surface-private.h b/gfx/cairo/cairo/src/cairo-xlib-surface-private.h index 164fe15f25c9..b30848d57f94 100644 --- a/gfx/cairo/cairo/src/cairo-xlib-surface-private.h +++ b/gfx/cairo/cairo/src/cairo-xlib-surface-private.h @@ -46,12 +46,10 @@ struct _cairo_xlib_surface { Display *dpy; cairo_xlib_display_t *display; - cairo_xlib_screen_info_t *screen_info; + cairo_xlib_screen_t *screen; cairo_xlib_hook_t close_display_hook; - GC gc; Drawable drawable; - Screen *screen; cairo_bool_t owns_pixmap; Visual *visual; @@ -75,8 +73,12 @@ struct _cairo_xlib_surface { * Both are fixed in xorg >= 6.9 and hopefully in > 6.8.2, so * we can reuse the test for now. */ - cairo_bool_t buggy_repeat; - cairo_bool_t buggy_pad_reflect; + unsigned int buggy_gradients : 1; + unsigned int buggy_pad_reflect : 1; + unsigned int buggy_repeat : 1; +#define CAIRO_XLIB_SURFACE_HAS_BUGGY_GRADIENTS 1 +#define CAIRO_XLIB_SURFACE_HAS_BUGGY_PAD_REFLECT 1 +#define CAIRO_XLIB_SURFACE_HAS_BUGGY_REPEAT 1 int width; int height; @@ -85,15 +87,15 @@ struct _cairo_xlib_surface { Picture dst_picture, src_picture; unsigned int clip_dirty; - cairo_bool_t have_clip_rects; - cairo_bool_t gc_has_clip_rects; XRectangle embedded_clip_rects[8]; XRectangle *clip_rects; int num_clip_rects; + cairo_region_t *clip_region; XRenderPictFormat *xrender_format; cairo_filter_t filter; - int repeat; + cairo_extend_t extend; + cairo_bool_t has_component_alpha; XTransform xtransform; uint32_t a_mask; diff --git a/gfx/cairo/cairo/src/cairo-xlib-surface.c b/gfx/cairo/cairo/src/cairo-xlib-surface.c index d0de199b0721..8f773b04ac21 100644 --- a/gfx/cairo/cairo/src/cairo-xlib-surface.c +++ b/gfx/cairo/cairo/src/cairo-xlib-surface.c @@ -39,25 +39,78 @@ * Karl Tomlinson , Mozilla Corporation */ +/* Heed well the words of Owen Taylor: + * "Any patch that works around a render bug, or claims to, without a + * specific reference to the bug filed in bugzilla.freedesktop.org will + * never pass approval." + */ + #include "cairoint.h" #include "cairo-xlib-private.h" #include "cairo-xlib-surface-private.h" #include "cairo-clip-private.h" #include "cairo-scaled-font-private.h" +#include "cairo-region-private.h" #include /* for XDestroyImage */ #define XLIB_COORD_MAX 32767 +#define DEBUG 0 + +#if DEBUG +#define UNSUPPORTED(reason) \ + fprintf (stderr, \ + "cairo-xlib: hit unsupported operation %s(), line %d: %s\n", \ + __FUNCTION__, __LINE__, reason), \ + CAIRO_INT_STATUS_UNSUPPORTED +#else +#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED +#endif + +#if DEBUG +#include +static void CAIRO_PRINTF_FORMAT (2, 3) +_x_bread_crumb (Display *dpy, + const char *fmt, + ...) +{ + xReq *req; + char buf[2048]; + unsigned int len, len_dwords; + va_list ap; + + va_start (ap, fmt); + len = vsnprintf (buf, sizeof (buf), fmt, ap); + va_end (ap); + + buf[len++] = '\0'; + while (len & 3) + buf[len++] = '\0'; + + LockDisplay (dpy); + GetEmptyReq (NoOperation, req); + + len_dwords = len >> 2; + SetReqLen (req, len_dwords, len_dwords); + Data (dpy, buf, len); + + UnlockDisplay (dpy); + SyncHandle (); +} +#define X_DEBUG(x) _x_bread_crumb x +#else +#define X_DEBUG(x) +#endif + /* Xlib doesn't define a typedef, so define one ourselves */ typedef int (*cairo_xlib_error_func_t) (Display *display, XErrorEvent *event); static cairo_surface_t * -_cairo_xlib_surface_create_internal (Display *dpy, +_cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen, Drawable drawable, - Screen *screen, Visual *visual, XRenderPictFormat *xrender_format, int width, @@ -65,7 +118,10 @@ _cairo_xlib_surface_create_internal (Display *dpy, int depth); static cairo_status_t -_cairo_xlib_surface_ensure_gc (cairo_xlib_surface_t *surface); +_cairo_xlib_surface_get_gc (cairo_xlib_surface_t *surface, GC *gc); + +static void +_cairo_xlib_surface_put_gc (cairo_xlib_surface_t *surface, GC gc); static void _cairo_xlib_surface_ensure_src_picture (cairo_xlib_surface_t *surface); @@ -86,8 +142,8 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip, + int *remaining_glyphs); /* XXX temporarily used by cairo-qt-surface.c */ slim_hidden_proto (cairo_xlib_surface_create); @@ -128,51 +184,70 @@ static const XTransform identity = { { #define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) #define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) -static cairo_surface_t * -_cairo_xlib_surface_create_similar_with_format (void *abstract_src, - cairo_format_t format, - int width, - int height) +#define CAIRO_SURFACE_RENDER_HAS_EXTENDED_REPEAT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10) +#define CAIRO_SURFACE_RENDER_HAS_GRADIENTS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10) + +#define CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 11) + +#define CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR(surface, op) \ + ((op) <= CAIRO_OPERATOR_SATURATE || \ + (CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS(surface) && \ + (op) <= CAIRO_OPERATOR_HSL_LUMINOSITY)) + +static cairo_status_t +_cairo_xlib_surface_set_clip_region (cairo_xlib_surface_t *surface, + cairo_region_t *region) { - cairo_xlib_surface_t *src = abstract_src; - Display *dpy = src->dpy; - Pixmap pix; - cairo_xlib_surface_t *surface; - XRenderPictFormat *xrender_format; + cairo_bool_t had_clip_rects = surface->clip_region != NULL; - assert (width <= XLIB_COORD_MAX && height <= XLIB_COORD_MAX); + if (had_clip_rects == FALSE && region == NULL) + return CAIRO_STATUS_SUCCESS; - /* As a good first approximation, if the display doesn't have even - * the most elementary RENDER operation, then we're better off - * using image surfaces for all temporary operations, so return NULL - * and let the fallback code happen. - */ - if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src)) - return NULL; + if (surface->clip_region == region) + return CAIRO_STATUS_SUCCESS; - xrender_format = _cairo_xlib_display_get_xrender_format (src->display, - format); - if (xrender_format == NULL) - return NULL; + if (cairo_region_equal (surface->clip_region, region)) + return CAIRO_STATUS_SUCCESS; - pix = XCreatePixmap (dpy, src->drawable, - width <= 0 ? 1 : width, height <= 0 ? 1 : height, - xrender_format->depth); + cairo_region_destroy (surface->clip_region); + surface->clip_region = cairo_region_reference (region); - surface = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_internal (dpy, pix, - src->screen, NULL, - xrender_format, - width, height, - xrender_format->depth); - if (surface->base.status) { - XFreePixmap (dpy, pix); - return &surface->base; + if (surface->clip_rects != surface->embedded_clip_rects) { + free (surface->clip_rects); + surface->clip_rects = surface->embedded_clip_rects; + } + surface->num_clip_rects = 0; + + if (region != NULL) { + XRectangle *rects = NULL; + int n_rects, i; + + n_rects = cairo_region_num_rectangles (region); + if (n_rects > ARRAY_LENGTH (surface->embedded_clip_rects)) { + rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle)); + if (unlikely (rects == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else { + rects = surface->embedded_clip_rects; + } + + for (i = 0; i < n_rects; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (region, i, &rect); + + rects[i].x = rect.x; + rects[i].y = rect.y; + rects[i].width = rect.width; + rects[i].height = rect.height; + } + + surface->clip_rects = rects; + surface->num_clip_rects = n_rects; } - surface->owns_pixmap = TRUE; - - return &surface->base; + surface->clip_dirty = CAIRO_XLIB_SURFACE_CLIP_DIRTY_ALL; + return CAIRO_STATUS_SUCCESS; } static cairo_content_t @@ -214,6 +289,9 @@ _cairo_xlib_surface_create_similar (void *abstract_src, if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) return NULL; + if (! CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (src)) + return NULL; + _cairo_xlib_display_notify (src->display); /* If we never found an XRenderFormat or if it isn't compatible @@ -222,28 +300,61 @@ _cairo_xlib_surface_create_similar (void *abstract_src, * arbitrarily pick a visual/depth for the similar surface. */ xrender_format = src->xrender_format; - if (xrender_format == NULL || - _xrender_format_to_content (xrender_format) != content) + if ((xrender_format != NULL && + _xrender_format_to_content (xrender_format) == content) || + (xrender_format = + _cairo_xlib_display_get_xrender_format (src->display, + _cairo_format_from_content (content)))) { - return _cairo_xlib_surface_create_similar_with_format (abstract_src, - _cairo_format_from_content (content), - width, height); + Visual *visual; + + /* We've got a compatible XRenderFormat now, which means the + * similar surface will match the existing surface as closely in + * visual/depth etc. as possible. */ + pix = XCreatePixmap (src->dpy, src->drawable, + width <= 0 ? 1 : width, height <= 0 ? 1 : height, + xrender_format->depth); + + visual = NULL; + if (xrender_format == src->xrender_format) + visual = src->visual; + + surface = (cairo_xlib_surface_t *) + _cairo_xlib_surface_create_internal (src->screen, pix, + visual, + xrender_format, + width, height, + xrender_format->depth); + } + else + { +#ifdef DEBUG_FORCE_FALLBACKS + Screen *screen = src->screen->screen; + int depth; + + /* No compatabile XRenderFormat, see if we can make an ordinary pixmap, + * so that we can still accelerate blits with XCopyArea(). */ + if (content != CAIRO_CONTENT_COLOR) + return NULL; + + depth = DefaultDepthOfScreen (screen); + + pix = XCreatePixmap (src->dpy, RootWindowOfScreen (screen), + width <= 0 ? 1 : width, height <= 0 ? 1 : height, + depth); + + surface = (cairo_xlib_surface_t *) + _cairo_xlib_surface_create_internal (src->screen, pix, + DefaultVisualOfScreen (screen), + NULL, + width, height, depth); +#else + /* No compatabile XRenderFormat, just say no. */ + return NULL; +#endif } - /* We've got a compatible XRenderFormat now, which means the - * similar surface will match the existing surface as closely in - * visual/depth etc. as possible. */ - pix = XCreatePixmap (src->dpy, src->drawable, - width <= 0 ? 1 : width, height <= 0 ? 1 : height, - xrender_format->depth); - - surface = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_internal (src->dpy, pix, - src->screen, src->visual, - xrender_format, - width, height, - xrender_format->depth); - if (surface->base.status != CAIRO_STATUS_SUCCESS) { + if (unlikely (surface->base.status)) { XFreePixmap (src->dpy, pix); return &surface->base; } @@ -260,6 +371,8 @@ _cairo_xlib_surface_finish (void *abstract_surface) cairo_xlib_display_t *display = surface->display; cairo_status_t status = CAIRO_STATUS_SUCCESS; + X_DEBUG ((surface->dpy, "finish (drawable=%x)", (unsigned int) surface->drawable)); + if (surface->owns_pixmap) { cairo_status_t status2; @@ -267,9 +380,7 @@ _cairo_xlib_surface_finish (void *abstract_surface) status2 = _cairo_xlib_display_queue_resource (display, XRenderFreePicture, surface->dst_picture); - if (status2 == CAIRO_STATUS_SUCCESS) - surface->dst_picture = None; - else if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_STATUS_SUCCESS) status = status2; } @@ -277,19 +388,14 @@ _cairo_xlib_surface_finish (void *abstract_surface) status2 = _cairo_xlib_display_queue_resource (display, XRenderFreePicture, surface->src_picture); - if (status2 == CAIRO_STATUS_SUCCESS) - surface->src_picture = None; - else if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_STATUS_SUCCESS) status = status2; } status2 = _cairo_xlib_display_queue_resource (display, (cairo_xlib_notify_resource_func) XFreePixmap, surface->drawable); - if (status2 == CAIRO_STATUS_SUCCESS) { - surface->owns_pixmap = FALSE; - surface->drawable = None; - } else if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_STATUS_SUCCESS) status = status2; } else { if (surface->dst_picture != None) @@ -299,30 +405,18 @@ _cairo_xlib_surface_finish (void *abstract_surface) XRenderFreePicture (surface->dpy, surface->src_picture); } - if (surface->gc != NULL) { - cairo_status_t status2; - status2 = _cairo_xlib_screen_put_gc (surface->screen_info, - surface->depth, - surface->gc, - surface->gc_has_clip_rects); - surface->gc = NULL; - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } - if (surface->clip_rects != surface->embedded_clip_rects) free (surface->clip_rects); - if (surface->screen_info != NULL) - _cairo_xlib_screen_info_destroy (surface->screen_info); - - if (surface->display != NULL) { + if (surface->dpy != NULL) { _cairo_xlib_remove_close_display_hook (surface->display, &surface->close_display_hook); - _cairo_xlib_display_destroy (surface->display); + surface->dpy = NULL; } - surface->dpy = NULL; + _cairo_xlib_screen_destroy (surface->screen); + + cairo_region_destroy (surface->clip_region); return status; } @@ -657,9 +751,7 @@ _get_image_surface (cairo_xlib_surface_t *surface, ximage = NULL; } - if (!ximage) - { - + if (ximage == NULL) { /* XGetImage from a window is dangerous because it can * produce errors if the window is unmapped or partially * outside the screen. We could check for errors and @@ -667,8 +759,9 @@ _get_image_surface (cairo_xlib_surface_t *surface, * temporary pixmap */ Pixmap pixmap; + GC gc; - status = _cairo_xlib_surface_ensure_gc (surface); + status = _cairo_xlib_surface_get_gc (surface, &gc); if (unlikely (status)) return status; @@ -678,7 +771,7 @@ _get_image_surface (cairo_xlib_surface_t *surface, extents.height <= 0 ? 1 : extents.height, surface->depth); if (pixmap) { - XCopyArea (surface->dpy, surface->drawable, pixmap, surface->gc, + XCopyArea (surface->dpy, surface->drawable, pixmap, gc, extents.x, extents.y, extents.width, extents.height, 0, 0); @@ -692,9 +785,12 @@ _get_image_surface (cairo_xlib_surface_t *surface, XFreePixmap (surface->dpy, pixmap); } + + _cairo_xlib_surface_put_gc (surface, gc); + + if (ximage == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - if (!ximage) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); _swap_ximage_to_native (ximage); @@ -704,10 +800,14 @@ _get_image_surface (cairo_xlib_surface_t *surface, xlib_masks.green_mask = surface->g_mask; xlib_masks.blue_mask = surface->b_mask; - if (_pixman_format_from_masks (&xlib_masks, &pixman_format) && - xlib_masks.bpp >= 24 && - ximage->bitmap_unit == 32 && - ximage->bitmap_pad == 32) + /* We can't use pixman to simply write to image if: + * (a) the pixels are not appropriately aligned, + * (b) pixman does not the pixel format, or + * (c) if the image is palettized and we need to convert. + */ + if (ximage->bitmap_unit == 32 && ximage->bitmap_pad == 32 && + _pixman_format_from_masks (&xlib_masks, &pixman_format) && + (surface->visual == NULL || surface->visual->class == TrueColor)) { image = (cairo_image_surface_t*) _cairo_image_surface_create_with_pixman_format ((unsigned char *) ximage->data, @@ -736,7 +836,7 @@ _get_image_surface (cairo_xlib_surface_t *surface, int a_width=0, r_width=0, g_width=0, b_width=0; int a_shift=0, r_shift=0, g_shift=0, b_shift=0; int x, y, x0, y0, x_off, y_off; - cairo_xlib_visual_info_t *visual_info; + cairo_xlib_visual_info_t *visual_info = NULL; if (surface->visual == NULL || surface->visual->class == TrueColor) { cairo_bool_t has_alpha; @@ -774,7 +874,7 @@ _get_image_surface (cairo_xlib_surface_t *surface, } else { format = CAIRO_FORMAT_RGB24; - status = _cairo_xlib_screen_get_visual_info (surface->screen_info, + status = _cairo_xlib_screen_get_visual_info (surface->screen, surface->visual, &visual_info); if (unlikely (status)) @@ -802,7 +902,7 @@ _get_image_surface (cairo_xlib_surface_t *surface, int dither_adjustment = dither_row[x_off]; in_pixel = XGetPixel (ximage, x, y); - if (surface->visual == NULL || surface->visual->class == TrueColor) { + if (visual_info == NULL) { out_pixel = ( _field_to_8 (in_pixel & a_mask, a_width, a_shift) << 24 | _field_to_8_undither (in_pixel & r_mask, r_width, r_shift, dither_adjustment) << 16 | @@ -816,6 +916,7 @@ _get_image_surface (cairo_xlib_surface_t *surface, } row += rowstride; } + cairo_surface_mark_dirty (&image->base); } BAIL: @@ -851,7 +952,7 @@ _cairo_xlib_surface_ensure_src_picture (cairo_xlib_surface_t *surface) static void _cairo_xlib_surface_set_picture_clip_rects (cairo_xlib_surface_t *surface) { - if (surface->have_clip_rects) { + if (surface->clip_region != NULL) { XRenderSetPictureClipRectangles (surface->dpy, surface->dst_picture, 0, 0, surface->clip_rects, @@ -866,21 +967,6 @@ _cairo_xlib_surface_set_picture_clip_rects (cairo_xlib_surface_t *surface) surface->clip_dirty &= ~CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE; } -static void -_cairo_xlib_surface_set_gc_clip_rects (cairo_xlib_surface_t *surface) -{ - surface->gc_has_clip_rects = surface->have_clip_rects; - if (surface->have_clip_rects) { - XSetClipRectangles(surface->dpy, surface->gc, - 0, 0, - surface->clip_rects, - surface->num_clip_rects, YXSorted); - } else - XSetClipMask (surface->dpy, surface->gc, None); - - surface->clip_dirty &= ~CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC; -} - static void _cairo_xlib_surface_ensure_dst_picture (cairo_xlib_surface_t *surface) { @@ -896,29 +982,25 @@ _cairo_xlib_surface_ensure_dst_picture (cairo_xlib_surface_t *surface) } static cairo_status_t -_cairo_xlib_surface_ensure_gc (cairo_xlib_surface_t *surface) +_cairo_xlib_surface_get_gc (cairo_xlib_surface_t *surface, GC *gc) { - XGCValues gcv; - - if (surface->gc == NULL) { - surface->gc = _cairo_xlib_screen_get_gc (surface->screen_info, - surface->depth, - &surface->clip_dirty); - if (surface->gc == NULL) { - gcv.graphics_exposures = False; - surface->gc = XCreateGC (surface->dpy, surface->drawable, - GCGraphicsExposures, &gcv); - if (unlikely (surface->gc == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - } - - if (surface->clip_dirty & CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC) - _cairo_xlib_surface_set_gc_clip_rects (surface); + *gc = _cairo_xlib_screen_get_gc (surface->screen, + surface->depth, + surface->drawable); + if (unlikely (*gc == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_SUCCESS; } +static void +_cairo_xlib_surface_put_gc (cairo_xlib_surface_t *surface, GC gc) +{ + _cairo_xlib_screen_put_gc (surface->screen, + surface->depth, + gc); +} + static cairo_status_t _draw_image_surface (cairo_xlib_surface_t *surface, cairo_image_surface_t *image, @@ -932,10 +1014,10 @@ _draw_image_surface (cairo_xlib_surface_t *surface, XImage ximage; cairo_format_masks_t image_masks; int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst; + pixman_image_t *pixman_image = NULL; cairo_status_t status; cairo_bool_t own_data; - - _pixman_format_to_masks (image->pixman_format, &image_masks); + GC gc; ximage.width = image->width; ximage.height = image->height; @@ -950,10 +1032,48 @@ _draw_image_surface (cairo_xlib_surface_t *surface, ximage.blue_mask = surface->b_mask; ximage.xoffset = 0; - if ((image_masks.alpha_mask == surface->a_mask || surface->a_mask == 0) && - (image_masks.red_mask == surface->r_mask || surface->r_mask == 0) && - (image_masks.green_mask == surface->g_mask || surface->g_mask == 0) && - (image_masks.blue_mask == surface->b_mask || surface->b_mask == 0)) + if (!_pixman_format_to_masks (image->pixman_format, &image_masks)) + { + pixman_format_code_t intermediate_format; + int ret; + + image_masks.alpha_mask = surface->a_mask; + image_masks.red_mask = surface->r_mask; + image_masks.green_mask = surface->g_mask; + image_masks.blue_mask = surface->b_mask; + image_masks.bpp = surface->depth; + ret = _pixman_format_from_masks (&image_masks, &intermediate_format); + assert (ret); + + pixman_image = pixman_image_create_bits (intermediate_format, + image->width, + image->height, + NULL, + 0); + if (pixman_image == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + pixman_image_composite (PIXMAN_OP_SRC, + image->pixman_image, + NULL, + pixman_image, + 0, 0, + 0, 0, + 0, 0, + image->width, image->height); + + ximage.bits_per_pixel = image_masks.bpp; + ximage.data = (char *) pixman_image_get_data (pixman_image); + ximage.bytes_per_line = pixman_image_get_stride (pixman_image); + own_data = FALSE; + + ret = XInitImage (&ximage); + assert (ret != 0); + } + else if ((image_masks.alpha_mask == surface->a_mask || surface->a_mask == 0) && + (image_masks.red_mask == surface->r_mask || surface->r_mask == 0) && + (image_masks.green_mask == surface->g_mask || surface->g_mask == 0) && + (image_masks.blue_mask == surface->b_mask || surface->b_mask == 0)) { int ret; @@ -964,7 +1084,9 @@ _draw_image_surface (cairo_xlib_surface_t *surface, ret = XInitImage (&ximage); assert (ret != 0); - } else { + } + else + { unsigned int stride, rowstride; int x, y, x0, y0, x_off, y_off; uint32_t in_pixel, out_pixel, *row; @@ -976,15 +1098,14 @@ _draw_image_surface (cairo_xlib_surface_t *surface, cairo_bool_t true_color; int ret; - if (surface->depth > 16) { + if (surface->depth > 16) ximage.bits_per_pixel = 32; - } else if (surface->depth > 8) { + else if (surface->depth > 8) ximage.bits_per_pixel = 16; - } else if (surface->depth > 1) { + else if (surface->depth > 1) ximage.bits_per_pixel = 8; - } else { + else ximage.bits_per_pixel = 1; - } stride = CAIRO_STRIDE_FOR_WIDTH_BPP (ximage.width, ximage.bits_per_pixel); ximage.bytes_per_line = stride; @@ -1010,7 +1131,7 @@ _draw_image_surface (cairo_xlib_surface_t *surface, _characterize_field (surface->g_mask, &o_g_width, &o_g_shift); _characterize_field (surface->b_mask, &o_b_width, &o_b_shift); } else { - status = _cairo_xlib_screen_get_visual_info (surface->screen_info, + status = _cairo_xlib_screen_get_visual_info (surface->screen, surface->visual, &visual_info); if (unlikely (status)) @@ -1034,10 +1155,22 @@ _draw_image_surface (cairo_xlib_surface_t *surface, int dither_adjustment = dither_row[x_off]; int a, r, g, b; - if (image_masks.bpp <= 8) + if (image_masks.bpp == 1) + in_pixel = !! (((uint8_t*)row)[x/8] & (1 << (x & 7))); + else if (image_masks.bpp <= 8) in_pixel = ((uint8_t*)row)[x]; else if (image_masks.bpp <= 16) in_pixel = ((uint16_t*)row)[x]; + else if (image_masks.bpp <= 24) +#ifdef WORDS_BIGENDIAN + in_pixel = ((uint8_t*)row)[3 * x] << 16 | + ((uint8_t*)row)[3 * x + 1] << 8 | + ((uint8_t*)row)[3 * x + 2]; +#else + in_pixel = ((uint8_t*)row)[3 * x] | + ((uint8_t*)row)[3 * x + 1] << 8 | + ((uint8_t*)row)[3 * x + 2] << 16; +#endif else in_pixel = row[x]; @@ -1069,19 +1202,23 @@ _draw_image_surface (cairo_xlib_surface_t *surface, } } - status = _cairo_xlib_surface_ensure_gc (surface); + status = _cairo_xlib_surface_get_gc (surface, &gc); if (unlikely (status)) goto BAIL; - XPutImage(surface->dpy, surface->drawable, surface->gc, - &ximage, src_x, src_y, dst_x, dst_y, - width, height); + XPutImage (surface->dpy, surface->drawable, gc, + &ximage, src_x, src_y, dst_x, dst_y, + width, height); + + _cairo_xlib_surface_put_gc (surface, gc); BAIL: if (own_data) free (ximage.data); + if (pixman_image) + pixman_image_unref (pixman_image); - return status; + return CAIRO_STATUS_SUCCESS; } static cairo_status_t @@ -1175,17 +1312,16 @@ _cairo_xlib_surface_release_dest_image (void *abstract_surfac * screen. Both core and Render drawing require this * when using multiple drawables in an operation. */ -static cairo_bool_t +static inline cairo_bool_t _cairo_xlib_surface_same_screen (cairo_xlib_surface_t *dst, cairo_xlib_surface_t *src) { - return dst->dpy == src->dpy && dst->screen == src->screen; + return dst->screen == src->screen; } static cairo_status_t _cairo_xlib_surface_clone_similar (void *abstract_surface, cairo_surface_t *src, - cairo_content_t content, int src_x, int src_y, int width, @@ -1212,25 +1348,18 @@ _cairo_xlib_surface_clone_similar (void *abstract_surface, } } else if (_cairo_surface_is_image (src)) { cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; - cairo_format_t format; if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return CAIRO_INT_STATUS_UNSUPPORTED; + return UNSUPPORTED ("roi too large for xlib"); - format = image_src->format; - if (format == CAIRO_FORMAT_INVALID || - (_cairo_content_from_format (format) & ~content)) - { - format = _cairo_format_from_content (image_src->base.content & content); - } clone = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_similar_with_format (surface, - format, - width, height); + _cairo_xlib_surface_create_similar (surface, + image_src->base.content, + width, height); if (clone == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; + return UNSUPPORTED ("unhandled image format, no similar surface"); - if (clone->base.status) + if (unlikely (clone->base.status)) return clone->base.status; status = _draw_image_surface (clone, image_src, @@ -1277,9 +1406,6 @@ _cairo_xlib_surface_create_solid_pattern_surface (void *abstrac if (CAIRO_SURFACE_RENDER_HAS_COMPOSITE (other)) return NULL; - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return NULL; - image = (cairo_image_surface_t *) _cairo_image_surface_create_with_content (solid_pattern->content, width, height); @@ -1293,9 +1419,9 @@ _cairo_xlib_surface_create_solid_pattern_surface (void *abstrac other->depth); surface = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_internal (other->dpy, + _cairo_xlib_surface_create_internal (other->screen, pixmap, - other->screen, other->visual, + other->visual, other->xrender_format, width, height, other->depth); @@ -1305,7 +1431,8 @@ _cairo_xlib_surface_create_solid_pattern_surface (void *abstrac status = _cairo_surface_paint (&image->base, CAIRO_OPERATOR_SOURCE, - &solid_pattern->base, NULL); + &solid_pattern->base, + NULL); if (unlikely (status)) goto BAIL; @@ -1339,7 +1466,6 @@ _cairo_xlib_surface_can_repaint_solid_pattern_surface (void *abstract_surface, return CAIRO_SURFACE_RENDER_HAS_COMPOSITE (other); } - static cairo_status_t _cairo_xlib_surface_set_matrix (cairo_xlib_surface_t *surface, cairo_matrix_t *matrix, @@ -1358,8 +1484,8 @@ _cairo_xlib_surface_set_matrix (cairo_xlib_surface_t *surface, if (memcmp (&xtransform, &surface->xtransform, sizeof (XTransform)) == 0) return CAIRO_STATUS_SUCCESS; - if (!CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (! CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) + return UNSUPPORTED ("XRender does not support picture transforms"); XRenderSetPictureTransform (surface->dpy, surface->src_picture, &xtransform); surface->xtransform = xtransform; @@ -1376,12 +1502,11 @@ _cairo_xlib_surface_set_filter (cairo_xlib_surface_t *surface, if (surface->filter == filter) return CAIRO_STATUS_SUCCESS; - if (!CAIRO_SURFACE_RENDER_HAS_FILTERS (surface)) - { + if (!CAIRO_SURFACE_RENDER_HAS_FILTERS (surface)) { if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) return CAIRO_STATUS_SUCCESS; - return CAIRO_INT_STATUS_UNSUPPORTED; + return UNSUPPORTED ("XRender does not support filter"); } switch (filter) { @@ -1418,20 +1543,62 @@ _cairo_xlib_surface_set_filter (cairo_xlib_surface_t *surface, return CAIRO_STATUS_SUCCESS; } -static void -_cairo_xlib_surface_set_repeat (cairo_xlib_surface_t *surface, int repeat) +static cairo_status_t +_cairo_xlib_surface_set_repeat (cairo_xlib_surface_t *surface, + cairo_extend_t extend, + unsigned long *mask, + XRenderPictureAttributes *pa) { - XRenderPictureAttributes pa; - unsigned long mask; + int repeat; - if (surface->repeat == repeat) - return; + if (surface->extend == extend) + return CAIRO_STATUS_SUCCESS; - mask = CPRepeat; - pa.repeat = repeat; + switch (extend) { + case CAIRO_EXTEND_NONE: + repeat = RepeatNone; + break; + case CAIRO_EXTEND_REPEAT: + repeat = RepeatNormal; + break; + case CAIRO_EXTEND_REFLECT: + if (surface->buggy_pad_reflect) + return UNSUPPORTED ("buggy reflect"); - XRenderChangePicture (surface->dpy, surface->src_picture, mask, &pa); - surface->repeat = repeat; + repeat = RepeatReflect; + break; + case CAIRO_EXTEND_PAD: + if (surface->buggy_pad_reflect) + return UNSUPPORTED ("buggy pad"); + + repeat = RepeatPad; + break; + default: + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + *mask |= CPRepeat; + pa->repeat = repeat; + + surface->extend = extend; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xlib_surface_set_component_alpha (cairo_xlib_surface_t *surface, + cairo_bool_t ca, + unsigned long *mask, + XRenderPictureAttributes *pa) +{ + if (surface->has_component_alpha == ca) + return CAIRO_STATUS_SUCCESS; + + *mask |= CPComponentAlpha; + pa->component_alpha = ca; + + surface->has_component_alpha = ca; + return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t @@ -1441,6 +1608,8 @@ _cairo_xlib_surface_set_attributes (cairo_xlib_surface_t *surface, double yc) { cairo_int_status_t status; + XRenderPictureAttributes pa; + unsigned long mask = 0; _cairo_xlib_surface_ensure_src_picture (surface); @@ -1449,31 +1618,24 @@ _cairo_xlib_surface_set_attributes (cairo_xlib_surface_t *surface, if (unlikely (status)) return status; - switch (attributes->extend) { - case CAIRO_EXTEND_NONE: - _cairo_xlib_surface_set_repeat (surface, RepeatNone); - break; - case CAIRO_EXTEND_REPEAT: - _cairo_xlib_surface_set_repeat (surface, RepeatNormal); - break; - case CAIRO_EXTEND_REFLECT: - if (surface->buggy_pad_reflect) - return CAIRO_INT_STATUS_UNSUPPORTED; - _cairo_xlib_surface_set_repeat (surface, RepeatReflect); - break; - case CAIRO_EXTEND_PAD: - if (surface->buggy_pad_reflect) - return CAIRO_INT_STATUS_UNSUPPORTED; - _cairo_xlib_surface_set_repeat (surface, RepeatPad); - break; - default: - return CAIRO_INT_STATUS_UNSUPPORTED; - } + status = _cairo_xlib_surface_set_repeat (surface, attributes->extend, + &mask, &pa); + if (unlikely (status)) + return status; + + status = _cairo_xlib_surface_set_component_alpha (surface, + attributes->has_component_alpha, + &mask, &pa); + if (unlikely (status)) + return status; status = _cairo_xlib_surface_set_filter (surface, attributes->filter); if (unlikely (status)) return status; + if (mask) + XRenderChangePicture (surface->dpy, surface->src_picture, mask, &pa); + return CAIRO_STATUS_SUCCESS; } @@ -1486,7 +1648,7 @@ _surfaces_compatible (cairo_xlib_surface_t *dst, cairo_xlib_surface_t *src) { /* same screen */ - if (!_cairo_xlib_surface_same_screen (dst, src)) + if (! _cairo_xlib_surface_same_screen (dst, src)) return FALSE; /* same depth (for core) */ @@ -1516,7 +1678,6 @@ _surface_has_alpha (cairo_xlib_surface_t *surface) else return FALSE; } else { - /* In the no-render case, we never have alpha */ return FALSE; } @@ -1576,27 +1737,38 @@ _categorize_composite_operation (cairo_xlib_surface_t *dst, cairo_bool_t have_mask) { - if (!dst->buggy_repeat) + if (!CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR (dst, op)) + return DO_UNSUPPORTED; + + if (! dst->buggy_repeat) return DO_RENDER; - if (src_pattern->type == CAIRO_PATTERN_TYPE_SURFACE) + if (src_pattern->type != CAIRO_PATTERN_TYPE_SOLID && + src_pattern->extend == CAIRO_EXTEND_REPEAT) { - cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *)src_pattern; - - if (_cairo_matrix_is_integer_translation (&src_pattern->matrix, NULL, NULL) && - src_pattern->extend == CAIRO_EXTEND_REPEAT) + /* Check for the bug with repeat patterns nad general transforms. */ + if (! _cairo_matrix_is_integer_translation (&src_pattern->matrix, + NULL, NULL)) { + return DO_UNSUPPORTED; + } + + if (have_mask || + !(op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER)) + { + return DO_UNSUPPORTED; + } + + if (src_pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) src_pattern; + /* This is the case where we have the bug involving * untransformed repeating source patterns with off-screen * video memory; reject some cases where a core protocol * fallback is impossible. */ - if (have_mask || - !(op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER)) - return DO_UNSUPPORTED; - if (_cairo_surface_is_xlib (surface_pattern->surface)) { - cairo_xlib_surface_t *src = (cairo_xlib_surface_t *)surface_pattern->surface; + cairo_xlib_surface_t *src = (cairo_xlib_surface_t *) surface_pattern->surface; if (op == CAIRO_OPERATOR_OVER && _surface_has_alpha (src)) return DO_UNSUPPORTED; @@ -1607,15 +1779,11 @@ _categorize_composite_operation (cairo_xlib_surface_t *dst, */ if (_cairo_xlib_surface_same_screen (dst, src) && !_surfaces_compatible (dst, src)) + { return DO_UNSUPPORTED; + } } } - - /* Check for the other bug involving repeat patterns with general - * transforms. */ - if (!_cairo_matrix_is_integer_translation (&src_pattern->matrix, NULL, NULL) && - src_pattern->extend == CAIRO_EXTEND_REPEAT) - return DO_UNSUPPORTED; } return DO_RENDER; @@ -1636,42 +1804,25 @@ _recategorize_composite_operation (cairo_xlib_surface_t *dst, cairo_surface_attributes_t *src_attr, cairo_bool_t have_mask) { - cairo_bool_t is_integer_translation = - _cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL); - cairo_bool_t needs_alpha_composite; - - if (! _cairo_surface_is_xlib (&src->base)) - return DO_UNSUPPORTED; - - needs_alpha_composite = - _operator_needs_alpha_composite (op, - _surface_has_alpha (dst), - _surface_has_alpha (src)); - + /* Can we use the core protocol? */ if (! have_mask && - is_integer_translation && - src_attr->extend == CAIRO_EXTEND_NONE && - ! needs_alpha_composite && - _surfaces_compatible (src, dst)) + src->depth == dst->depth && + _cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) && + ! _operator_needs_alpha_composite (op, + _surface_has_alpha (dst), + _surface_has_alpha (src))) { - return DO_XCOPYAREA; - } + if (src_attr->extend == CAIRO_EXTEND_NONE) + return DO_XCOPYAREA; - if (dst->buggy_repeat && - is_integer_translation && - src_attr->extend == CAIRO_EXTEND_REPEAT && - (src->width != 1 || src->height != 1)) - { - if (! have_mask && - ! needs_alpha_composite && - _surfaces_compatible (dst, src)) - { + if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT) return DO_XTILE; - } - - return DO_UNSUPPORTED; } + if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT && + (src->width != 1 || src->height != 1)) + return DO_UNSUPPORTED; + if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src)) return DO_UNSUPPORTED; @@ -1716,11 +1867,287 @@ _render_operator (cairo_operator_t op) return PictOpAdd; case CAIRO_OPERATOR_SATURATE: return PictOpSaturate; + + case CAIRO_OPERATOR_MULTIPLY: + return PictOpMultiply; + case CAIRO_OPERATOR_SCREEN: + return PictOpScreen; + case CAIRO_OPERATOR_OVERLAY: + return PictOpOverlay; + case CAIRO_OPERATOR_DARKEN: + return PictOpDarken; + case CAIRO_OPERATOR_LIGHTEN: + return PictOpLighten; + case CAIRO_OPERATOR_COLOR_DODGE: + return PictOpColorDodge; + case CAIRO_OPERATOR_COLOR_BURN: + return PictOpColorBurn; + case CAIRO_OPERATOR_HARD_LIGHT: + return PictOpHardLight; + case CAIRO_OPERATOR_SOFT_LIGHT: + return PictOpSoftLight; + case CAIRO_OPERATOR_DIFFERENCE: + return PictOpDifference; + case CAIRO_OPERATOR_EXCLUSION: + return PictOpExclusion; + case CAIRO_OPERATOR_HSL_HUE: + return PictOpHSLHue; + case CAIRO_OPERATOR_HSL_SATURATION: + return PictOpHSLSaturation; + case CAIRO_OPERATOR_HSL_COLOR: + return PictOpHSLColor; + case CAIRO_OPERATOR_HSL_LUMINOSITY: + return PictOpHSLLuminosity; + default: + ASSERT_NOT_REACHED; return PictOpOver; } } +static cairo_int_status_t +_cairo_xlib_surface_acquire_pattern_surface (cairo_xlib_surface_t *dst, + const cairo_pattern_t *pattern, + int x, int y, + int width, int height, + cairo_xlib_surface_t **surface_out, + cairo_surface_attributes_t *attributes) +{ + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + { + cairo_gradient_pattern_t *gradient = + (cairo_gradient_pattern_t *) pattern; + cairo_matrix_t matrix = pattern->matrix; + cairo_xlib_surface_t *surface; + char buf[CAIRO_STACK_BUFFER_SIZE]; + XFixed *stops; + XRenderColor *colors; + XRenderPictFormat *format; + Picture picture; + unsigned int i; + + if (dst->buggy_gradients) + break; + + if (gradient->n_stops < 2) /* becomes a solid */ + break; + + if (gradient->n_stops < sizeof (buf) / (sizeof (XFixed) + sizeof (XRenderColor))) + { + stops = (XFixed *) buf; + } + else + { + stops = + _cairo_malloc_ab (gradient->n_stops, + sizeof (XFixed) + sizeof (XRenderColor)); + if (unlikely (stops == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + colors = (XRenderColor *) (stops + gradient->n_stops); + for (i = 0; i < gradient->n_stops; i++) { + stops[i] = + _cairo_fixed_16_16_from_double (gradient->stops[i].offset); + + colors[i].red = gradient->stops[i].color.red_short; + colors[i].green = gradient->stops[i].color.green_short; + colors[i].blue = gradient->stops[i].color.blue_short; + colors[i].alpha = gradient->stops[i].color.alpha_short; + } + +#if 0 + /* For some weird reason the X server is sometimes getting + * CreateGradient requests with bad length. So far I've only seen + * XRenderCreateLinearGradient request with 4 stops sometime end up + * with length field matching 0 stops at the server side. I've + * looked at the libXrender code and I can't see anything that + * could cause this behavior. However, for some reason having a + * XSync call here seems to avoid the issue so I'll keep it here + * until it's solved. + */ + XSync (dst->dpy, False); +#endif + + if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { + cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern; + XLinearGradient grad; + + cairo_fixed_t xdim, ydim; + + xdim = linear->p2.x - linear->p1.x; + ydim = linear->p2.y - linear->p1.y; + + /* + * Transform the matrix to avoid overflow when converting between + * cairo_fixed_t and pixman_fixed_t (without incurring performance + * loss when the transformation is unnecessary). + * + * XXX: Consider converting out-of-range co-ordinates and transforms. + * Having a function to compute the required transformation to + * "normalize" a given bounding box would be generally useful - + * cf linear patterns, gradient patterns, surface patterns... + */ +#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ + if (_cairo_fixed_integer_ceil (xdim) > PIXMAN_MAX_INT || + _cairo_fixed_integer_ceil (ydim) > PIXMAN_MAX_INT) + { + double sf; + + if (xdim > ydim) + sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (xdim); + else + sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (ydim); + + grad.p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf); + grad.p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf); + grad.p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf); + grad.p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf); + + cairo_matrix_scale (&matrix, sf, sf); + } + else + { + grad.p1.x = _cairo_fixed_to_16_16 (linear->p1.x); + grad.p1.y = _cairo_fixed_to_16_16 (linear->p1.y); + grad.p2.x = _cairo_fixed_to_16_16 (linear->p2.x); + grad.p2.y = _cairo_fixed_to_16_16 (linear->p2.y); + } + + picture = XRenderCreateLinearGradient (dst->dpy, &grad, + stops, colors, + gradient->n_stops); + } else { + cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; + XRadialGradient grad; + + grad.inner.x = _cairo_fixed_to_16_16 (radial->c1.x); + grad.inner.y = _cairo_fixed_to_16_16 (radial->c1.y); + grad.inner.radius = _cairo_fixed_to_16_16 (radial->r1); + + grad.outer.x = _cairo_fixed_to_16_16 (radial->c2.x); + grad.outer.y = _cairo_fixed_to_16_16 (radial->c2.y); + grad.outer.radius = _cairo_fixed_to_16_16 (radial->r2); + + picture = XRenderCreateRadialGradient (dst->dpy, &grad, + stops, colors, + gradient->n_stops); + + } + + if (stops != (XFixed *) buf) + free (stops); + + if (unlikely (picture == None)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + /* Wrap the remote Picture in an xlib surface. */ + format = _cairo_xlib_display_get_xrender_format (dst->display, + CAIRO_FORMAT_ARGB32); + + surface = (cairo_xlib_surface_t *) + _cairo_xlib_surface_create_internal (dst->screen, None, + NULL, format, + 0, 0, 32); + if (unlikely (surface->base.status)) { + XRenderFreePicture (dst->dpy, picture); + return surface->base.status; + } + + surface->src_picture = picture; + + attributes->matrix = matrix; + attributes->extend = pattern->extend; + attributes->filter = CAIRO_FILTER_NEAREST; + attributes->x_offset = 0; + attributes->y_offset = 0; + attributes->has_component_alpha = FALSE; + + *surface_out = surface; + return CAIRO_STATUS_SUCCESS; + } + default: + ASSERT_NOT_REACHED; + case CAIRO_PATTERN_TYPE_SOLID: + case CAIRO_PATTERN_TYPE_SURFACE: + break; + } + + return _cairo_pattern_acquire_surface (pattern, &dst->base, + x, y, width, height, + dst->buggy_pad_reflect ? + CAIRO_PATTERN_ACQUIRE_NO_REFLECT : + CAIRO_PATTERN_ACQUIRE_NONE, + (cairo_surface_t **) surface_out, + attributes); +} + +static cairo_int_status_t +_cairo_xlib_surface_acquire_pattern_surfaces (cairo_xlib_surface_t *dst, + const cairo_pattern_t *src, + const cairo_pattern_t *mask, + int src_x, + int src_y, + int mask_x, + int mask_y, + unsigned int width, + unsigned int height, + cairo_xlib_surface_t **src_out, + cairo_xlib_surface_t **mask_out, + cairo_surface_attributes_t *src_attr, + cairo_surface_attributes_t *mask_attr) +{ + if (! dst->buggy_gradients && + (src->type == CAIRO_PATTERN_TYPE_LINEAR || + src->type == CAIRO_PATTERN_TYPE_RADIAL || + (mask && (mask->type == CAIRO_PATTERN_TYPE_LINEAR || + mask->type == CAIRO_PATTERN_TYPE_RADIAL)))) + { + cairo_int_status_t status; + + status = _cairo_xlib_surface_acquire_pattern_surface (dst, src, + src_x, src_y, + width, height, + src_out, + src_attr); + if (unlikely (status)) + return status; + + if (mask) { + status = _cairo_xlib_surface_acquire_pattern_surface (dst, mask, + mask_x, + mask_y, + width, + height, + mask_out, + mask_attr); + if (unlikely (status)) { + _cairo_pattern_release_surface (src, &(*src_out)->base, + src_attr); + return status; + } + } else { + *mask_out = NULL; + } + + return CAIRO_STATUS_SUCCESS; + } + + return _cairo_pattern_acquire_surfaces (src, mask, + &dst->base, + src_x, src_y, + mask_x, mask_y, + width, height, + dst->buggy_pad_reflect ? + CAIRO_PATTERN_ACQUIRE_NO_REFLECT : + CAIRO_PATTERN_ACQUIRE_NONE, + (cairo_surface_t **) src_out, + (cairo_surface_t **) mask_out, + src_attr, mask_attr); +} + static cairo_int_status_t _cairo_xlib_surface_composite (cairo_operator_t op, const cairo_pattern_t *src_pattern, @@ -1733,7 +2160,8 @@ _cairo_xlib_surface_composite (cairo_operator_t op, int dst_x, int dst_y, unsigned int width, - unsigned int height) + unsigned int height, + cairo_region_t *clip_region) { cairo_surface_attributes_t src_attr, mask_attr; cairo_xlib_surface_t *dst = abstract_dst; @@ -1744,55 +2172,49 @@ _cairo_xlib_surface_composite (cairo_operator_t op, int itx, ity; cairo_bool_t is_integer_translation; cairo_bool_t needs_alpha_composite; - cairo_content_t src_content; + GC gc; - _cairo_xlib_display_notify (dst->display); + if (mask_pattern != NULL && ! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) + return UNSUPPORTED ("no support for masks"); operation = _categorize_composite_operation (dst, op, src_pattern, mask_pattern != NULL); if (operation == DO_UNSUPPORTED) - return CAIRO_INT_STATUS_UNSUPPORTED; + return UNSUPPORTED ("unsupported operation"); + + X_DEBUG ((dst->dpy, "composite (dst=%x)", (unsigned int) dst->drawable)); needs_alpha_composite = _operator_needs_alpha_composite (op, _surface_has_alpha (dst), ! _cairo_pattern_is_opaque (src_pattern)); - src_content = CAIRO_CONTENT_COLOR_ALPHA; - if (! needs_alpha_composite) - src_content &= ~CAIRO_CONTENT_ALPHA; - status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern, - &dst->base, - src_content, - src_x, src_y, - mask_x, mask_y, - width, height, - dst->buggy_pad_reflect ? - CAIRO_PATTERN_ACQUIRE_NO_REFLECT : - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) &src, - (cairo_surface_t **) &mask, - &src_attr, &mask_attr); + _cairo_xlib_display_notify (dst->display); + + status = + _cairo_xlib_surface_acquire_pattern_surfaces (dst, + src_pattern, mask_pattern, + src_x, src_y, + mask_x, mask_y, + width, height, + &src, &mask, + &src_attr, &mask_attr); if (unlikely (status)) return status; /* check for fallback surfaces that we cannot handle ... */ - if (!_cairo_surface_is_xlib (&src->base)) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto BAIL; - } - if (mask != NULL && - (! _cairo_surface_is_xlib (&mask->base) || - ! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst))) - { - status = CAIRO_INT_STATUS_UNSUPPORTED; + assert (_cairo_surface_is_xlib (&src->base)); + assert (mask == NULL || _cairo_surface_is_xlib (&mask->base)); + + if (mask != NULL && ! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (mask)) { + status = UNSUPPORTED ("unsupported mask"); goto BAIL; } operation = _recategorize_composite_operation (dst, op, src, &src_attr, mask_pattern != NULL); if (operation == DO_UNSUPPORTED) { - status = CAIRO_INT_STATUS_UNSUPPORTED; + status = UNSUPPORTED ("unsupported operation"); goto BAIL; } @@ -1805,6 +2227,10 @@ _cairo_xlib_surface_composite (cairo_operator_t op, if (unlikely (status)) goto BAIL; + status = _cairo_xlib_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + goto BAIL; + _cairo_xlib_surface_ensure_dst_picture (dst); if (mask) { status = _cairo_xlib_surface_set_attributes (mask, &mask_attr, @@ -1840,23 +2266,40 @@ _cairo_xlib_surface_composite (cairo_operator_t op, break; case DO_XCOPYAREA: - status = _cairo_xlib_surface_ensure_gc (dst); + status = _cairo_xlib_surface_get_gc (dst, &gc); if (unlikely (status)) goto BAIL; - is_integer_translation = _cairo_matrix_is_integer_translation (&src_attr.matrix, - &itx, &ity); + is_integer_translation = + _cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity); /* This is a pre-condition for DO_XCOPYAREA. */ assert (is_integer_translation); - XCopyArea (dst->dpy, - src->drawable, - dst->drawable, - dst->gc, - src_x + src_attr.x_offset + itx, - src_y + src_attr.y_offset + ity, - width, height, - dst_x, dst_y); + if (clip_region == NULL) { + XCopyArea (dst->dpy, src->drawable, dst->drawable, gc, + src_x + src_attr.x_offset + itx, + src_y + src_attr.y_offset + ity, + width, height, + dst_x, dst_y); + } else { + int n, num_rects; + + src_x += src_attr.x_offset + itx - dst_x; + src_y += src_attr.y_offset + ity - dst_y; + + num_rects = cairo_region_num_rectangles (clip_region); + for (n = 0; n < num_rects; n++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (clip_region, n, &rect); + XCopyArea (dst->dpy, src->drawable, dst->drawable, gc, + rect.x + src_x, rect.y + src_y, + rect.width, rect.height, + rect.x, rect.y); + } + } + + _cairo_xlib_surface_put_gc (dst, gc); break; case DO_XTILE: @@ -1868,21 +2311,36 @@ _cairo_xlib_surface_composite (cairo_operator_t op, * _recategorize_composite_operation. */ - status = _cairo_xlib_surface_ensure_gc (dst); + status = _cairo_xlib_surface_get_gc (dst, &gc); if (unlikely (status)) goto BAIL; - is_integer_translation = _cairo_matrix_is_integer_translation (&src_attr.matrix, - &itx, &ity); + + is_integer_translation = + _cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity); /* This is a pre-condition for DO_XTILE. */ assert (is_integer_translation); - XSetTSOrigin (dst->dpy, dst->gc, + XSetTSOrigin (dst->dpy, gc, - (itx + src_attr.x_offset), - (ity + src_attr.y_offset)); - XSetTile (dst->dpy, dst->gc, src->drawable); - XSetFillStyle (dst->dpy, dst->gc, FillTiled); + XSetTile (dst->dpy, gc, src->drawable); - XFillRectangle (dst->dpy, dst->drawable, dst->gc, - dst_x, dst_y, width, height); + if (clip_region == NULL) { + XFillRectangle (dst->dpy, dst->drawable, gc, + dst_x, dst_y, width, height); + } else { + int n, num_rects; + + num_rects = cairo_region_num_rectangles (clip_region); + for (n = 0; n < num_rects; n++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (clip_region, n, &rect); + XFillRectangle (dst->dpy, dst->drawable, gc, + rect.x, rect.y, rect.width, rect.height); + } + } + + _cairo_xlib_surface_put_gc (dst, gc); break; case DO_UNSUPPORTED: @@ -1898,7 +2356,8 @@ _cairo_xlib_surface_composite (cairo_operator_t op, mask ? mask->height : 0, src_x, src_y, mask_x, mask_y, - dst_x, dst_y, width, height); + dst_x, dst_y, width, height, + clip_region); BAIL: if (mask) @@ -1909,6 +2368,7 @@ _cairo_xlib_surface_composite (cairo_operator_t op, return status; } +/* XXX move this out of core and into acquire_pattern_surface() above. */ static cairo_int_status_t _cairo_xlib_surface_solid_fill_rectangles (cairo_xlib_surface_t *surface, const cairo_color_t *color, @@ -1919,47 +2379,48 @@ _cairo_xlib_surface_solid_fill_rectangles (cairo_xlib_surface_t *surface, cairo_solid_pattern_t solid; cairo_surface_t *solid_surface = NULL; cairo_surface_attributes_t attrs; + GC gc; int i; _cairo_pattern_init_solid (&solid, color, CAIRO_CONTENT_COLOR); - status = _cairo_xlib_surface_ensure_gc (surface); + status = _cairo_xlib_surface_get_gc (surface, &gc); if (unlikely (status)) return status; + X_DEBUG ((surface->dpy, "solid_fill_rectangles (dst=%x)", (unsigned int) surface->drawable)); + status = _cairo_pattern_acquire_surface (&solid.base, &surface->base, - CAIRO_CONTENT_COLOR_ALPHA, 0, 0, ARRAY_LENGTH (dither_pattern[0]), ARRAY_LENGTH (dither_pattern), CAIRO_PATTERN_ACQUIRE_NONE, &solid_surface, &attrs); - if (unlikely (status)) - return status; - - if (! _cairo_surface_is_xlib (solid_surface)) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto BAIL; + if (unlikely (status)) { + _cairo_xlib_surface_put_gc (surface, gc); + return status; } - XSetTSOrigin (surface->dpy, surface->gc, + assert (_cairo_surface_is_xlib (solid_surface)); + + XSetTSOrigin (surface->dpy, gc, - (surface->base.device_transform.x0 + attrs.x_offset), - (surface->base.device_transform.y0 + attrs.y_offset)); - XSetTile (surface->dpy, surface->gc, + XSetTile (surface->dpy, gc, ((cairo_xlib_surface_t *) solid_surface)->drawable); - XSetFillStyle (surface->dpy, surface->gc, FillTiled); for (i = 0; i < num_rects; i++) { - XFillRectangle (surface->dpy, surface->drawable, surface->gc, + XFillRectangle (surface->dpy, surface->drawable, gc, rects[i].x, rects[i].y, rects[i].width, rects[i].height); } - BAIL: + _cairo_xlib_surface_put_gc (surface, gc); + _cairo_pattern_release_surface (&solid.base, solid_surface, &attrs); - return status; + return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t @@ -1971,11 +2432,15 @@ _cairo_xlib_surface_fill_rectangles (void *abstract_surface, { cairo_xlib_surface_t *surface = abstract_surface; XRenderColor render_color; + cairo_status_t status; int i; _cairo_xlib_display_notify (surface->display); - if (! CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES (surface)) { + if (!CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR (surface, op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (!CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES (surface)) { if (op == CAIRO_OPERATOR_CLEAR || ((op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER) && CAIRO_COLOR_IS_OPAQUE (color))) @@ -1984,14 +2449,19 @@ _cairo_xlib_surface_fill_rectangles (void *abstract_surface, rects, num_rects); } - return CAIRO_INT_STATUS_UNSUPPORTED; + return UNSUPPORTED ("no support for FillRectangles with this op"); } + X_DEBUG ((surface->dpy, "fill_rectangles (dst=%x)", (unsigned int) surface->drawable)); + render_color.red = color->red_short; render_color.green = color->green_short; render_color.blue = color->blue_short; render_color.alpha = color->alpha_short; + status = _cairo_xlib_surface_set_clip_region (surface, NULL); + assert (status == CAIRO_STATUS_SUCCESS); + _cairo_xlib_surface_ensure_dst_picture (surface); if (num_rects == 1) { /* Take advantage of the protocol compaction that libXrender performs @@ -2034,117 +2504,41 @@ _cairo_xlib_surface_fill_rectangles (void *abstract_surface, return CAIRO_STATUS_SUCCESS; } -/* Creates an A8 picture of size @width x @height, initialized with @color - */ -static Picture -_create_a8_picture (cairo_xlib_surface_t *surface, - XRenderColor *color, - int width, - int height, - cairo_bool_t repeat) +#define CAIRO_FIXED_16_16_MIN -32768 +#define CAIRO_FIXED_16_16_MAX 32767 + +static cairo_bool_t +_line_exceeds_16_16 (const cairo_line_t *line) { - XRenderPictureAttributes pa; - unsigned long mask = 0; - - Pixmap pixmap; - Picture picture; - XRenderPictFormat *xrender_format; - - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return None; - - xrender_format = - _cairo_xlib_display_get_xrender_format (surface->display, - CAIRO_FORMAT_A8); - if (xrender_format == NULL) - return None; - - pixmap = XCreatePixmap (surface->dpy, surface->drawable, - width <= 0 ? 1 : width, - height <= 0 ? 1 : height, - 8); - - if (repeat) { - pa.repeat = TRUE; - mask = CPRepeat; - } - - picture = XRenderCreatePicture (surface->dpy, pixmap, - xrender_format, mask, &pa); - XRenderFillRectangle (surface->dpy, PictOpSrc, picture, color, - 0, 0, width, height); - XFreePixmap (surface->dpy, pixmap); - - return picture; + return + line->p1.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || + line->p1.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || + line->p2.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || + line->p2.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || + line->p1.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || + line->p1.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || + line->p2.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || + line->p2.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX); } -/* Creates a temporary mask for the trapezoids covering the area - * [@dst_x, @dst_y, @width, @height] of the destination surface. - */ -static Picture -_create_trapezoid_mask (cairo_xlib_surface_t *dst, - cairo_trapezoid_t *traps, - int num_traps, - int dst_x, - int dst_y, - int width, - int height, - XRenderPictFormat *pict_format) +static void +_project_line_x_onto_16_16 (const cairo_line_t *line, + cairo_fixed_t top, + cairo_fixed_t bottom, + XLineFixed *out) { - XRenderColor transparent = { 0, 0, 0, 0 }; - XRenderColor solid = { 0xffff, 0xffff, 0xffff, 0xffff }; - Picture mask_picture, solid_picture; - XTrapezoid *offset_traps; - int i; + cairo_point_double_t p1, p2; + double m; - /* This would be considerably simpler using XRenderAddTraps(), but since - * we are only using this in the unbounded-operator case, we stick with - * XRenderCompositeTrapezoids, which is available on older versions - * of RENDER rather than conditionalizing. We should still hit an - * optimization that avoids creating another intermediate surface on - * the servers that have XRenderAddTraps(). - */ - mask_picture = _create_a8_picture (dst, &transparent, width, height, FALSE); - if (mask_picture == None || num_traps == 0) - return mask_picture; + p1.x = _cairo_fixed_to_double (line->p1.x); + p1.y = _cairo_fixed_to_double (line->p1.y); - offset_traps = _cairo_malloc_ab (num_traps, sizeof (XTrapezoid)); - if (!offset_traps) { - XRenderFreePicture (dst->dpy, mask_picture); - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return None; - } + p2.x = _cairo_fixed_to_double (line->p2.x); + p2.y = _cairo_fixed_to_double (line->p2.y); - for (i = 0; i < num_traps; i++) { - offset_traps[i].top = _cairo_fixed_to_16_16(traps[i].top) - 0x10000 * dst_y; - offset_traps[i].bottom = _cairo_fixed_to_16_16(traps[i].bottom) - 0x10000 * dst_y; - offset_traps[i].left.p1.x = _cairo_fixed_to_16_16(traps[i].left.p1.x) - 0x10000 * dst_x; - offset_traps[i].left.p1.y = _cairo_fixed_to_16_16(traps[i].left.p1.y) - 0x10000 * dst_y; - offset_traps[i].left.p2.x = _cairo_fixed_to_16_16(traps[i].left.p2.x) - 0x10000 * dst_x; - offset_traps[i].left.p2.y = _cairo_fixed_to_16_16(traps[i].left.p2.y) - 0x10000 * dst_y; - offset_traps[i].right.p1.x = _cairo_fixed_to_16_16(traps[i].right.p1.x) - 0x10000 * dst_x; - offset_traps[i].right.p1.y = _cairo_fixed_to_16_16(traps[i].right.p1.y) - 0x10000 * dst_y; - offset_traps[i].right.p2.x = _cairo_fixed_to_16_16(traps[i].right.p2.x) - 0x10000 * dst_x; - offset_traps[i].right.p2.y = _cairo_fixed_to_16_16(traps[i].right.p2.y) - 0x10000 * dst_y; - } - - solid_picture = _create_a8_picture (dst, &solid, width, height, TRUE); - if (solid_picture == None) { - XRenderFreePicture (dst->dpy, mask_picture); - free (offset_traps); - return None; - } - - XRenderCompositeTrapezoids (dst->dpy, PictOpAdd, - solid_picture, mask_picture, - pict_format, - 0, 0, - offset_traps, num_traps); - - XRenderFreePicture (dst->dpy, solid_picture); - free (offset_traps); - - return mask_picture; + 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)); } static cairo_int_status_t @@ -2159,7 +2553,8 @@ _cairo_xlib_surface_composite_trapezoids (cairo_operator_t op, unsigned int width, unsigned int height, cairo_trapezoid_t *traps, - int num_traps) + int num_traps, + cairo_region_t *clip_region) { cairo_surface_attributes_t attributes; cairo_xlib_surface_t *dst = abstract_dst; @@ -2169,31 +2564,33 @@ _cairo_xlib_surface_composite_trapezoids (cairo_operator_t op, int render_reference_x, render_reference_y; int render_src_x, render_src_y; XRenderPictFormat *pict_format; + XTrapezoid xtraps_stack[CAIRO_STACK_ARRAY_LENGTH (XTrapezoid)]; + XTrapezoid *xtraps = xtraps_stack; + int i; _cairo_xlib_display_notify (dst->display); - if (!CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst)) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (! CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst)) + return UNSUPPORTED ("XRender does not support CompositeTrapezoids"); operation = _categorize_composite_operation (dst, op, pattern, TRUE); if (operation == DO_UNSUPPORTED) - return CAIRO_INT_STATUS_UNSUPPORTED; + return UNSUPPORTED ("unsupported operation"); - status = _cairo_pattern_acquire_surface (pattern, &dst->base, - CAIRO_CONTENT_COLOR_ALPHA, - src_x, src_y, width, height, - dst->buggy_pad_reflect ? - CAIRO_PATTERN_ACQUIRE_NO_REFLECT : - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) &src, - &attributes); + X_DEBUG ((dst->dpy, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable)); + + status = _cairo_xlib_surface_acquire_pattern_surface (dst, + pattern, + src_x, src_y, + width, height, + &src, &attributes); if (unlikely (status)) return status; operation = _recategorize_composite_operation (dst, op, src, &attributes, TRUE); if (operation == DO_UNSUPPORTED) { - status = CAIRO_INT_STATUS_UNSUPPORTED; + status = UNSUPPORTED ("unsupported operation"); goto BAIL; } @@ -2213,98 +2610,110 @@ _cairo_xlib_surface_composite_trapezoids (cairo_operator_t op, break; } - if (traps[0].left.p1.y < traps[0].left.p2.y) { - render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p1.x); - render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p1.y); - } else { - render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p2.x); - render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p2.y); - } - - render_src_x = src_x + render_reference_x - dst_x; - render_src_y = src_y + render_reference_y - dst_y; + status = _cairo_xlib_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + goto BAIL; _cairo_xlib_surface_ensure_dst_picture (dst); + status = _cairo_xlib_surface_set_attributes (src, &attributes, dst_x + width / 2., dst_y + height / 2.); if (unlikely (status)) goto BAIL; - if (!_cairo_operator_bounded_by_mask (op)) { - /* XRenderCompositeTrapezoids() creates a mask only large enough for the - * trapezoids themselves, but if the operator is unbounded, then we need - * to actually composite all the way out to the bounds, so we create - * the mask and composite ourselves. There actually would - * be benefit to doing this in all cases, since RENDER implementations - * will frequently create a too temporary big mask, ignoring destination - * bounds and clip. (XRenderAddTraps() could be used to make creating - * the mask somewhat cheaper.) - */ - Picture mask_picture = _create_trapezoid_mask (dst, traps, num_traps, - dst_x, dst_y, width, height, - pict_format); - if (!mask_picture) { + if (num_traps > ARRAY_LENGTH (xtraps_stack)) { + xtraps = _cairo_malloc_ab (num_traps, sizeof (XTrapezoid)); + if (unlikely (xtraps == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } + } - XRenderComposite (dst->dpy, - _render_operator (op), - src->src_picture, - mask_picture, - dst->dst_picture, - src_x + attributes.x_offset, - src_y + attributes.y_offset, - 0, 0, - dst_x, dst_y, - width, height); + for (i = 0; i < num_traps; i++) { + /* top/bottom will be clamped to surface bounds */ + xtraps[i].top = _cairo_fixed_to_16_16(traps[i].top); + xtraps[i].bottom = _cairo_fixed_to_16_16(traps[i].bottom); - XRenderFreePicture (dst->dpy, mask_picture); + /* 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 (&traps[i].left))) { + _project_line_x_onto_16_16 (&traps[i].left, + traps[i].top, + traps[i].bottom, + &xtraps[i].left); + xtraps[i].left.p1.y = xtraps[i].top; + xtraps[i].left.p2.y = xtraps[i].bottom; + } else { + xtraps[i].left.p1.x = _cairo_fixed_to_16_16(traps[i].left.p1.x); + xtraps[i].left.p1.y = _cairo_fixed_to_16_16(traps[i].left.p1.y); + xtraps[i].left.p2.x = _cairo_fixed_to_16_16(traps[i].left.p2.x); + xtraps[i].left.p2.y = _cairo_fixed_to_16_16(traps[i].left.p2.y); + } + + if (unlikely (_line_exceeds_16_16 (&traps[i].right))) { + _project_line_x_onto_16_16 (&traps[i].right, + traps[i].top, + traps[i].bottom, + &xtraps[i].right); + xtraps[i].right.p1.y = xtraps[i].top; + xtraps[i].right.p2.y = xtraps[i].bottom; + } else { + xtraps[i].right.p1.x = _cairo_fixed_to_16_16(traps[i].right.p1.x); + xtraps[i].right.p1.y = _cairo_fixed_to_16_16(traps[i].right.p1.y); + xtraps[i].right.p2.x = _cairo_fixed_to_16_16(traps[i].right.p2.x); + xtraps[i].right.p2.y = _cairo_fixed_to_16_16(traps[i].right.p2.y); + } + } + + if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) { + render_reference_x = _cairo_fixed_16_16_floor (xtraps[0].left.p1.x); + render_reference_y = _cairo_fixed_16_16_floor (xtraps[0].left.p1.y); + } else { + render_reference_x = _cairo_fixed_16_16_floor (xtraps[0].left.p2.x); + render_reference_y = _cairo_fixed_16_16_floor (xtraps[0].left.p2.y); + } + + render_src_x = src_x + render_reference_x - dst_x; + render_src_y = src_y + render_reference_y - dst_y; + + XRenderCompositeTrapezoids (dst->dpy, + _render_operator (op), + src->src_picture, dst->dst_picture, + pict_format, + render_src_x + attributes.x_offset, + render_src_y + attributes.y_offset, + xtraps, num_traps); + + if (xtraps != xtraps_stack) + free (xtraps); + + if (! _cairo_operator_bounded_by_mask (op)) { + cairo_traps_t _traps; + cairo_box_t box; + cairo_rectangle_int_t extents; + + /* XRenderCompositeTrapezoids() creates a mask only large enough for the + * trapezoids themselves, but if the operator is unbounded, then we need + * to actually composite all the way out to the bounds. + */ + /* XXX: update the interface to pass composite rects */ + _traps.traps = traps; + _traps.num_traps = num_traps; + _cairo_traps_extents (&_traps, &box); + _cairo_box_round_to_rectangle (&box, &extents); status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base, - &attributes, src->width, src->height, - width, height, + &attributes, + src->width, src->height, + extents.width, extents.height, src_x, src_y, - 0, 0, - dst_x, dst_y, width, height); - - } else { - XTrapezoid xtraps_stack[CAIRO_STACK_ARRAY_LENGTH (XTrapezoid)]; - XTrapezoid *xtraps = xtraps_stack; - int i; - - if (num_traps > ARRAY_LENGTH (xtraps_stack)) { - xtraps = _cairo_malloc_ab (num_traps, sizeof (XTrapezoid)); - if (unlikely (xtraps == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - } - - for (i = 0; i < num_traps; i++) { - xtraps[i].top = _cairo_fixed_to_16_16(traps[i].top); - xtraps[i].bottom = _cairo_fixed_to_16_16(traps[i].bottom); - xtraps[i].left.p1.x = _cairo_fixed_to_16_16(traps[i].left.p1.x); - xtraps[i].left.p1.y = _cairo_fixed_to_16_16(traps[i].left.p1.y); - xtraps[i].left.p2.x = _cairo_fixed_to_16_16(traps[i].left.p2.x); - xtraps[i].left.p2.y = _cairo_fixed_to_16_16(traps[i].left.p2.y); - xtraps[i].right.p1.x = _cairo_fixed_to_16_16(traps[i].right.p1.x); - xtraps[i].right.p1.y = _cairo_fixed_to_16_16(traps[i].right.p1.y); - xtraps[i].right.p2.x = _cairo_fixed_to_16_16(traps[i].right.p2.x); - xtraps[i].right.p2.y = _cairo_fixed_to_16_16(traps[i].right.p2.y); - } - - XRenderCompositeTrapezoids (dst->dpy, - _render_operator (op), - src->src_picture, dst->dst_picture, - pict_format, - render_src_x + attributes.x_offset, - render_src_y + attributes.y_offset, - xtraps, num_traps); - - if (xtraps != xtraps_stack) - free(xtraps); + -extents.x + dst_x, -extents.y + dst_y, + dst_x, dst_y, + width, height, + clip_region); } BAIL: @@ -2313,115 +2722,7 @@ _cairo_xlib_surface_composite_trapezoids (cairo_operator_t op, return status; } -static cairo_region_t * -_surface_maybe_clip_region (cairo_xlib_surface_t *surface, - cairo_region_t *clip, - cairo_region_t *bounded) -{ - cairo_rectangle_int_t rect; - - cairo_region_get_extents (clip, &rect); - if (rect.x >= 0 && - rect.y >= 0 && - rect.x + rect.width <= surface->width && - rect.y + rect.height <= surface->height) - { - return clip; - } - - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - _cairo_region_init_rectangle (bounded, &rect); - - bounded->status = cairo_region_intersect (bounded, clip); - - return bounded; -} - -static cairo_int_status_t -_cairo_xlib_surface_set_clip_region (void *abstract_surface, - cairo_region_t *region) -{ - cairo_xlib_surface_t *surface = abstract_surface; - cairo_bool_t had_clip_rects = surface->have_clip_rects; - - if (had_clip_rects == FALSE && region == NULL) - return CAIRO_STATUS_SUCCESS; - - if (surface->clip_rects != surface->embedded_clip_rects) { - free (surface->clip_rects); - surface->clip_rects = surface->embedded_clip_rects; - } - - surface->have_clip_rects = FALSE; - surface->num_clip_rects = 0; - - if (region != NULL) { - XRectangle *rects = NULL; - int n_rects, i; - cairo_region_t bounded; - - /* Intersect the region with the bounds of the surface. This - * is necessary so we don't wrap around when we convert cairo's - * 32 bit region into 16 bit rectangles. - */ - region = _surface_maybe_clip_region (surface, region, &bounded); - if (unlikely (region->status)) - return region->status; - - n_rects = cairo_region_num_rectangles (region); - if (n_rects > ARRAY_LENGTH (surface->embedded_clip_rects)) { - rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle)); - if (unlikely (rects == NULL)) { - if (unlikely (region == &bounded)) - _cairo_region_fini (&bounded); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - } else { - rects = surface->embedded_clip_rects; - } - - for (i = 0; i < n_rects; i++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (region, i, &rect); - - rects[i].x = rect.x; - rects[i].y = rect.y; - rects[i].width = rect.width; - rects[i].height = rect.height; - } - - if (unlikely (region == &bounded)) - _cairo_region_fini (&bounded); - - surface->have_clip_rects = TRUE; - surface->clip_rects = rects; - surface->num_clip_rects = n_rects; - - /* Discard the trivial clip rectangle that covers the entire surface */ - if (n_rects == 1 && - rects[0].x == 0 && - rects[0].y == 0 && - rects[0].width == surface->width && - rects[0].height == surface->height) - { - surface->have_clip_rects = FALSE; - surface->num_clip_rects = 0; - - if (! had_clip_rects) - goto DONE; - } - } - - surface->clip_dirty = CAIRO_XLIB_SURFACE_CLIP_DIRTY_ALL; - DONE: - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t +static cairo_bool_t _cairo_xlib_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { @@ -2433,7 +2734,7 @@ _cairo_xlib_surface_get_extents (void *abstract_surface, rectangle->width = surface->width; rectangle->height = surface->height; - return CAIRO_STATUS_SUCCESS; + return TRUE; } static void @@ -2442,7 +2743,7 @@ _cairo_xlib_surface_get_font_options (void *abstract_surface, { cairo_xlib_surface_t *surface = abstract_surface; - *options = *_cairo_xlib_screen_get_font_options (surface->screen_info); + *options = *_cairo_xlib_screen_get_font_options (surface->screen); } static void @@ -2461,7 +2762,7 @@ _cairo_xlib_surface_is_similar (void *surface_a, cairo_xlib_surface_t *b = surface_b; XRenderPictFormat *xrender_format = b->xrender_format; - if (!_cairo_xlib_surface_same_screen (a, b)) + if (! _cairo_xlib_surface_same_screen (a, b)) return FALSE; /* now inspect the content to check that a is similar to b */ @@ -2480,19 +2781,6 @@ _cairo_xlib_surface_is_similar (void *surface_a, return a->xrender_format == xrender_format; } -static cairo_status_t -_cairo_xlib_surface_reset (void *abstract_surface) -{ - cairo_xlib_surface_t *surface = abstract_surface; - cairo_status_t status; - - status = _cairo_xlib_surface_set_clip_region (surface, NULL); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; -} - static const cairo_surface_backend_t cairo_xlib_surface_backend = { CAIRO_SURFACE_TYPE_XLIB, _cairo_xlib_surface_create_similar, @@ -2509,8 +2797,6 @@ static const cairo_surface_backend_t cairo_xlib_surface_backend = { NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ - _cairo_xlib_surface_set_clip_region, - NULL, /* intersect_clip_path */ _cairo_xlib_surface_get_extents, NULL, /* old_show_glyphs */ _cairo_xlib_surface_get_font_options, @@ -2526,10 +2812,10 @@ static const cairo_surface_backend_t cairo_xlib_surface_backend = { _cairo_xlib_surface_show_glyphs, _cairo_xlib_surface_snapshot, - _cairo_xlib_surface_is_similar, - _cairo_xlib_surface_reset, + NULL, /* fill_stroke */ + _cairo_xlib_surface_create_solid_pattern_surface, _cairo_xlib_surface_can_repaint_solid_pattern_surface }; @@ -2560,6 +2846,8 @@ _cairo_xlib_surface_detach_display (cairo_xlib_display_t *display, void *data) dpy = surface->dpy; surface->dpy = NULL; + X_DEBUG ((dpy, "detach (drawable=%x)", (unsigned int) surface->drawable)); + if (surface->dst_picture != None) { XRenderFreePicture (dpy, surface->dst_picture); surface->dst_picture = None; @@ -2575,96 +2863,83 @@ _cairo_xlib_surface_detach_display (cairo_xlib_display_t *display, void *data) surface->drawable = None; surface->owns_pixmap = FALSE; } - - if (surface->gc != NULL) { - XFreeGC (dpy, surface->gc); - surface->gc = NULL; - } } static cairo_surface_t * -_cairo_xlib_surface_create_internal (Display *dpy, - Drawable drawable, - Screen *screen, - Visual *visual, - XRenderPictFormat *xrender_format, - int width, - int height, - int depth) +_cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen, + Drawable drawable, + Visual *visual, + XRenderPictFormat *xrender_format, + int width, + int height, + int depth) { cairo_xlib_surface_t *surface; - cairo_xlib_display_t *display; - cairo_xlib_screen_info_t *screen_info; - cairo_status_t status; CAIRO_MUTEX_INITIALIZE (); - if (xrender_format) { - depth = xrender_format->depth; + if (depth == 0) { + if (xrender_format) { + depth = xrender_format->depth; - /* XXX find matching visual for core/dithering fallbacks? */ - } else if (visual) { - int j, k; + /* XXX find matching visual for core/dithering fallbacks? */ + } else if (visual) { + Screen *scr = screen->screen; - /* This is ugly, but we have to walk over all visuals - * for the display to find the correct depth. - */ - depth = 0; - for (j = 0; j < screen->ndepths; j++) { - Depth *d = &screen->depths[j]; - for (k = 0; k < d->nvisuals; k++) { - if (&d->visuals[k] == visual) { - depth = d->depth; - goto found; + if (visual == DefaultVisualOfScreen (scr)) { + depth = DefaultDepthOfScreen (scr); + } else { + int j, k; + + /* This is ugly, but we have to walk over all visuals + * for the display to find the correct depth. + */ + depth = 0; + for (j = 0; j < scr->ndepths; j++) { + Depth *d = &scr->depths[j]; + for (k = 0; k < d->nvisuals; k++) { + if (&d->visuals[k] == visual) { + depth = d->depth; + goto found; + } + } } } } - found: + + if (depth == 0) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); + +found: ; } - if (depth == 0) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); - - status = _cairo_xlib_display_get (dpy, &display); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); - - status = _cairo_xlib_screen_info_get (display, screen, &screen_info); - if (unlikely (status)) { - _cairo_xlib_display_destroy (display); - return _cairo_surface_create_in_error (status); - } - surface = malloc (sizeof (cairo_xlib_surface_t)); - if (unlikely (surface == NULL)) { - _cairo_xlib_screen_info_destroy (screen_info); - _cairo_xlib_display_destroy (display); + if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } + + surface->dpy = _cairo_xlib_display_get_dpy (screen->display); /* initialize and hook into the CloseDisplay callback */ surface->close_display_hook.func = _cairo_xlib_surface_detach_display; - _cairo_xlib_add_close_display_hook (display, &surface->close_display_hook); + _cairo_xlib_add_close_display_hook (screen->display, + &surface->close_display_hook); - surface->render_major = display->render_major; - surface->render_minor = display->render_minor; + _cairo_xlib_display_get_xrender_version (screen->display, + &surface->render_major, + &surface->render_minor); if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) { if (!xrender_format) { if (visual) { - xrender_format = XRenderFindVisualFormat (dpy, visual); + xrender_format = XRenderFindVisualFormat (surface->dpy, visual); } else if (depth == 1) { xrender_format = - _cairo_xlib_display_get_xrender_format (display, + _cairo_xlib_display_get_xrender_format (screen->display, CAIRO_FORMAT_A1); } } } else { - xrender_format = NULL; - } - - /* we cannot use XRender for this surface, so ensure we don't try */ - if (xrender_format == NULL) { + /* we cannot use XRender for this surface, so ensure we don't try */ surface->render_major = -1; surface->render_minor = -1; } @@ -2672,24 +2947,28 @@ _cairo_xlib_surface_create_internal (Display *dpy, _cairo_surface_init (&surface->base, &cairo_xlib_surface_backend, _xrender_format_to_content (xrender_format)); - surface->dpy = dpy; - surface->display = display; - surface->screen_info = screen_info; + surface->screen = _cairo_xlib_screen_reference (screen); + surface->display = screen->display; - surface->gc = NULL; surface->drawable = drawable; - surface->screen = screen; surface->owns_pixmap = FALSE; surface->use_pixmap = 0; surface->width = width; surface->height = height; - surface->buggy_repeat = screen_info->display->buggy_repeat; - if (!CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES (surface)) { + surface->buggy_repeat = ! _cairo_xlib_display_has_repeat (surface->display); + if (! CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES (surface)) { /* so we can use the XTile fallback */ surface->buggy_repeat = TRUE; } - surface->buggy_pad_reflect = screen_info->display->buggy_pad_reflect; + + surface->buggy_pad_reflect = ! _cairo_xlib_display_has_reflect (surface->display); + if (! CAIRO_SURFACE_RENDER_HAS_EXTENDED_REPEAT (surface)) + surface->buggy_pad_reflect = TRUE; + + surface->buggy_gradients = ! _cairo_xlib_display_has_gradients (surface->display); + if (! CAIRO_SURFACE_RENDER_HAS_GRADIENTS (surface)) + surface->buggy_gradients = TRUE; surface->dst_picture = None; surface->src_picture = None; @@ -2698,11 +2977,11 @@ _cairo_xlib_surface_create_internal (Display *dpy, surface->xrender_format = xrender_format; surface->depth = depth; surface->filter = CAIRO_FILTER_NEAREST; - surface->repeat = FALSE; + surface->extend = CAIRO_EXTEND_NONE; + surface->has_component_alpha = FALSE; surface->xtransform = identity; - surface->have_clip_rects = FALSE; - surface->gc_has_clip_rects = FALSE; + surface->clip_region = NULL; surface->clip_rects = surface->embedded_clip_rects; surface->num_clip_rects = 0; surface->clip_dirty = 0; @@ -2741,29 +3020,31 @@ _cairo_xlib_surface_create_internal (Display *dpy, surface->b_mask = 0; } - return (cairo_surface_t *) surface; + return &surface->base; } static Screen * _cairo_xlib_screen_from_visual (Display *dpy, Visual *visual) { - int s; - int d; - int v; - Screen *screen; - Depth *depth; + int s, d, v; for (s = 0; s < ScreenCount (dpy); s++) { + Screen *screen; + screen = ScreenOfDisplay (dpy, s); if (visual == DefaultVisualOfScreen (screen)) return screen; + for (d = 0; d < screen->ndepths; d++) { + Depth *depth; + depth = &screen->depths[d]; for (v = 0; v < depth->nvisuals; v++) if (visual == &depth->visuals[v]) return screen; } } + return NULL; } @@ -2799,13 +3080,32 @@ cairo_xlib_surface_create (Display *dpy, int width, int height) { - Screen *screen = _cairo_xlib_screen_from_visual (dpy, visual); + Screen *scr; + cairo_xlib_screen_t *screen; + cairo_surface_t *surface; + cairo_status_t status; - if (screen == NULL) + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) { + /* you're lying, and you know it! */ + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + } + + scr = _cairo_xlib_screen_from_visual (dpy, visual); + if (scr == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); - return _cairo_xlib_surface_create_internal (dpy, drawable, screen, - visual, NULL, width, height, 0); + status = _cairo_xlib_screen_get (dpy, scr, &screen); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + X_DEBUG ((dpy, "create (drawable=%x)", (unsigned int) drawable)); + + surface = _cairo_xlib_surface_create_internal (screen, drawable, + visual, NULL, + width, height, 0); + _cairo_xlib_screen_destroy (screen); + + return surface; } slim_hidden_def (cairo_xlib_surface_create); @@ -2825,12 +3125,29 @@ slim_hidden_def (cairo_xlib_surface_create); cairo_surface_t * cairo_xlib_surface_create_for_bitmap (Display *dpy, Pixmap bitmap, - Screen *screen, + Screen *scr, int width, int height) { - return _cairo_xlib_surface_create_internal (dpy, bitmap, screen, - NULL, NULL, width, height, 1); + cairo_xlib_screen_t *screen; + cairo_surface_t *surface; + cairo_status_t status; + + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + + status = _cairo_xlib_screen_get (dpy, scr, &screen); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + X_DEBUG ((dpy, "create_for_bitmap (drawable=%x)", (unsigned int) bitmap)); + + surface = _cairo_xlib_surface_create_internal (screen, bitmap, + NULL, NULL, + width, height, 1); + _cairo_xlib_screen_destroy (screen); + + return surface; } #if CAIRO_HAS_XLIB_XRENDER_SURFACE @@ -2857,13 +3174,30 @@ cairo_xlib_surface_create_for_bitmap (Display *dpy, cairo_surface_t * cairo_xlib_surface_create_with_xrender_format (Display *dpy, Drawable drawable, - Screen *screen, + Screen *scr, XRenderPictFormat *format, int width, int height) { - return _cairo_xlib_surface_create_internal (dpy, drawable, screen, - NULL, format, width, height, 0); + cairo_xlib_screen_t *screen; + cairo_surface_t *surface; + cairo_status_t status; + + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + + status = _cairo_xlib_screen_get (dpy, scr, &screen); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + X_DEBUG ((dpy, "create_with_xrender_format (drawable=%x)", (unsigned int) drawable)); + + surface = _cairo_xlib_surface_create_internal (screen, drawable, + NULL, format, + width, height, 0); + _cairo_xlib_screen_destroy (screen); + + return surface; } slim_hidden_def (cairo_xlib_surface_create_with_xrender_format); @@ -2927,6 +3261,12 @@ cairo_xlib_surface_set_size (cairo_surface_t *abstract_surface, return; } + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) { + status = _cairo_surface_set_error (abstract_surface, + CAIRO_STATUS_INVALID_SIZE); + return; + } + surface->width = width; surface->height = height; } @@ -2959,11 +3299,19 @@ cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface, return; } + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) { + status = _cairo_surface_set_error (abstract_surface, + CAIRO_STATUS_INVALID_SIZE); + return; + } + /* XXX: and what about this case? */ if (surface->owns_pixmap) return; if (surface->drawable != drawable) { + X_DEBUG ((surface->dpy, "set_drawable (drawable=%x)", (unsigned int) drawable)); + if (surface->dst_picture != None) { status = _cairo_xlib_display_queue_resource ( surface->display, @@ -3062,7 +3410,7 @@ cairo_xlib_surface_get_screen (cairo_surface_t *abstract_surface) return NULL; } - return surface->screen; + return surface->screen->screen; } /** @@ -3128,7 +3476,7 @@ cairo_xlib_surface_get_width (cairo_surface_t *abstract_surface) if (! _cairo_surface_is_xlib (abstract_surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return -1; + return 0; } return surface->width; @@ -3151,7 +3499,7 @@ cairo_xlib_surface_get_height (cairo_surface_t *abstract_surface) if (! _cairo_surface_is_xlib (abstract_surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return -1; + return 0; } return surface->height; @@ -3208,7 +3556,7 @@ _cairo_xlib_surface_remove_scaled_font (cairo_xlib_display_t *display, Display *dpy; int i; - dpy = display->display; + dpy = _cairo_xlib_display_get_dpy (display); for (i = 0; i < NUM_GLYPHSETS; i++) { cairo_xlib_font_glyphset_info_t *glyphset_info; @@ -3410,8 +3758,8 @@ _cairo_xlib_scaled_font_get_glyphset_info_for_format (cairo_scaled_font_t *scale glyphset_info->xrender_format = _cairo_xlib_display_get_xrender_format (display, glyphset_info->format); - glyphset_info->glyphset = XRenderCreateGlyphSet (display->display, - glyphset_info->xrender_format); + glyphset_info->glyphset = XRenderCreateGlyphSet (_cairo_xlib_display_get_dpy (display), + glyphset_info->xrender_format); } return glyphset_info; @@ -3538,14 +3886,13 @@ _cairo_xlib_surface_add_glyph (Display *dpy, sz_xGlyphInfo - 8; if (len >= max_request_size) - return CAIRO_INT_STATUS_UNSUPPORTED; + return UNSUPPORTED ("glyph too large for XRequest"); } /* If the glyph surface has zero height or width, we create * a clear 1x1 surface, to avoid various X server bugs. */ if (glyph_surface->width == 0 || glyph_surface->height == 0) { - cairo_t *cr; cairo_surface_t *tmp_surface; tmp_surface = cairo_image_surface_create (glyphset_info->format, 1, 1); @@ -3553,19 +3900,10 @@ _cairo_xlib_surface_add_glyph (Display *dpy, if (unlikely (status)) goto BAIL; - cr = cairo_create (tmp_surface); - cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); - cairo_paint (cr); - status = cairo_status (cr); - cairo_destroy (cr); - tmp_surface->device_transform = glyph_surface->base.device_transform; tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; glyph_surface = (cairo_image_surface_t *) tmp_surface; - - if (unlikely (status)) - goto BAIL; } /* If the glyph format does not match the font format, then we @@ -3573,7 +3911,7 @@ _cairo_xlib_surface_add_glyph (Display *dpy, * format. */ if (glyph_surface->format != glyphset_info->format) { - cairo_t *cr; + cairo_surface_pattern_t pattern; cairo_surface_t *tmp_surface; tmp_surface = cairo_image_surface_create (glyphset_info->format, @@ -3586,12 +3924,11 @@ _cairo_xlib_surface_add_glyph (Display *dpy, tmp_surface->device_transform = glyph_surface->base.device_transform; tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; - cr = cairo_create (tmp_surface); - cairo_set_source_surface (cr, &glyph_surface->base, 0, 0); - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_paint (cr); - status = cairo_status (cr); - cairo_destroy (cr); + _cairo_pattern_init_for_surface (&pattern, &glyph_surface->base); + status = _cairo_surface_paint (tmp_surface, + CAIRO_OPERATOR_SOURCE, &pattern.base, + NULL); + _cairo_pattern_fini (&pattern.base); glyph_surface = (cairo_image_surface_t *) tmp_surface; @@ -3823,7 +4160,6 @@ _emit_glyphs_chunk (cairo_xlib_surface_t *dst, if (n) { elts[nelt].nchars = n; nelt++; - n = 0; } /* Check that we agree with _cairo_xlib_surface_emit_glyphs() on the @@ -3889,7 +4225,7 @@ _cairo_xlib_surface_emit_glyphs (cairo_xlib_surface_t *dst, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); - if (status != CAIRO_STATUS_SUCCESS) + if (unlikely (status)) return status; this_x = _cairo_lround (glyphs[i].d.x); @@ -3969,7 +4305,7 @@ _cairo_xlib_surface_emit_glyphs (cairo_xlib_surface_t *dst, status = _emit_glyphs_chunk (dst, glyphs, i, scaled_font, op, src, attributes, num_elts, old_width, glyphset_info); - if (status != CAIRO_STATUS_SUCCESS) + if (unlikely (status)) return status; glyphs += i; @@ -4008,13 +4344,14 @@ _cairo_xlib_surface_emit_glyphs (cairo_xlib_surface_t *dst, request_size += width; } - if (num_elts) + if (num_elts) { status = _emit_glyphs_chunk (dst, glyphs, i, scaled_font, op, src, attributes, num_elts, width, glyphset_info); + } *remaining_glyphs = num_glyphs - i; - if (*remaining_glyphs && status == CAIRO_STATUS_SUCCESS) + if (*remaining_glyphs != 0 && status == CAIRO_STATUS_SUCCESS) status = CAIRO_INT_STATUS_UNSUPPORTED; return status; @@ -4044,8 +4381,8 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip, + int *remaining_glyphs) { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_xlib_surface_t *dst = (cairo_xlib_surface_t*) abstract_dst; @@ -4053,16 +4390,17 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst, composite_operation_t operation; cairo_surface_attributes_t attributes; cairo_xlib_surface_t *src = NULL; + cairo_region_t *clip_region = NULL; cairo_solid_pattern_t solid_pattern; if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT (dst)) - return CAIRO_INT_STATUS_UNSUPPORTED; + return UNSUPPORTED ("XRender does not support CompositeText"); /* Just let unbounded operators go through the fallback code * instead of trying to do the fixups here */ - if (!_cairo_operator_bounded_by_mask (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (! _cairo_operator_bounded_by_mask (op)) + return UNSUPPORTED ("unsupported unbounded op"); /* Render <= 0.10 seems to have a bug with PictOpSrc and glyphs -- * the solid source seems to be multiplied by the glyph mask, and @@ -4070,25 +4408,58 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst, * including the fully transparent "background" of the rectangular * glyph surface. */ if (op == CAIRO_OPERATOR_SOURCE && - !CAIRO_SURFACE_RENDER_AT_LEAST(dst, 0, 11)) - return CAIRO_INT_STATUS_UNSUPPORTED; + ! CAIRO_SURFACE_RENDER_AT_LEAST(dst, 0, 11)) + { + return UNSUPPORTED ("known bug in Render"); + } /* We can only use our code if we either have no clip or * have a real native clip region set. If we're using * fallback clip masking, we have to go through the full * fallback path. */ - if (dst->base.clip && - (dst->base.clip->mode != CAIRO_CLIP_MODE_REGION || - dst->base.clip->surface != NULL)) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); + if (status) + return status; + } operation = _categorize_composite_operation (dst, op, src_pattern, TRUE); if (operation == DO_UNSUPPORTED) - return CAIRO_INT_STATUS_UNSUPPORTED; + return UNSUPPORTED ("unsupported op"); if (! _cairo_xlib_surface_owns_font (dst, scaled_font)) - return CAIRO_INT_STATUS_UNSUPPORTED; + return UNSUPPORTED ("unowned font"); + + X_DEBUG ((dst->dpy, "show_glyphs (dst=%x)", (unsigned int) dst->drawable)); + + if (clip_region != NULL && + cairo_region_num_rectangles (clip_region) == 1) + { + cairo_rectangle_int_t glyph_extents; + const cairo_rectangle_int_t *clip_extents; + + /* Can we do without the clip? + * Around 50% of the time the clip is redundant (firefox). + */ + _cairo_scaled_font_glyph_approximate_extents (scaled_font, + glyphs, num_glyphs, + &glyph_extents); + + clip_extents = &clip->path->extents; + if (clip_extents->x <= glyph_extents.x && + clip_extents->y <= glyph_extents.y && + clip_extents->x + clip_extents->width >= glyph_extents.x + glyph_extents.width && + clip_extents->y + clip_extents->height >= glyph_extents.y + glyph_extents.height) + { + clip_region = NULL; + } + } + + status = _cairo_xlib_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + return status; /* After passing all those tests, we're now committed to rendering * these glyphs or to fail trying. We first upload any glyphs to @@ -4110,7 +4481,6 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst, if (src_pattern->type == CAIRO_PATTERN_TYPE_SOLID) { status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, - CAIRO_CONTENT_COLOR_ALPHA, 0, 0, 1, 1, CAIRO_PATTERN_ACQUIRE_NONE, (cairo_surface_t **) &src, @@ -4123,19 +4493,25 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst, status = _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs, - &glyph_extents); + &glyph_extents, + NULL); if (unlikely (status)) goto BAIL0; - status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, - CAIRO_CONTENT_COLOR_ALPHA, - glyph_extents.x, glyph_extents.y, - glyph_extents.width, glyph_extents.height, - dst->buggy_pad_reflect ? - CAIRO_PATTERN_ACQUIRE_NO_REFLECT : - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) &src, - &attributes); + if (clip != NULL) { + if (! _cairo_rectangle_intersect (&glyph_extents, + _cairo_clip_get_extents (clip))) + { + goto BAIL0; + } + } + + status = _cairo_xlib_surface_acquire_pattern_surface (dst, src_pattern, + glyph_extents.x, + glyph_extents.y, + glyph_extents.width, + glyph_extents.height, + &src, &attributes); if (unlikely (status)) goto BAIL0; } @@ -4143,7 +4519,7 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst, operation = _recategorize_composite_operation (dst, op, src, &attributes, TRUE); if (operation == DO_UNSUPPORTED) { - status = CAIRO_INT_STATUS_UNSUPPORTED; + status = UNSUPPORTED ("unsupported op"); goto BAIL1; } @@ -4161,15 +4537,14 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst, src, &attributes, remaining_glyphs); - } else - status = CAIRO_INT_STATUS_UNSUPPORTED; + } else { + status = UNSUPPORTED ("unowned font"); + } _cairo_scaled_font_thaw_cache (scaled_font); BAIL1: if (src) _cairo_pattern_release_surface (src_pattern, &src->base, &attributes); - if (src_pattern == &solid_pattern.base) - _cairo_pattern_fini (&solid_pattern.base); BAIL0: _cairo_xlib_display_notify (dst->display); diff --git a/gfx/cairo/cairo/src/cairo-xlib-xrender-private.h b/gfx/cairo/cairo/src/cairo-xlib-xrender-private.h index eee585cc4d79..63a0ecbdc659 100644 --- a/gfx/cairo/cairo/src/cairo-xlib-xrender-private.h +++ b/gfx/cairo/cairo/src/cairo-xlib-xrender-private.h @@ -45,6 +45,16 @@ #include #include +/* These prototypes are used when defining interfaces missing from the + * render headers. As it happens, it is the case that all libxrender + * functions take a pointer as first argument. */ + +__attribute__((__unused__)) static void _void_consume (void *p, ...) { } +__attribute__((__unused__)) static void * _voidp_consume (void *p, ...) { return (void *)0; } +__attribute__((__unused__)) static int _int_consume (void *p, ...) { return 0; } +__attribute__((__unused__)) static void _void_consume_free (Display *p, XID n) { } + + /* We require Render >= 0.6. The following defines were only added in * 0.10. Make sure they are defined. */ @@ -63,6 +73,57 @@ #endif +#ifndef PictOptBlendMinimum +/* + * Operators only available in version 0.11 + */ +#define PictOpBlendMinimum 0x30 +#define PictOpMultiply 0x30 +#define PictOpScreen 0x31 +#define PictOpOverlay 0x32 +#define PictOpDarken 0x33 +#define PictOpLighten 0x34 +#define PictOpColorDodge 0x35 +#define PictOpColorBurn 0x36 +#define PictOpHardLight 0x37 +#define PictOpSoftLight 0x38 +#define PictOpDifference 0x39 +#define PictOpExclusion 0x3a +#define PictOpHSLHue 0x3b +#define PictOpHSLSaturation 0x3c +#define PictOpHSLColor 0x3d +#define PictOpHSLLuminosity 0x3e +#define PictOpBlendMaximum 0x3e +#endif + +/* There doesn't appear to be a simple #define that we can conditionalize + * on. Instead, use the version; gradients were introdiced in 0.10. */ +#if RENDER_MAJOR == 0 && RENDER_MINOR < 10 +#define XRenderCreateLinearGradient _int_consume +#define XRenderCreateRadialGradient _int_consume +#define XRenderCreateConicalGradient _int_consume +typedef struct _XCircle { + XFixed x; + XFixed y; + XFixed radius; +} XCircle; +typedef struct _XLinearGradient { + XPointFixed p1; + XPointFixed p2; +} XLinearGradient; + +typedef struct _XRadialGradient { + XCircle inner; + XCircle outer; +} XRadialGradient; + +typedef struct _XConicalGradient { + XPointFixed center; + XFixed angle; /* in degrees */ +} XConicalGradient; +#endif + + #else /* !CAIRO_HAS_XLIB_XRENDER_SURFACE */ /* Provide dummy symbols and macros to get it compile and take the fallback @@ -71,14 +132,6 @@ /* Functions */ -/* As it happens, it is the case that, all libxrender functions - * take a pointer as first argument */ - -__attribute__((__unused__)) static void _void_consume (void *p, ...) { } -__attribute__((__unused__)) static void * _voidp_consume (void *p, ...) { return (void *)0; } -__attribute__((__unused__)) static int _int_consume (void *p, ...) { return 0; } -__attribute__((__unused__)) static void _void_consume_free (Display *p, XID n) { } - #define XRenderQueryExtension _int_consume #define XRenderQueryVersion _int_consume #define XRenderQueryFormats _int_consume @@ -221,6 +274,27 @@ typedef unsigned long PictFormat; #define PictOpConjointXor 0x2b #define PictOpConjointMaximum 0x2b +/* + * Operators only available in version 0.11 + */ +#define PictOpBlendMinimum 0x30 +#define PictOpMultiply 0x30 +#define PictOpScreen 0x31 +#define PictOpOverlay 0x32 +#define PictOpDarken 0x33 +#define PictOpLighten 0x34 +#define PictOpColorDodge 0x35 +#define PictOpColorBurn 0x36 +#define PictOpHardLight 0x37 +#define PictOpSoftLight 0x38 +#define PictOpDifference 0x39 +#define PictOpExclusion 0x3a +#define PictOpHSLHue 0x3b +#define PictOpHSLSaturation 0x3c +#define PictOpHSLColor 0x3d +#define PictOpHSLLuminosity 0x3e +#define PictOpBlendMaximum 0x3e + #define PolyEdgeSharp 0 #define PolyEdgeSmooth 1 diff --git a/gfx/cairo/cairo/src/cairo-xml-surface.c b/gfx/cairo/cairo/src/cairo-xml-surface.c new file mode 100644 index 000000000000..25e0bb224a31 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xml-surface.c @@ -0,0 +1,1153 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Chris Wilson. + * + * Contributor(s): + * Chris Wilson + */ + +/* This surface is intended to produce a verbose, hierarchical, DAG XML file + * representing a single surface. It is intended to be used by debuggers, + * such as cairo-sphinx, or by application test-suites that what a log of + * operations. + */ + +#include "cairoint.h" + +#include "cairo-xml.h" + +#include "cairo-clip-private.h" +#include "cairo-output-stream-private.h" +#include "cairo-recording-surface-private.h" + +#define static cairo_warn static + +typedef struct _cairo_xml_surface cairo_xml_surface_t; + +struct _cairo_xml { + cairo_status_t status; + + int ref; + + cairo_output_stream_t *stream; + int indent; +}; + +struct _cairo_xml_surface { + cairo_surface_t base; + + cairo_xml_t *xml; + + double width, height; +}; + +slim_hidden_proto (cairo_xml_for_recording_surface); + +static const cairo_xml_t _nil_xml = { + CAIRO_STATUS_NO_MEMORY, + -1 +}; + +static const cairo_surface_backend_t _cairo_xml_surface_backend; + +static const char * +_direction_to_string (cairo_bool_t backward) +{ + static const char *names[] = { + "FORWARD", + "BACKWARD" + }; + assert (backward < ARRAY_LENGTH (names)); + return names[backward]; +} + +static const char * +_operator_to_string (cairo_operator_t op) +{ + static const char *names[] = { + "CLEAR", /* CAIRO_OPERATOR_CLEAR */ + + "SOURCE", /* CAIRO_OPERATOR_SOURCE */ + "OVER", /* CAIRO_OPERATOR_OVER */ + "IN", /* CAIRO_OPERATOR_IN */ + "OUT", /* CAIRO_OPERATOR_OUT */ + "ATOP", /* CAIRO_OPERATOR_ATOP */ + + "DEST", /* CAIRO_OPERATOR_DEST */ + "DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */ + "DEST_IN", /* CAIRO_OPERATOR_DEST_IN */ + "DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */ + "DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */ + + "XOR", /* CAIRO_OPERATOR_XOR */ + "ADD", /* CAIRO_OPERATOR_ADD */ + "SATURATE", /* CAIRO_OPERATOR_SATURATE */ + + "MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */ + "SCREEN", /* CAIRO_OPERATOR_SCREEN */ + "OVERLAY", /* CAIRO_OPERATOR_OVERLAY */ + "DARKEN", /* CAIRO_OPERATOR_DARKEN */ + "LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */ + "DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */ + "BURN", /* CAIRO_OPERATOR_COLOR_BURN */ + "HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */ + "SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */ + "DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */ + "EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */ + "HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */ + "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */ + "HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */ + "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */ + }; + assert (op < ARRAY_LENGTH (names)); + return names[op]; +} + +static const char * +_extend_to_string (cairo_extend_t extend) +{ + static const char *names[] = { + "EXTEND_NONE", /* CAIRO_EXTEND_NONE */ + "EXTEND_REPEAT", /* CAIRO_EXTEND_REPEAT */ + "EXTEND_REFLECT", /* CAIRO_EXTEND_REFLECT */ + "EXTEND_PAD" /* CAIRO_EXTEND_PAD */ + }; + assert (extend < ARRAY_LENGTH (names)); + return names[extend]; +} + +static const char * +_filter_to_string (cairo_filter_t filter) +{ + static const char *names[] = { + "FILTER_FAST", /* CAIRO_FILTER_FAST */ + "FILTER_GOOD", /* CAIRO_FILTER_GOOD */ + "FILTER_BEST", /* CAIRO_FILTER_BEST */ + "FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */ + "FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */ + "FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */ + }; + assert (filter < ARRAY_LENGTH (names)); + return names[filter]; +} + +static const char * +_fill_rule_to_string (cairo_fill_rule_t rule) +{ + static const char *names[] = { + "WINDING", /* CAIRO_FILL_RULE_WINDING */ + "EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */ + }; + assert (rule < ARRAY_LENGTH (names)); + return names[rule]; +} + +static const char * +_antialias_to_string (cairo_antialias_t antialias) +{ + static const char *names[] = { + "ANTIALIAS_DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */ + "ANTIALIAS_NONE", /* CAIRO_ANTIALIAS_NONE */ + "ANTIALIAS_GRAY", /* CAIRO_ANTIALIAS_GRAY */ + "ANTIALIAS_SUBPIXEL" /* CAIRO_ANTIALIAS_SUBPIXEL */ + }; + assert (antialias < ARRAY_LENGTH (names)); + return names[antialias]; +} + +static const char * +_line_cap_to_string (cairo_line_cap_t line_cap) +{ + static const char *names[] = { + "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */ + "LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */ + "LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */ + }; + assert (line_cap < ARRAY_LENGTH (names)); + return names[line_cap]; +} + +static const char * +_line_join_to_string (cairo_line_join_t line_join) +{ + static const char *names[] = { + "LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */ + "LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */ + "LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */ + }; + assert (line_join < ARRAY_LENGTH (names)); + return names[line_join]; +} + +static const char * +_content_to_string (cairo_content_t content) +{ + switch (content) { + case CAIRO_CONTENT_ALPHA: return "ALPHA"; + case CAIRO_CONTENT_COLOR: return "COLOR"; + default: + case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA"; + } +} + +static const char * +_format_to_string (cairo_format_t format) +{ + static const char *names[] = { + "ARGB32", /* CAIRO_FORMAT_ARGB32 */ + "RGB24", /* CAIRO_FORMAT_RGB24 */ + "A8", /* CAIRO_FORMAT_A8 */ + "A1" /* CAIRO_FORMAT_A1 */ + }; + assert (format < ARRAY_LENGTH (names)); + return names[format]; +} + +static cairo_xml_t * +_cairo_xml_create_internal (cairo_output_stream_t *stream) +{ + cairo_xml_t *xml; + + xml = malloc (sizeof (cairo_xml_t)); + if (unlikely (xml == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_xml_t *) &_nil_xml; + } + + memset (xml, 0, sizeof (cairo_xml_t)); + xml->status = CAIRO_STATUS_SUCCESS; + xml->ref = 1; + xml->indent = 0; + + xml->stream = stream; + + return xml; +} + +static void +_cairo_xml_indent (cairo_xml_t *xml, int indent) +{ + xml->indent += indent; + assert (xml->indent >= 0); +} + +static void CAIRO_PRINTF_FORMAT (2, 3) +_cairo_xml_printf (cairo_xml_t *xml, const char *fmt, ...) +{ + va_list ap; + char indent[80]; + int len; + + len = MIN (xml->indent, ARRAY_LENGTH (indent)); + memset (indent, ' ', len); + _cairo_output_stream_write (xml->stream, indent, len); + + va_start (ap, fmt); + _cairo_output_stream_vprintf (xml->stream, fmt, ap); + va_end (ap); + + _cairo_output_stream_write (xml->stream, "\n", 1); +} + +static void CAIRO_PRINTF_FORMAT (2, 3) +_cairo_xml_printf_start (cairo_xml_t *xml, const char *fmt, ...) +{ + char indent[80]; + int len; + + len = MIN (xml->indent, ARRAY_LENGTH (indent)); + memset (indent, ' ', len); + _cairo_output_stream_write (xml->stream, indent, len); + + if (fmt != NULL) { + va_list ap; + + va_start (ap, fmt); + _cairo_output_stream_vprintf (xml->stream, fmt, ap); + va_end (ap); + } +} + +static void CAIRO_PRINTF_FORMAT (2, 3) +_cairo_xml_printf_continue (cairo_xml_t *xml, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + _cairo_output_stream_vprintf (xml->stream, fmt, ap); + va_end (ap); +} + +static void CAIRO_PRINTF_FORMAT (2, 3) +_cairo_xml_printf_end (cairo_xml_t *xml, const char *fmt, ...) +{ + if (fmt != NULL) { + va_list ap; + + va_start (ap, fmt); + _cairo_output_stream_vprintf (xml->stream, fmt, ap); + va_end (ap); + } + + _cairo_output_stream_write (xml->stream, "\n", 1); +} + +static cairo_status_t +_cairo_xml_destroy_internal (cairo_xml_t *xml) +{ + cairo_status_t status; + + assert (xml->ref > 0); + if (--xml->ref) + return _cairo_output_stream_flush (xml->stream); + + status = _cairo_output_stream_destroy (xml->stream); + + free (xml); + + return status; +} + +static cairo_surface_t * +_cairo_xml_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_rectangle_t extents; + + extents.x = extents.y = 0; + extents.width = width; + extents.height = height; + + return cairo_recording_surface_create (content, &extents); +} + +static cairo_status_t +_cairo_xml_surface_finish (void *abstract_surface) +{ + cairo_xml_surface_t *surface = abstract_surface; + + return _cairo_xml_destroy_internal (surface->xml); +} + +static cairo_bool_t +_cairo_xml_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_xml_surface_t *surface = abstract_surface; + + if (surface->width < 0 || surface->height < 0) + return FALSE; + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = surface->width; + rectangle->height = surface->height; + + return TRUE; +} + +static cairo_status_t +_cairo_xml_move_to (void *closure, + const cairo_point_t *p1) +{ + _cairo_xml_printf_continue (closure, " %f %f m", + _cairo_fixed_to_double (p1->x), + _cairo_fixed_to_double (p1->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_line_to (void *closure, + const cairo_point_t *p1) +{ + _cairo_xml_printf_continue (closure, " %f %f l", + _cairo_fixed_to_double (p1->x), + _cairo_fixed_to_double (p1->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_curve_to (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2, + const cairo_point_t *p3) +{ + _cairo_xml_printf_continue (closure, " %f %f %f %f %f %f c", + _cairo_fixed_to_double (p1->x), + _cairo_fixed_to_double (p1->y), + _cairo_fixed_to_double (p2->x), + _cairo_fixed_to_double (p2->y), + _cairo_fixed_to_double (p3->x), + _cairo_fixed_to_double (p3->y)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_close_path (void *closure) +{ + _cairo_xml_printf_continue (closure, " h"); + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_xml_emit_path (cairo_xml_t *xml, + cairo_path_fixed_t *path) +{ + cairo_status_t status; + + _cairo_xml_printf_start (xml, ""); + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_xml_move_to, + _cairo_xml_line_to, + _cairo_xml_curve_to, + _cairo_xml_close_path, + xml); + assert (status == CAIRO_STATUS_SUCCESS); + _cairo_xml_printf_start (xml, ""); +} + +static void +_cairo_xml_emit_string (cairo_xml_t *xml, + const char *node, + const char *data) +{ + _cairo_xml_printf (xml, "<%s>%s", node, data, node); +} + +static void +_cairo_xml_emit_double (cairo_xml_t *xml, + const char *node, + double data) +{ + _cairo_xml_printf (xml, "<%s>%f", node, data, node); +} + +static cairo_status_t +_cairo_xml_surface_emit_clip_path (cairo_xml_surface_t *surface, + cairo_clip_path_t *clip_path) +{ + cairo_box_t box; + cairo_status_t status; + + if (clip_path->prev != NULL) { + status = _cairo_xml_surface_emit_clip_path (surface, clip_path->prev); + if (unlikely (status)) + return status; + } + + + /* skip the trivial clip covering the surface extents */ + if (surface->width >= 0 && surface->height >= 0 && + _cairo_path_fixed_is_box (&clip_path->path, &box)) + { + if (box.p1.x <= 0 && box.p1.y <= 0 && + box.p2.x - box.p1.x >= _cairo_fixed_from_double (surface->width) && + box.p2.y - box.p1.y >= _cairo_fixed_from_double (surface->height)) + { + return CAIRO_STATUS_SUCCESS; + } + } + + _cairo_xml_printf_start (surface->xml, ""); + _cairo_xml_indent (surface->xml, 2); + + _cairo_xml_emit_path (surface->xml, &clip_path->path); + _cairo_xml_emit_double (surface->xml, "tolerance", clip_path->tolerance); + _cairo_xml_emit_string (surface->xml, "antialias", + _antialias_to_string (clip_path->antialias)); + _cairo_xml_emit_string (surface->xml, "fill-rule", + _fill_rule_to_string (clip_path->fill_rule)); + + _cairo_xml_indent (surface->xml, -2); + _cairo_xml_printf_end (surface->xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_surface_emit_clip (cairo_xml_surface_t *surface, + cairo_clip_t *clip) +{ + if (clip == NULL) + return CAIRO_STATUS_SUCCESS; + + return _cairo_xml_surface_emit_clip_path (surface, clip->path); +} + +static cairo_status_t +_cairo_xml_emit_solid (cairo_xml_t *xml, + const cairo_solid_pattern_t *solid) +{ + _cairo_xml_printf (xml, "%f %f %f %f", + solid->color.red, + solid->color.green, + solid->color.blue, + solid->color.alpha); + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_xml_emit_matrix (cairo_xml_t *xml, + const cairo_matrix_t *matrix) +{ + if (! _cairo_matrix_is_identity (matrix)) { + _cairo_xml_printf (xml, "%f %f %f %f %f %f", + matrix->xx, matrix->yx, + matrix->xy, matrix->yy, + matrix->x0, matrix->y0); + } +} + +static void +_cairo_xml_emit_gradient (cairo_xml_t *xml, + const cairo_gradient_pattern_t *gradient) +{ + unsigned int i; + + for (i = 0; i < gradient->n_stops; i++) { + _cairo_xml_printf (xml, + "%f %f %f %f %f", + gradient->stops[i].offset, + gradient->stops[i].color.red, + gradient->stops[i].color.green, + gradient->stops[i].color.blue, + gradient->stops[i].color.alpha); + } +} + +static cairo_status_t +_cairo_xml_emit_linear (cairo_xml_t *xml, + const cairo_linear_pattern_t *linear) +{ + _cairo_xml_printf (xml, + "", + _cairo_fixed_to_double (linear->p1.x), + _cairo_fixed_to_double (linear->p1.y), + _cairo_fixed_to_double (linear->p2.x), + _cairo_fixed_to_double (linear->p2.y)); + _cairo_xml_indent (xml, 2); + _cairo_xml_emit_gradient (xml, &linear->base); + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_emit_radial (cairo_xml_t *xml, + const cairo_radial_pattern_t *radial) +{ + _cairo_xml_printf (xml, + "", + _cairo_fixed_to_double (radial->c1.x), + _cairo_fixed_to_double (radial->c1.y), + _cairo_fixed_to_double (radial->r1), + _cairo_fixed_to_double (radial->c2.x), + _cairo_fixed_to_double (radial->c2.y), + _cairo_fixed_to_double (radial->r2)); + _cairo_xml_indent (xml, 2); + _cairo_xml_emit_gradient (xml, &radial->base); + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_write_func (void *closure, const unsigned char *data, unsigned len) +{ + _cairo_output_stream_write (closure, data, len); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_emit_image (cairo_xml_t *xml, + cairo_image_surface_t *image) +{ + cairo_output_stream_t *stream; + cairo_status_t status; + + _cairo_xml_printf_start (xml, + "", + image->width, image->height, + _format_to_string (image->format)); + + stream = _cairo_base64_stream_create (xml->stream); + status = cairo_surface_write_to_png_stream (&image->base, + _write_func, stream); + assert (status == CAIRO_STATUS_SUCCESS); + status = _cairo_output_stream_destroy (stream); + if (unlikely (status)) + return status; + + _cairo_xml_printf_end (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_emit_surface (cairo_xml_t *xml, + const cairo_surface_pattern_t *pattern) +{ + cairo_surface_t *source = pattern->surface; + cairo_status_t status; + + if (_cairo_surface_is_recording (source)) { + status = cairo_xml_for_recording_surface (xml, source); + } else { + cairo_image_surface_t *image; + void *image_extra; + + status = _cairo_surface_acquire_source_image (source, + &image, &image_extra); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_image (xml, image); + + _cairo_surface_release_source_image (source, image, image_extra); + } + + return status; +} + +static cairo_status_t +_cairo_xml_emit_pattern (cairo_xml_t *xml, + const char *source_or_mask, + const cairo_pattern_t *pattern) +{ + cairo_status_t status; + + _cairo_xml_printf (xml, "<%s-pattern>", source_or_mask); + _cairo_xml_indent (xml, 2); + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + status = _cairo_xml_emit_solid (xml, (cairo_solid_pattern_t *) pattern); + break; + case CAIRO_PATTERN_TYPE_LINEAR: + status = _cairo_xml_emit_linear (xml, (cairo_linear_pattern_t *) pattern); + break; + case CAIRO_PATTERN_TYPE_RADIAL: + status = _cairo_xml_emit_radial (xml, (cairo_radial_pattern_t *) pattern); + break; + case CAIRO_PATTERN_TYPE_SURFACE: + status = _cairo_xml_emit_surface (xml, (cairo_surface_pattern_t *) pattern); + break; + default: + ASSERT_NOT_REACHED; + status = CAIRO_INT_STATUS_UNSUPPORTED; + break; + } + + if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) { + _cairo_xml_emit_matrix (xml, &pattern->matrix); + _cairo_xml_printf (xml, + "%s", + _extend_to_string (pattern->extend)); + _cairo_xml_printf (xml, + "%s", + _filter_to_string (pattern->filter)); + } + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, "", source_or_mask); + + return status; +} + +static cairo_int_status_t +_cairo_xml_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_xml_surface_t *surface = abstract_surface; + cairo_xml_t *xml = surface->xml; + cairo_status_t status; + + _cairo_xml_printf (xml, ""); + _cairo_xml_indent (xml, 2); + + _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); + + status = _cairo_xml_surface_emit_clip (surface, clip); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_pattern (xml, "source", source); + if (unlikely (status)) + return status; + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_xml_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_xml_surface_t *surface = abstract_surface; + cairo_xml_t *xml = surface->xml; + cairo_status_t status; + + _cairo_xml_printf (xml, ""); + _cairo_xml_indent (xml, 2); + + _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); + + status = _cairo_xml_surface_emit_clip (surface, clip); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_pattern (xml, "source", source); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_pattern (xml, "mask", mask); + if (unlikely (status)) + return status; + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_xml_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_stroke_style_t *style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_xml_surface_t *surface = abstract_surface; + cairo_xml_t *xml = surface->xml; + cairo_status_t status; + + _cairo_xml_printf (xml, ""); + _cairo_xml_indent (xml, 2); + + _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); + _cairo_xml_emit_double (xml, "line-width", style->line_width); + _cairo_xml_emit_double (xml, "miter-limit", style->miter_limit); + _cairo_xml_emit_string (xml, "line-cap", _line_cap_to_string (style->line_cap)); + _cairo_xml_emit_string (xml, "line-join", _line_join_to_string (style->line_join)); + + status = _cairo_xml_surface_emit_clip (surface, clip); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_pattern (xml, "source", source); + if (unlikely (status)) + return status; + + if (style->num_dashes) { + unsigned int i; + + _cairo_xml_printf_start (xml, "", + style->dash_offset); + for (i = 0; i < style->num_dashes; i++) + _cairo_xml_printf_continue (xml, "%f ", style->dash[i]); + + _cairo_xml_printf_end (xml, ""); + } + + _cairo_xml_emit_path (surface->xml, path); + _cairo_xml_emit_double (xml, "tolerance", tolerance); + _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias)); + + _cairo_xml_emit_matrix (xml, ctm); + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_xml_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_xml_surface_t *surface = abstract_surface; + cairo_xml_t *xml = surface->xml; + cairo_status_t status; + + _cairo_xml_printf (xml, ""); + _cairo_xml_indent (xml, 2); + + _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); + + status = _cairo_xml_surface_emit_clip (surface, clip); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_pattern (xml, "source", source); + if (unlikely (status)) + return status; + + _cairo_xml_emit_path (surface->xml, path); + _cairo_xml_emit_double (xml, "tolerance", tolerance); + _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias)); + _cairo_xml_emit_string (xml, "fill-rule", _fill_rule_to_string (fill_rule)); + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +#if CAIRO_HAS_FT_FONT +#include "cairo-ft-private.h" +static cairo_status_t +_cairo_xml_emit_type42_font (cairo_xml_t *xml, + cairo_scaled_font_t *scaled_font) +{ + const cairo_scaled_font_backend_t *backend; + cairo_output_stream_t *base64_stream; + cairo_output_stream_t *zlib_stream; + cairo_status_t status, status2; + unsigned long size; + uint32_t len; + uint8_t *buf; + + backend = scaled_font->backend; + if (backend->load_truetype_table == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + size = 0; + status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size); + if (unlikely (status)) + return status; + + buf = malloc (size); + if (unlikely (buf == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = backend->load_truetype_table (scaled_font, 0, 0, buf, NULL); + if (unlikely (status)) { + free (buf); + return status; + } + + _cairo_xml_printf_start (xml, "", + _cairo_ft_scaled_font_get_load_flags (scaled_font)); + + + base64_stream = _cairo_base64_stream_create (xml->stream); + len = size; + _cairo_output_stream_write (base64_stream, &len, sizeof (len)); + + zlib_stream = _cairo_deflate_stream_create (base64_stream); + + _cairo_output_stream_write (zlib_stream, buf, size); + free (buf); + + status2 = _cairo_output_stream_destroy (zlib_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + status2 = _cairo_output_stream_destroy (base64_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + _cairo_xml_printf_end (xml, ""); + + return status; +} +#else +static cairo_status_t +_cairo_xml_emit_type42_font (cairo_xml_t *xml, + cairo_scaled_font_t *scaled_font) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} +#endif + +static cairo_status_t +_cairo_xml_emit_type3_font (cairo_xml_t *xml, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs) +{ + _cairo_xml_printf_start (xml, ""); + _cairo_xml_printf_end (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_emit_scaled_font (cairo_xml_t *xml, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs) +{ + cairo_status_t status; + + _cairo_xml_printf (xml, ""); + _cairo_xml_indent (xml, 2); + + status = _cairo_xml_emit_type42_font (xml, scaled_font); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = _cairo_xml_emit_type3_font (xml, scaled_font, + glyphs, num_glyphs); + } + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + return status; +} + +static cairo_int_status_t +_cairo_xml_surface_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs) +{ + cairo_xml_surface_t *surface = abstract_surface; + cairo_xml_t *xml = surface->xml; + cairo_status_t status; + int i; + + _cairo_xml_printf (xml, ""); + _cairo_xml_indent (xml, 2); + + _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); + + status = _cairo_xml_surface_emit_clip (surface, clip); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_pattern (xml, "source", source); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_scaled_font (xml, scaled_font, glyphs, num_glyphs); + if (unlikely (status)) + return status; + + for (i = 0; i < num_glyphs; i++) { + _cairo_xml_printf (xml, "%f %f", + glyphs[i].index, + glyphs[i].x, + glyphs[i].y); + } + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + *remaining_glyphs = 0; + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t +_cairo_xml_surface_backend = { + CAIRO_SURFACE_TYPE_XML, + _cairo_xml_surface_create_similar, + _cairo_xml_surface_finish, + NULL, NULL, /* source image */ + NULL, NULL, /* dst image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, NULL, /* copy/show page */ + _cairo_xml_surface_get_extents, + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* font fini */ + NULL, /* scaled_glyph_fini */ + + /* The 5 high level operations */ + _cairo_xml_surface_paint, + _cairo_xml_surface_mask, + _cairo_xml_surface_stroke, + _cairo_xml_surface_fill, + _cairo_xml_surface_glyphs, + + NULL, /* snapshot */ + + NULL, /* is_similar */ + NULL, /* fill_stroke */ + NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ + + /* The alternate high-level text operation */ + NULL, NULL, /* has, show_text_glyphs */ +}; + +static cairo_surface_t * +_cairo_xml_surface_create_internal (cairo_xml_t *xml, + cairo_content_t content, + double width, + double height) +{ + cairo_xml_surface_t *surface; + + if (unlikely (xml == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); + + surface = malloc (sizeof (cairo_xml_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &_cairo_xml_surface_backend, + content); + + surface->xml = xml; + xml->ref++; + + surface->width = width; + surface->height = height; + + return &surface->base; +} + +cairo_xml_t * +cairo_xml_create (const char *filename) +{ + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create_for_filename (filename); + if (_cairo_output_stream_get_status (stream)) + return (cairo_xml_t *) &_nil_xml; + + return _cairo_xml_create_internal (stream); +} + +cairo_xml_t * +cairo_xml_create_for_stream (cairo_write_func_t write_func, + void *closure) +{ + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create (write_func, NULL, closure); + if (_cairo_output_stream_get_status (stream)) + return (cairo_xml_t *) &_nil_xml; + + return _cairo_xml_create_internal (stream); +} + +cairo_surface_t * +cairo_xml_surface_create (cairo_xml_t *xml, + cairo_content_t content, + double width, double height) +{ + return _cairo_xml_surface_create_internal (xml, content, width, height); +} + +cairo_status_t +cairo_xml_for_recording_surface (cairo_xml_t *xml, + cairo_surface_t *recording_surface) +{ + cairo_box_t bbox; + cairo_rectangle_int_t extents; + cairo_surface_t *surface; + cairo_status_t status; + + if (unlikely (xml->status)) + return xml->status; + + if (unlikely (recording_surface->status)) + return recording_surface->status; + + if (unlikely (! _cairo_surface_is_recording (recording_surface))) + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + + status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, + &bbox, NULL); + if (unlikely (status)) + return status; + + _cairo_box_round_to_rectangle (&bbox, &extents); + surface = _cairo_xml_surface_create_internal (xml, + recording_surface->content, + extents.width, + extents.height); + if (unlikely (surface->status)) + return surface->status; + + _cairo_xml_printf (xml, + "", + _content_to_string (recording_surface->content), + extents.width, extents.height); + _cairo_xml_indent (xml, 2); + + cairo_surface_set_device_offset (surface, -extents.x, -extents.y); + status = _cairo_recording_surface_replay (recording_surface, surface); + cairo_surface_destroy (surface); + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + return status; +} +slim_hidden_def (cairo_xml_for_recording_surface); + +void +cairo_xml_destroy (cairo_xml_t *xml) +{ + cairo_status_t status_ignored; + + if (xml == NULL || xml->ref < 0) + return; + + status_ignored = _cairo_xml_destroy_internal (xml); +} diff --git a/gfx/cairo/cairo/src/cairo-xml.h b/gfx/cairo/cairo/src/cairo-xml.h new file mode 100644 index 000000000000..bba8adc04111 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xml.h @@ -0,0 +1,72 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Chris Wilson + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_XML_H +#define CAIRO_XML_H + +#include "cairo.h" + +#if CAIRO_HAS_XML_SURFACE + +CAIRO_BEGIN_DECLS + +typedef struct _cairo_xml cairo_xml_t; + +cairo_public cairo_xml_t * +cairo_xml_create (const char *filename); + +cairo_public cairo_xml_t * +cairo_xml_create_for_stream (cairo_write_func_t write_func, + void *closure); + +cairo_public void +cairo_xml_destroy (cairo_xml_t *context); + +cairo_public cairo_surface_t * +cairo_xml_surface_create (cairo_xml_t *xml, + cairo_content_t content, + double width, double height); + +cairo_public cairo_status_t +cairo_xml_for_recording_surface (cairo_xml_t *context, + cairo_surface_t *surface); + +CAIRO_END_DECLS + +#else /*CAIRO_HAS_XML_SURFACE*/ +# error Cairo was not compiled with support for the XML backend +#endif /*CAIRO_HAS_XML_SURFACE*/ + +#endif /*CAIRO_XML_H*/ diff --git a/gfx/cairo/cairo/src/cairo.c b/gfx/cairo/cairo/src/cairo.c index 3e590496af65..50f017441c1b 100644 --- a/gfx/cairo/cairo/src/cairo.c +++ b/gfx/cairo/cairo/src/cairo.c @@ -44,6 +44,10 @@ #define CAIRO_TOLERANCE_MINIMUM _cairo_fixed_to_double(1) +#if !defined(INFINITY) +#define INFINITY HUGE_VAL +#endif + static const cairo_t _cairo_nil = { CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ CAIRO_STATUS_NO_MEMORY, /* status */ @@ -57,7 +61,8 @@ static const cairo_t _cairo_nil = { FALSE, /* has_current_point */ FALSE, /* has_curve_to */ FALSE, /* is_box */ - FALSE, /* is_region */ + FALSE, /* maybe_fill_region */ + TRUE, /* is_empty_fill */ {{{NULL,NULL}}} /* link */ }} }; @@ -114,7 +119,23 @@ _cairo_set_error (cairo_t *cr, cairo_status_t status) _cairo_status_set_error (&cr->status, _cairo_error (status)); } -#if HAS_ATOMIC_OPS +#if defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +static __forceinline int +ffs(int x) +{ + unsigned long i; + + if (_BitScanForward(&i, x) != 0) + return i + 1; + + return 0; +} +#endif + + +#if CAIRO_NO_MUTEX /* We keep a small stash of contexts to reduce malloc pressure */ #define CAIRO_STASH_SIZE 4 static struct { @@ -127,14 +148,13 @@ _context_get (void) { int avail, old, new; - do { - old = _context_stash.occupied; - avail = ffs (~old) - 1; - if (avail >= CAIRO_STASH_SIZE) - return malloc (sizeof (cairo_t)); + old = _context_stash.occupied; + avail = ffs (~old) - 1; + if (avail >= CAIRO_STASH_SIZE) + return malloc (sizeof (cairo_t)); - new = old | (1 << avail); - } while (_cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, new) != old); + new = old | (1 << avail); + _context_stash.occupied = new; return &_context_stash.pool[avail]; } @@ -152,8 +172,50 @@ _context_put (cairo_t *cr) } avail = ~(1 << (cr - &_context_stash.pool[0])); + old = _context_stash.occupied; + new = old & avail; + _context_stash.occupied = new; +} +#elif HAS_ATOMIC_OPS +/* We keep a small stash of contexts to reduce malloc pressure */ +#define CAIRO_STASH_SIZE 4 +static struct { + cairo_t pool[CAIRO_STASH_SIZE]; + cairo_atomic_int_t occupied; +} _context_stash; + +static cairo_t * +_context_get (void) +{ + cairo_atomic_int_t avail, old, new; + do { - old = _context_stash.occupied; + old = _cairo_atomic_int_get (&_context_stash.occupied); + avail = ffs (~old) - 1; + if (avail >= CAIRO_STASH_SIZE) + return malloc (sizeof (cairo_t)); + + new = old | (1 << avail); + } while (_cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, new) != old); + + return &_context_stash.pool[avail]; +} + +static void +_context_put (cairo_t *cr) +{ + cairo_atomic_int_t old, new, avail; + + if (cr < &_context_stash.pool[0] || + cr >= &_context_stash.pool[CAIRO_STASH_SIZE]) + { + free (cr); + return; + } + + avail = ~(1 << (cr - &_context_stash.pool[0])); + do { + old = _cairo_atomic_int_get (&_context_stash.occupied); new = old & avail; } while (_cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, new) != old); } @@ -496,25 +558,28 @@ cairo_push_group_with_content (cairo_t *cr, cairo_content_t content) { cairo_status_t status; cairo_rectangle_int_t extents; + const cairo_rectangle_int_t *clip_extents; cairo_surface_t *parent_surface, *group_surface = NULL; + cairo_bool_t is_empty; if (unlikely (cr->status)) return; parent_surface = _cairo_gstate_get_target (cr->gstate); - /* Get the extents that we'll use in creating our new group surface */ - status = _cairo_surface_get_extents (parent_surface, &extents); - if (unlikely (status)) - goto bail; - status = _cairo_clip_intersect_to_rectangle (_cairo_gstate_get_clip (cr->gstate), &extents); - if (unlikely (status)) - goto bail; - group_surface = cairo_surface_create_similar (parent_surface, - content, - extents.width, - extents.height); - status = cairo_surface_status (group_surface); + /* Get the extents that we'll use in creating our new group surface */ + is_empty = _cairo_surface_get_extents (parent_surface, &extents); + clip_extents = _cairo_clip_get_extents (_cairo_gstate_get_clip (cr->gstate)); + if (clip_extents != NULL) + is_empty = _cairo_rectangle_intersect (&extents, clip_extents); + + group_surface = _cairo_surface_create_similar_solid (parent_surface, + content, + extents.width, + extents.height, + CAIRO_COLOR_TRANSPARENT, + TRUE); + status = group_surface->status; if (unlikely (status)) goto bail; @@ -1670,8 +1735,10 @@ cairo_arc (cairo_t *cr, return; /* Do nothing, successfully, if radius is <= 0 */ - if (radius <= 0.0) + if (radius <= 0.0) { + cairo_line_to (cr, xc, yc); return; + } while (angle2 < angle1) angle2 += 2 * M_PI; @@ -1925,7 +1992,7 @@ cairo_stroke_to_path (cairo_t *cr) if (unlikely (cr->status)) return; - /* The code in _cairo_meta_surface_get_path has a poorman's stroke_to_path */ + /* The code in _cairo_recording_surface_get_path has a poorman's stroke_to_path */ status = _cairo_gstate_stroke_path (cr->gstate); if (unlikely (status)) @@ -2340,7 +2407,7 @@ cairo_in_stroke (cairo_t *cr, double x, double y) cairo_bool_t inside = FALSE; if (unlikely (cr->status)) - return 0; + return FALSE; status = _cairo_gstate_in_stroke (cr->gstate, cr->path, @@ -2370,16 +2437,10 @@ cairo_in_stroke (cairo_t *cr, double x, double y) cairo_bool_t cairo_in_fill (cairo_t *cr, double x, double y) { - cairo_bool_t inside; - if (unlikely (cr->status)) - return 0; + return FALSE; - _cairo_gstate_in_fill (cr->gstate, - cr->path, - x, y, &inside); - - return inside; + return _cairo_gstate_in_fill (cr->gstate, cr->path, x, y); } /** @@ -2600,8 +2661,6 @@ cairo_clip_extents (cairo_t *cr, double *x1, double *y1, double *x2, double *y2) { - cairo_status_t status; - if (unlikely (cr->status)) { if (x1) *x1 = 0.0; @@ -2615,9 +2674,38 @@ cairo_clip_extents (cairo_t *cr, return; } - status = _cairo_gstate_clip_extents (cr->gstate, x1, y1, x2, y2); - if (unlikely (status)) - _cairo_set_error (cr, status); + if (! _cairo_gstate_clip_extents (cr->gstate, x1, y1, x2, y2)) { + *x1 = -INFINITY; + *y1 = -INFINITY; + *x2 = +INFINITY; + *y2 = +INFINITY; + } +} + +/** + * cairo_in_clip: + * @cr: a cairo context + * @x: X coordinate of the point to test + * @y: Y coordinate of the point to test + * + * Tests whether the given point is inside the area that would be + * visible through the current clip, i.e. the area that would be filled by + * a cairo_paint() operation. + * + * See cairo_clip(), and cairo_clip_preserve(). + * + * Return value: A non-zero value if the point is inside, or zero if + * outside. + * + * Since: 1.10 + **/ +cairo_bool_t +cairo_in_clip (cairo_t *cr, double x, double y) +{ + if (unlikely (cr->status)) + return FALSE; + + return _cairo_gstate_in_clip (cr->gstate, x, y); } static cairo_rectangle_list_t * @@ -3355,6 +3443,7 @@ cairo_show_text_glyphs (cairo_t *cr, { cairo_status_t status; + if (unlikely (cr->status)) return; diff --git a/gfx/cairo/cairo/src/cairo.h b/gfx/cairo/cairo/src/cairo.h index a766cfcc0f5d..16b52b6b496a 100644 --- a/gfx/cairo/cairo/src/cairo.h +++ b/gfx/cairo/cairo/src/cairo.h @@ -132,6 +132,15 @@ typedef struct _cairo cairo_t; * creates a bitmap image in memory. * The type of a surface can be queried with cairo_surface_get_type(). * + * The initial contents of a surface after creation depend upon the manner + * of its creation. If cairo creates the surface and backing storage for + * the user, it will be initially cleared; for example, + * cairo_image_surface_create() and cairo_surface_create_similar(). + * Alternatively, if the user passes in a reference to some backing storage + * and asks cairo to wrap that in a #cairo_surface_t, then the contents are + * not modified; for example, cairo_image_surface_create_for_data() and + * cairo_xlib_surface_create(). + * * Memory management of #cairo_surface_t is done with * cairo_surface_reference() and cairo_surface_destroy(). **/ @@ -423,6 +432,41 @@ cairo_pop_group_to_source (cairo_t *cr); * @CAIRO_OPERATOR_ADD: source and destination layers are accumulated * @CAIRO_OPERATOR_SATURATE: like over, but assuming source and dest are * disjoint geometries + * @CAIRO_OPERATOR_MULTIPLY: source and destination layers are multiplied. + * This causes the result to be at least as dark as the darker inputs. + * @CAIRO_OPERATOR_SCREEN: source and destination are complemented and + * multiplied. This causes the result to be at least as light as the lighter + * inputs. + * @CAIRO_OPERATOR_OVERLAY: multiplies or screens, depending on the + * lightness of the destination color. + * @CAIRO_OPERATOR_DARKEN: replaces the destination with the source if it + * is darker, otherwise keeps the source. + * @CAIRO_OPERATOR_LIGHTEN: replaces the destination with the source if it + * is lighter, otherwise keeps the source. + * @CAIRO_OPERATOR_COLOR_DODGE: brightens the destination color to reflect + * the source color. + * @CAIRO_OPERATOR_COLOR_BURN: darkens the destination color to reflect + * the source color. + * @CAIRO_OPERATOR_HARD_LIGHT: Multiplies or screens, dependant on source + * color. + * @CAIRO_OPERATOR_SOFT_LIGHT: Darkens or lightens, dependant on source + * color. + * @CAIRO_OPERATOR_DIFFERENCE: Takes the difference of the source and + * destination color. + * @CAIRO_OPERATOR_EXCLUSION: Produces an effect similar to difference, but + * with lower contrast. + * @CAIRO_OPERATOR_HSL_HUE: Creates a color with the hue of the source + * and the saturation and luminosity of the target. + * @CAIRO_OPERATOR_HSL_SATURATION: Creates a color with the saturation + * of the source and the hue and luminosity of the target. Painting with + * this mode onto a gray area prduces no change. + * @CAIRO_OPERATOR_HSL_COLOR: Creates a color with the hue and saturation + * of the source and the luminosity of the target. This preserves the gray + * levels of the target and is useful for coloring monochrome images or + * tinting color images. + * @CAIRO_OPERATOR_HSL_LUMINOSITY: Creates a color with the luminosity of + * the source and the hue and saturation of the target. This produces an + * inverse effect to @CAIRO_OPERATOR_HSL_COLOR. * * #cairo_operator_t is used to set the compositing operator for all cairo * drawing operations. @@ -459,7 +503,23 @@ typedef enum _cairo_operator { CAIRO_OPERATOR_XOR, CAIRO_OPERATOR_ADD, - CAIRO_OPERATOR_SATURATE + CAIRO_OPERATOR_SATURATE, + + CAIRO_OPERATOR_MULTIPLY, + CAIRO_OPERATOR_SCREEN, + CAIRO_OPERATOR_OVERLAY, + CAIRO_OPERATOR_DARKEN, + CAIRO_OPERATOR_LIGHTEN, + CAIRO_OPERATOR_COLOR_DODGE, + CAIRO_OPERATOR_COLOR_BURN, + CAIRO_OPERATOR_HARD_LIGHT, + CAIRO_OPERATOR_SOFT_LIGHT, + CAIRO_OPERATOR_DIFFERENCE, + CAIRO_OPERATOR_EXCLUSION, + CAIRO_OPERATOR_HSL_HUE, + CAIRO_OPERATOR_HSL_SATURATION, + CAIRO_OPERATOR_HSL_COLOR, + CAIRO_OPERATOR_HSL_LUMINOSITY } cairo_operator_t; cairo_public void @@ -738,6 +798,9 @@ cairo_in_stroke (cairo_t *cr, double x, double y); cairo_public cairo_bool_t cairo_in_fill (cairo_t *cr, double x, double y); +cairo_public cairo_bool_t +cairo_in_clip (cairo_t *cr, double x, double y); + /* Rectangular extents */ cairo_public void cairo_stroke_extents (cairo_t *cr, @@ -1892,7 +1955,13 @@ cairo_surface_status (cairo_surface_t *surface); * @CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: The surface is of type quartz_image * @CAIRO_SURFACE_TYPE_SCRIPT: The surface is of type script, since 1.10 * @CAIRO_SURFACE_TYPE_QT: The surface is of type Qt, since 1.10 - * @CAIRO_SURFACE_TYPE_META: The surface is a meta-type, since 1.10 + * @CAIRO_SURFACE_TYPE_RECORDING: The surface is of type recording, since 1.10 + * @CAIRO_SURFACE_TYPE_VG: The surface is a OpenVG surface, since 1.10 + * @CAIRO_SURFACE_TYPE_GL: The surface is of type OpenGL, since 1.10 + * @CAIRO_SURFACE_TYPE_DRM: The surface is of type Direct Render Manager, since 1.10 + * @CAIRO_SURFACE_TYPE_TEE: The surface is of type 'tee' (a multiplexing surface), since 1.10 + * @CAIRO_SURFACE_TYPE_XML: The surface is of type XML (for debugging), since 1.10 + * @CAIRO_SURFACE_TYPE_SKIA: The surface is of type Skia, since 1.10 * @CAIRO_SURFACE_TYPE_DDRAW: The surface is of type ddraw * * #cairo_surface_type_t is used to describe the type of a given @@ -1935,7 +2004,13 @@ typedef enum _cairo_surface_type { CAIRO_SURFACE_TYPE_QUARTZ_IMAGE, CAIRO_SURFACE_TYPE_SCRIPT, CAIRO_SURFACE_TYPE_QT, - CAIRO_SURFACE_TYPE_META, + CAIRO_SURFACE_TYPE_RECORDING, + CAIRO_SURFACE_TYPE_VG, + CAIRO_SURFACE_TYPE_GL, + CAIRO_SURFACE_TYPE_DRM, + CAIRO_SURFACE_TYPE_TEE, + CAIRO_SURFACE_TYPE_XML, + CAIRO_SURFACE_TYPE_SKIA, CAIRO_SURFACE_TYPE_DDRAW, CAIRO_SURFACE_TYPE_D2D } cairo_surface_type_t; @@ -2115,23 +2190,35 @@ cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func, #endif -/* Meta-surface functions */ +/* Recording-surface functions */ cairo_public cairo_surface_t * -cairo_meta_surface_create (cairo_content_t content, - double width_pixels, - double height_pixels); +cairo_recording_surface_create (cairo_content_t content, + const cairo_rectangle_t *extents); cairo_public void -cairo_meta_surface_ink_extents (cairo_surface_t *surface, - double *x0, - double *y0, - double *width, - double *height); +cairo_recording_surface_ink_extents (cairo_surface_t *surface, + double *x0, + double *y0, + double *width, + double *height); -cairo_public cairo_status_t -cairo_meta_surface_replay (cairo_surface_t *surface, - cairo_surface_t *target); +/* Tee-surface functions */ + +cairo_public cairo_surface_t * +cairo_tee_surface_create (cairo_surface_t *master); + +cairo_public void +cairo_tee_surface_add (cairo_surface_t *surface, + cairo_surface_t *target); + +cairo_public void +cairo_tee_surface_remove (cairo_surface_t *surface, + cairo_surface_t *target); + +cairo_public cairo_surface_t * +cairo_tee_surface_index (cairo_surface_t *surface, + int index); /* Pattern creation functions */ @@ -2401,45 +2488,51 @@ cairo_public cairo_region_t * cairo_region_create_rectangle (const cairo_rectangle_int_t *rectangle); cairo_public cairo_region_t * -cairo_region_create_rectangles (cairo_rectangle_int_t *rects, +cairo_region_create_rectangles (const cairo_rectangle_int_t *rects, int count); cairo_public cairo_region_t * -cairo_region_copy (cairo_region_t *original); +cairo_region_copy (const cairo_region_t *original); + +cairo_public cairo_region_t * +cairo_region_reference (cairo_region_t *); cairo_public void cairo_region_destroy (cairo_region_t *region); +cairo_public cairo_bool_t +cairo_region_equal (const cairo_region_t *a, const cairo_region_t *b); + cairo_public cairo_status_t -cairo_region_status (cairo_region_t *region); +cairo_region_status (const cairo_region_t *region); cairo_public void -cairo_region_get_extents (cairo_region_t *region, +cairo_region_get_extents (const cairo_region_t *region, cairo_rectangle_int_t *extents); cairo_public int -cairo_region_num_rectangles (cairo_region_t *region); +cairo_region_num_rectangles (const cairo_region_t *region); cairo_public void -cairo_region_get_rectangle (cairo_region_t *region, +cairo_region_get_rectangle (const cairo_region_t *region, int nth_rectangle, cairo_rectangle_int_t *rectangle); cairo_public cairo_bool_t -cairo_region_is_empty (cairo_region_t *region); +cairo_region_is_empty (const cairo_region_t *region); cairo_public cairo_region_overlap_t -cairo_region_contains_rectangle (cairo_region_t *region, +cairo_region_contains_rectangle (const cairo_region_t *region, const cairo_rectangle_int_t *rectangle); cairo_public cairo_bool_t -cairo_region_contains_point (cairo_region_t *region, int x, int y); +cairo_region_contains_point (const cairo_region_t *region, int x, int y); cairo_public void cairo_region_translate (cairo_region_t *region, int dx, int dy); cairo_public cairo_status_t -cairo_region_subtract (cairo_region_t *dst, cairo_region_t *other); +cairo_region_subtract (cairo_region_t *dst, const cairo_region_t *other); cairo_public cairo_status_t cairo_region_subtract_rectangle (cairo_region_t *dst, diff --git a/gfx/cairo/cairo/src/cairoint.h b/gfx/cairo/cairo/src/cairoint.h index 85b79b684b13..1cdf6ff9a952 100644 --- a/gfx/cairo/cairo/src/cairoint.h +++ b/gfx/cairo/cairo/src/cairoint.h @@ -132,7 +132,7 @@ _cairo_win32_tmpfile (void); #define STRINGIFY(macro_or_string) STRINGIFY_ARG (macro_or_string) #define STRINGIFY_ARG(contents) #contents -#ifdef __GNUC__ +#if defined (__GNUC__) #define cairo_container_of(ptr, type, member) ({ \ const __typeof__ (((type *) 0)->member) *mptr__ = (ptr); \ (type *) ((char *) mptr__ - offsetof (type, member)); \ @@ -236,6 +236,22 @@ be32_to_cpu(uint32_t v) #endif + +/* The glibc versions of ispace() and isdigit() are slow in UTF-8 locales. + */ + +static inline int cairo_const +_cairo_isspace (int c) +{ + return (c == 0x20 || (c >= 0x09 && c <= 0x0d)); +} + +static inline int cairo_const +_cairo_isdigit (int c) +{ + return (c >= '0' && c <= '9'); +} + #include "cairo-types-private.h" #include "cairo-cache-private.h" #include "cairo-reference-count-private.h" @@ -259,6 +275,20 @@ cairo_private void _cairo_box_round_to_rectangle (const cairo_box_t *box, cairo_rectangle_int_t *rectangle); +cairo_private void +_cairo_boxes_get_extents (const cairo_box_t *boxes, + int num_boxes, + cairo_box_t *extents); + +static inline void +_cairo_unbounded_rectangle_init (cairo_rectangle_int_t *rect) +{ + rect->x = CAIRO_RECT_INT_MIN; + rect->y = CAIRO_RECT_INT_MIN; + rect->width = CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN; + rect->height = CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN; +} + cairo_private cairo_bool_t _cairo_rectangle_intersect (cairo_rectangle_int_t *dst, const cairo_rectangle_int_t *src); @@ -385,7 +415,7 @@ typedef struct _cairo_scaled_glyph { cairo_image_surface_t *surface; /* device-space image */ cairo_path_fixed_t *path; /* device-space outline */ - cairo_surface_t *meta_surface; /* device-space meta-surface */ + cairo_surface_t *recording_surface; /* device-space recording-surface */ void *surface_private; /* for the surface backend */ } cairo_scaled_glyph_t; @@ -434,7 +464,7 @@ typedef enum _cairo_scaled_glyph_info { CAIRO_SCALED_GLYPH_INFO_METRICS = (1 << 0), CAIRO_SCALED_GLYPH_INFO_SURFACE = (1 << 1), CAIRO_SCALED_GLYPH_INFO_PATH = (1 << 2), - CAIRO_SCALED_GLYPH_INFO_META_SURFACE = (1 << 3) + CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE = (1 << 3) } cairo_scaled_glyph_info_t; typedef struct _cairo_scaled_font_subset { @@ -451,6 +481,7 @@ typedef struct _cairo_scaled_font_subset { char **glyph_names; unsigned int num_glyphs; cairo_bool_t is_composite; + cairo_bool_t is_scaled; } cairo_scaled_font_subset_t; struct _cairo_scaled_font_backend { @@ -496,6 +527,7 @@ struct _cairo_scaled_font_backend { unsigned int height, cairo_glyph_t *glyphs, int num_glyphs, + cairo_region_t *clip_region, int *remaining_glyphs); cairo_warn cairo_int_status_t @@ -619,7 +651,6 @@ struct _cairo_surface_backend { cairo_warn cairo_status_t (*clone_similar) (void *surface, cairo_surface_t *src, - cairo_content_t content, int src_x, int src_y, int width, @@ -628,6 +659,7 @@ struct _cairo_surface_backend { int *clone_offset_y, cairo_surface_t **clone_out); + /* XXX remove to a separate cairo_surface_compositor_t */ /* XXX: dst should be the first argument for consistency */ cairo_warn cairo_int_status_t (*composite) (cairo_operator_t op, @@ -641,7 +673,8 @@ struct _cairo_surface_backend { int dst_x, int dst_y, unsigned int width, - unsigned int height); + unsigned int height, + cairo_region_t *clip_region); cairo_warn cairo_int_status_t (*fill_rectangles) (void *surface, @@ -663,21 +696,22 @@ struct _cairo_surface_backend { unsigned int width, unsigned int height, cairo_trapezoid_t *traps, - int num_traps); + int num_traps, + cairo_region_t *region); cairo_warn cairo_span_renderer_t * (*create_span_renderer) (cairo_operator_t op, const cairo_pattern_t *pattern, void *dst, cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects); + const cairo_composite_rectangles_t *rects, + cairo_region_t *clip_region); cairo_warn cairo_bool_t (*check_span_renderer) (cairo_operator_t op, const cairo_pattern_t *pattern, void *dst, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects); + cairo_antialias_t antialias); cairo_warn cairo_int_status_t (*copy_page) (void *surface); @@ -685,59 +719,15 @@ struct _cairo_surface_backend { cairo_warn cairo_int_status_t (*show_page) (void *surface); - /* Set given region as the clip region for the surface, replacing - * any previously set clip region. Passing in a NULL region will - * clear the surface clip region. - * - * The surface is expected to store the clip region and clip all - * following drawing operations against it until the clip region - * is cleared of replaced by another clip region. - * - * Cairo will call this function whenever a clip path can be - * represented as a device pixel aligned set of rectangles. When - * this is not possible, cairo will use mask surfaces for - * clipping. - */ - cairo_warn cairo_int_status_t - (*set_clip_region) (void *surface, - cairo_region_t *region); - - /* Intersect the given path against the clip path currently set in - * the surface, using the given fill_rule and tolerance, and set - * the result as the new clipping path for the surface. Passing - * in a NULL path will clear the surface clipping path. - * - * The surface is expected to store the resulting clip path and - * clip all following drawing operations against it until the clip - * path cleared or intersected with a new path. - * - * If a surface implements this function, set_clip_region() will - * never be called and should not be implemented. If this - * function is not implemented cairo will use set_clip_region() - * (if available) and mask surfaces for clipping. - */ - cairo_warn cairo_int_status_t - (*intersect_clip_path) (void *dst, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias); - /* Get the extents of the current surface. For many surface types * this will be as simple as { x=0, y=0, width=surface->width, * height=surface->height}. * - * This function need not take account of any clipping from - * set_clip_region since the generic version of set_clip_region - * saves those, and the generic get_clip_extents will only call - * into the specific surface->get_extents if there is no current - * clip. - * * If this function is not implemented, or if it returns - * %CAIRO_INT_STATUS_UNSUPPORTED, the surface is considered to be - * boundless and inifnite bounds are used for it. + * FALSE the surface is considered to be + * boundless and infinite bounds are used for it. */ - cairo_warn cairo_int_status_t + cairo_warn cairo_bool_t (*get_extents) (void *surface, cairo_rectangle_int_t *extents); @@ -758,7 +748,8 @@ struct _cairo_surface_backend { unsigned int width, unsigned int height, cairo_glyph_t *glyphs, - int num_glyphs); + int num_glyphs, + cairo_region_t *clip_region); void (*get_font_options) (void *surface, @@ -788,14 +779,14 @@ struct _cairo_surface_backend { (*paint) (void *surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); cairo_warn cairo_int_status_t (*mask) (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); cairo_warn cairo_int_status_t (*stroke) (void *surface, @@ -807,7 +798,7 @@ struct _cairo_surface_backend { cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); cairo_warn cairo_int_status_t (*fill) (void *surface, @@ -817,7 +808,7 @@ struct _cairo_surface_backend { cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); cairo_warn cairo_int_status_t (*show_glyphs) (void *surface, @@ -826,8 +817,8 @@ struct _cairo_surface_backend { cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip, + int *remaining_glyphs); cairo_surface_t * (*snapshot) (void *surface); @@ -837,9 +828,6 @@ struct _cairo_surface_backend { void *surface_b, cairo_content_t content); - cairo_warn cairo_status_t - (*reset) (void *surface); - cairo_warn cairo_int_status_t (*fill_stroke) (void *surface, cairo_operator_t fill_op, @@ -855,7 +843,7 @@ struct _cairo_surface_backend { cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, cairo_antialias_t stroke_antialias, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); cairo_surface_t * (*create_solid_pattern_surface) @@ -882,7 +870,7 @@ struct _cairo_surface_backend { int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); }; #include "cairo-surface-private.h" @@ -900,9 +888,9 @@ struct _cairo_image_surface { int depth; pixman_image_t *pixman_image; + cairo_region_t *clip_region; unsigned owns_data : 1; - unsigned has_clip : 1; unsigned transparency : 2; }; @@ -912,80 +900,14 @@ extern const cairo_private cairo_surface_backend_t _cairo_image_surface_backend; #define CAIRO_EXTEND_GRADIENT_DEFAULT CAIRO_EXTEND_PAD #define CAIRO_FILTER_DEFAULT CAIRO_FILTER_GOOD -struct _cairo_pattern { - cairo_pattern_type_t type; - cairo_reference_count_t ref_count; - cairo_status_t status; - cairo_user_data_array_t user_data; - - cairo_matrix_t matrix; - cairo_filter_t filter; - cairo_extend_t extend; -}; - -struct _cairo_solid_pattern { - cairo_pattern_t base; - cairo_color_t color; - cairo_content_t content; -}; - extern const cairo_private cairo_solid_pattern_t _cairo_pattern_black; -typedef struct _cairo_surface_pattern { - cairo_pattern_t base; - - cairo_surface_t *surface; -} cairo_surface_pattern_t; - -typedef struct _cairo_gradient_stop { - double offset; - cairo_color_t color; -} cairo_gradient_stop_t; - -typedef struct _cairo_gradient_pattern { - cairo_pattern_t base; - - unsigned int n_stops; - unsigned int stops_size; - cairo_gradient_stop_t *stops; - cairo_gradient_stop_t stops_embedded[2]; -} cairo_gradient_pattern_t; - -typedef struct _cairo_linear_pattern { - cairo_gradient_pattern_t base; - - cairo_point_t p1; - cairo_point_t p2; -} cairo_linear_pattern_t; - -typedef struct _cairo_radial_pattern { - cairo_gradient_pattern_t base; - - cairo_point_t c1; - cairo_fixed_t r1; - cairo_point_t c2; - cairo_fixed_t r2; -} cairo_radial_pattern_t; - -typedef union { - cairo_gradient_pattern_t base; - - cairo_linear_pattern_t linear; - cairo_radial_pattern_t radial; -} cairo_gradient_pattern_union_t; - -typedef union { - cairo_pattern_t base; - - cairo_solid_pattern_t solid; - cairo_surface_pattern_t surface; - cairo_gradient_pattern_union_t gradient; -} cairo_pattern_union_t; typedef struct _cairo_surface_attributes { cairo_matrix_t matrix; cairo_extend_t extend; cairo_filter_t filter; + cairo_bool_t has_component_alpha; int x_offset; int y_offset; void *extra; @@ -994,16 +916,18 @@ typedef struct _cairo_surface_attributes { typedef struct _cairo_traps { cairo_status_t status; - cairo_box_t extents; + const cairo_box_t *limits; + int num_limits; + + unsigned int maybe_region : 1; /* hint: 0 implies that it cannot be */ + unsigned int has_intersections : 1; + unsigned int is_rectilinear : 1; + unsigned int is_rectangular : 1; int num_traps; int traps_size; cairo_trapezoid_t *traps; - /* embed enough storage for a stroked rectangle */ - cairo_trapezoid_t traps_embedded[4]; - - cairo_bool_t has_limits; - cairo_box_t limits; + cairo_trapezoid_t traps_embedded[16]; } cairo_traps_t; #define CAIRO_FONT_SLANT_DEFAULT CAIRO_FONT_SLANT_NORMAL @@ -1263,12 +1187,16 @@ _cairo_gstate_in_stroke (cairo_gstate_t *gstate, double y, cairo_bool_t *inside_ret); -cairo_private void +cairo_private cairo_bool_t _cairo_gstate_in_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path, double x, - double y, - cairo_bool_t *inside_ret); + double y); + +cairo_private cairo_bool_t +_cairo_gstate_in_clip (cairo_gstate_t *gstate, + double x, + double y); cairo_private cairo_status_t _cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path); @@ -1276,12 +1204,12 @@ _cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path); cairo_private cairo_status_t _cairo_gstate_reset_clip (cairo_gstate_t *gstate); -cairo_private cairo_status_t +cairo_private cairo_bool_t _cairo_gstate_clip_extents (cairo_gstate_t *gstate, double *x1, double *y1, - double *x2, - double *y2); + double *x2, + double *y2); cairo_private cairo_rectangle_list_t* _cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate); @@ -1498,19 +1426,19 @@ cairo_private void _cairo_intern_string_reset_static_data (void); /* cairo-path-fixed.c */ +cairo_private cairo_path_fixed_t * +_cairo_path_fixed_create (void); + cairo_private void _cairo_path_fixed_init (cairo_path_fixed_t *path); cairo_private cairo_status_t _cairo_path_fixed_init_copy (cairo_path_fixed_t *path, - cairo_path_fixed_t *other); + const cairo_path_fixed_t *other); cairo_private cairo_bool_t -_cairo_path_fixed_is_equal (cairo_path_fixed_t *path, - cairo_path_fixed_t *other); - -cairo_private cairo_path_fixed_t * -_cairo_path_fixed_create (void); +_cairo_path_fixed_is_equal (const cairo_path_fixed_t *path, + const cairo_path_fixed_t *other); cairo_private void _cairo_path_fixed_fini (cairo_path_fixed_t *path); @@ -1596,70 +1524,119 @@ _cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path, void *closure, double tolerance); -cairo_private cairo_status_t -_cairo_path_fixed_append (cairo_path_fixed_t *path, - const cairo_path_fixed_t *other, - cairo_direction_t dir); - cairo_private void -_cairo_path_fixed_approximate_clip_extents (cairo_path_fixed_t *path, +_cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path, cairo_rectangle_int_t *extents); cairo_private void -_cairo_path_fixed_approximate_fill_extents (cairo_path_fixed_t *path, +_cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path, cairo_rectangle_int_t *extents); cairo_private void -_cairo_path_fixed_approximate_stroke_extents (cairo_path_fixed_t *path, +_cairo_path_fixed_fill_extents (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_rectangle_int_t *extents); + +cairo_private void +_cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path, cairo_stroke_style_t *style, const cairo_matrix_t *ctm, cairo_rectangle_int_t *extents); +cairo_private cairo_status_t +_cairo_path_fixed_stroke_extents (const cairo_path_fixed_t *path, + cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_rectangle_int_t *extents); + cairo_private void -_cairo_path_fixed_bounds (cairo_path_fixed_t *path, +_cairo_path_fixed_bounds (const cairo_path_fixed_t *path, double *x1, double *y1, double *x2, double *y2); cairo_private void _cairo_path_fixed_transform (cairo_path_fixed_t *path, - cairo_matrix_t *matrix); + const cairo_matrix_t *matrix); cairo_private cairo_bool_t -_cairo_path_fixed_is_empty (cairo_path_fixed_t *path); - -cairo_private cairo_bool_t -_cairo_path_fixed_is_box (cairo_path_fixed_t *path, +_cairo_path_fixed_is_box (const cairo_path_fixed_t *path, cairo_box_t *box); cairo_private cairo_bool_t -_cairo_path_fixed_is_rectangle (cairo_path_fixed_t *path, +_cairo_path_fixed_is_rectangle (const cairo_path_fixed_t *path, cairo_box_t *box); /* cairo-path-in-fill.c */ -cairo_private void -_cairo_path_fixed_in_fill (cairo_path_fixed_t *path, +cairo_private cairo_bool_t +_cairo_path_fixed_in_fill (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, double x, - double y, - cairo_bool_t *is_inside); + double y); /* cairo-path-fill.c */ cairo_private cairo_status_t -_cairo_path_fixed_fill_to_traps (cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_traps_t *traps); +_cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path, + double tolerance, + cairo_polygon_t *polygon); + +cairo_private cairo_int_status_t +_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + cairo_traps_t *traps); + +cairo_private cairo_region_t * +_cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + const cairo_rectangle_int_t *extents); + +cairo_private cairo_status_t +_cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_traps_t *traps); /* cairo-path-stroke.c */ cairo_private cairo_status_t -_cairo_path_fixed_stroke_to_traps (cairo_path_fixed_t *path, +_cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path, + cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_polygon_t *polygon); + +cairo_private cairo_int_status_t +_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path, + cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + cairo_traps_t *traps); +cairo_private cairo_status_t +_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path, cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_traps_t *traps); +cairo_private cairo_status_t +_cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t *path, + cairo_stroke_style_t *stroke_style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_status_t (*add_triangle) (void *closure, + const cairo_point_t triangle[3]), + cairo_status_t (*add_triangle_fan) (void *closure, + const cairo_point_t *midpt, + const cairo_point_t *points, + int npoints), + cairo_status_t (*add_quad) (void *closure, + const cairo_point_t quad[4]), + void *closure); + /* cairo-scaled-font.c */ cairo_private void @@ -1711,7 +1688,14 @@ cairo_private cairo_status_t _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font, const cairo_glyph_t *glyphs, int num_glyphs, - cairo_rectangle_int_t *extents); + cairo_rectangle_int_t *extents, + cairo_bool_t *overlap); + +cairo_private void +_cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t *scaled_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_rectangle_int_t *extents); cairo_private cairo_status_t _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, @@ -1725,7 +1709,8 @@ _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, unsigned int width, unsigned int height, cairo_glyph_t *glyphs, - int num_glyphs); + int num_glyphs, + cairo_region_t *clip_region); cairo_private cairo_status_t _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, @@ -1749,9 +1734,9 @@ _cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph, cairo_path_fixed_t *path); cairo_private void -_cairo_scaled_glyph_set_meta_surface (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font, - cairo_surface_t *meta_surface); +_cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_surface_t *recording_surface); cairo_private cairo_int_status_t _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, @@ -1782,6 +1767,26 @@ _cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, double *dx, double *dy); +cairo_private double +_cairo_stroke_style_dash_period (const cairo_stroke_style_t *style); + +cairo_private double +_cairo_stroke_style_dash_stroked (const cairo_stroke_style_t *style); + +cairo_private cairo_bool_t +_cairo_stroke_style_dash_can_approximate (const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + double tolerance); + +cairo_private void +_cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + double tolerance, + double *dash_offset, + double *dashes, + unsigned int *num_dashes); + + /* cairo-surface.c */ cairo_private cairo_surface_t * @@ -1807,11 +1812,12 @@ _cairo_surface_create_similar_scratch (cairo_surface_t *other, int height); cairo_private cairo_surface_t * -_cairo_surface_create_similar_solid (cairo_surface_t *other, - cairo_content_t content, - int width, - int height, - const cairo_color_t *color); +_cairo_surface_create_similar_solid (cairo_surface_t *other, + cairo_content_t content, + int width, + int height, + const cairo_color_t *color, + cairo_bool_t allow_fallback); cairo_private cairo_surface_t * _cairo_surface_create_solid_pattern_surface (cairo_surface_t *other, @@ -1831,22 +1837,20 @@ cairo_private void _cairo_surface_set_font_options (cairo_surface_t *surface, cairo_font_options_t *options); -cairo_private cairo_clip_mode_t -_cairo_surface_get_clip_mode (cairo_surface_t *surface); - cairo_private cairo_status_t _cairo_surface_composite (cairo_operator_t op, const cairo_pattern_t *src, const cairo_pattern_t *mask, cairo_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height); + 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_region_t *clip_region); cairo_private cairo_status_t _cairo_surface_fill_rectangle (cairo_surface_t *surface, @@ -1874,14 +1878,14 @@ cairo_private cairo_status_t _cairo_surface_paint (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_mask (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_fill_stroke (cairo_surface_t *surface, @@ -1898,7 +1902,7 @@ _cairo_surface_fill_stroke (cairo_surface_t *surface, cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, cairo_antialias_t stroke_antialias, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_stroke (cairo_surface_t *surface, @@ -1910,7 +1914,7 @@ _cairo_surface_stroke (cairo_surface_t *surface, cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_fill (cairo_surface_t *surface, @@ -1920,7 +1924,7 @@ _cairo_surface_fill (cairo_surface_t *surface, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_show_text_glyphs (cairo_surface_t *surface, @@ -1934,7 +1938,56 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, - cairo_rectangle_int_t *extents); + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_paint_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents); + +cairo_private cairo_status_t +_cairo_surface_mask_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents); + +cairo_private cairo_status_t +_cairo_surface_stroke_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents); + +cairo_private cairo_status_t +_cairo_surface_fill_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents); + +cairo_private cairo_status_t +_cairo_surface_glyphs_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents); cairo_private cairo_status_t _cairo_surface_composite_trapezoids (cairo_operator_t op, @@ -1948,23 +2001,34 @@ _cairo_surface_composite_trapezoids (cairo_operator_t op, unsigned int width, unsigned int height, cairo_trapezoid_t *traps, - int ntraps); + int ntraps, + cairo_region_t *clip_region); + +cairo_private cairo_status_t +_cairo_surface_composite_trapezoids_as_polygon (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_antialias_t antialias, + int src_x, int src_y, + int dst_x, int dst_y, + int width, int height, + cairo_trapezoid_t *traps, + int num_traps, + cairo_region_t *clip_region); cairo_private cairo_span_renderer_t * -_cairo_surface_create_span_renderer ( - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *dst, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects); +_cairo_surface_create_span_renderer (cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *dst, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects, + cairo_region_t *clip_region); cairo_private cairo_bool_t -_cairo_surface_check_span_renderer ( - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *dst, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects); +_cairo_surface_check_span_renderer (cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *dst, + cairo_antialias_t antialias); cairo_private cairo_status_t _cairo_surface_acquire_source_image (cairo_surface_t *surface, @@ -1993,7 +2057,6 @@ _cairo_surface_release_dest_image (cairo_surface_t *surface, cairo_private cairo_status_t _cairo_surface_clone_similar (cairo_surface_t *surface, cairo_surface_t *src, - cairo_content_t content, int src_x, int src_y, int width, @@ -2023,37 +2086,7 @@ _cairo_surface_is_similar (cairo_surface_t *surface_a, cairo_surface_t *surface_b, cairo_content_t content); -cairo_private cairo_status_t -_cairo_surface_reset (cairo_surface_t *surface); - -cairo_private unsigned int -_cairo_surface_get_current_clip_serial (cairo_surface_t *surface); - -cairo_private unsigned int -_cairo_surface_allocate_clip_serial (cairo_surface_t *surface); - -cairo_private cairo_status_t -_cairo_surface_reset_clip (cairo_surface_t *surface); - -cairo_private cairo_status_t -_cairo_surface_set_clip_region (cairo_surface_t *surface, - cairo_region_t *region, - unsigned int serial); - -cairo_private cairo_int_status_t -_cairo_surface_intersect_clip_path (cairo_surface_t *surface, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias); - -cairo_private cairo_clip_t * -_cairo_surface_get_clip (cairo_surface_t *surface); - -cairo_private cairo_status_t -_cairo_surface_set_clip (cairo_surface_t *surface, cairo_clip_t *clip); - -cairo_private cairo_int_status_t +cairo_private cairo_bool_t _cairo_surface_get_extents (cairo_surface_t *surface, cairo_rectangle_int_t *extents); @@ -2069,7 +2102,8 @@ _cairo_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, unsigned int width, unsigned int height, cairo_glyph_t *glyphs, - int num_glyphs); + int num_glyphs, + cairo_region_t *clip_region); cairo_private cairo_status_t _cairo_surface_composite_fixup_unbounded (cairo_surface_t *dst, @@ -2086,7 +2120,8 @@ _cairo_surface_composite_fixup_unbounded (cairo_surface_t *dst, int dst_x, int dst_y, unsigned int width, - unsigned int height); + unsigned int height, + cairo_region_t *clip_region); cairo_private cairo_status_t _cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t *dst, @@ -2102,7 +2137,8 @@ _cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t *dst, int dst_x, int dst_y, unsigned int width, - unsigned int height); + unsigned int height, + cairo_region_t *clip_region); cairo_private cairo_bool_t _cairo_surface_is_opaque (const cairo_surface_t *surface); @@ -2181,7 +2217,7 @@ cairo_private cairo_bool_t _pixman_format_from_masks (cairo_format_masks_t *masks, pixman_format_code_t *format_ret); -cairo_private void +cairo_private cairo_bool_t _pixman_format_to_masks (pixman_format_code_t pixman_format, cairo_format_masks_t *masks); @@ -2214,19 +2250,6 @@ _cairo_image_surface_create_for_data_with_content (unsigned char *data, cairo_private void _cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface); -/* XXX: It's a nasty kludge that this appears here. Backend functions - * like this should really be static. But we're doing this to work - * around some general defects in the backend clipping interfaces, - * (see some notes in test-paginated-surface.c). - * - * I want to fix the real defects, but it's "hard" as they touch many - * backends, so doing that will require synchronizing several backend - * maintainers. - */ -cairo_private cairo_int_status_t -_cairo_image_surface_set_clip_region (void *abstract_surface, - cairo_region_t *region); - cairo_private cairo_image_surface_t * _cairo_image_surface_coerce (cairo_image_surface_t *surface, cairo_format_t format); @@ -2234,8 +2257,8 @@ cairo_private void _cairo_image_surface_span_render_row (int y, const cairo_half_open_span_t *spans, unsigned num_spans, - cairo_image_surface_t *mask, - const cairo_composite_rectangles_t *rects); + uint8_t *data, + uint32_t stride); cairo_private cairo_image_transparency_t _cairo_image_analyze_transparency (cairo_image_surface_t *image); @@ -2244,7 +2267,7 @@ cairo_private cairo_bool_t _cairo_surface_is_image (const cairo_surface_t *surface) cairo_pure; cairo_private cairo_bool_t -_cairo_surface_is_meta (const cairo_surface_t *surface) cairo_pure; +_cairo_surface_is_recording (const cairo_surface_t *surface) cairo_pure; /* cairo-pen.c */ cairo_private cairo_status_t @@ -2280,58 +2303,41 @@ cairo_private int _cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen, const cairo_slope_t *slope); -typedef struct _cairo_pen_stroke_spline { - cairo_pen_t pen; - cairo_spline_t spline; - cairo_polygon_t polygon; - cairo_point_t last_point; - cairo_point_t forward_hull_point; - cairo_point_t backward_hull_point; - int forward_vertex; - int backward_vertex; -} cairo_pen_stroke_spline_t; - -cairo_private cairo_int_status_t -_cairo_pen_stroke_spline_init (cairo_pen_stroke_spline_t *stroker, - const cairo_pen_t *pen, - const cairo_point_t *a, - const cairo_point_t *b, - const cairo_point_t *c, - const cairo_point_t *d); - -cairo_private cairo_status_t -_cairo_pen_stroke_spline (cairo_pen_stroke_spline_t *pen, - double tolerance, - cairo_traps_t *traps); - -cairo_private void -_cairo_pen_stroke_spline_fini (cairo_pen_stroke_spline_t *stroker); - /* cairo-polygon.c */ cairo_private void _cairo_polygon_init (cairo_polygon_t *polygon); +cairo_private void +_cairo_polygon_limit (cairo_polygon_t *polygon, + const cairo_box_t *boxes, + int num_boxes); + cairo_private void _cairo_polygon_fini (cairo_polygon_t *polygon); -cairo_private void -_cairo_polygon_add_edge (cairo_polygon_t *polygon, - const cairo_point_t *p1, - const cairo_point_t *p2, +cairo_private cairo_status_t +_cairo_polygon_add_line (cairo_polygon_t *polygon, + const cairo_line_t *line, + int top, int bottom, int dir); -cairo_private void +cairo_private cairo_status_t +_cairo_polygon_add_external_edge (void *polygon, + const cairo_point_t *p1, + const cairo_point_t *p2); + +cairo_private cairo_status_t _cairo_polygon_move_to (cairo_polygon_t *polygon, const cairo_point_t *point); -cairo_private void +cairo_private cairo_status_t _cairo_polygon_line_to (cairo_polygon_t *polygon, const cairo_point_t *point); -cairo_private void +cairo_private cairo_status_t _cairo_polygon_close (cairo_polygon_t *polygon); -#define _cairo_polygon_status(P) (P)->status +#define _cairo_polygon_status(P) ((cairo_polygon_t *) (P))->status /* cairo-spline.c */ cairo_private cairo_bool_t @@ -2388,6 +2394,9 @@ cairo_private cairo_bool_t _cairo_matrix_is_integer_translation(const cairo_matrix_t *matrix, int *itx, int *ity); +cairo_private cairo_bool_t +_cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix); + cairo_private cairo_bool_t _cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) cairo_pure; @@ -2407,15 +2416,13 @@ _cairo_traps_init (cairo_traps_t *traps); cairo_private void _cairo_traps_limit (cairo_traps_t *traps, - cairo_box_t *limits); + const cairo_box_t *boxes, + int num_boxes); -cairo_private cairo_bool_t -_cairo_traps_get_limit (cairo_traps_t *traps, - cairo_box_t *limits); - -cairo_private void -_cairo_traps_init_box (cairo_traps_t *traps, - const cairo_box_t *box); +cairo_private cairo_status_t +_cairo_traps_init_boxes (cairo_traps_t *traps, + const cairo_box_t *boxes, + int num_boxes); cairo_private void _cairo_traps_clear (cairo_traps_t *traps); @@ -2428,14 +2435,6 @@ _cairo_traps_fini (cairo_traps_t *traps); cairo_private void _cairo_traps_translate (cairo_traps_t *traps, int x, int y); -cairo_private cairo_status_t -_cairo_traps_tessellate_triangle (cairo_traps_t *traps, - const cairo_point_t t[3]); - -cairo_private cairo_status_t -_cairo_traps_tessellate_convex_quad (cairo_traps_t *traps, - const cairo_point_t q[4]); - cairo_private cairo_status_t _cairo_traps_tessellate_rectangle (cairo_traps_t *traps, const cairo_point_t *top_left, @@ -2446,11 +2445,28 @@ _cairo_traps_add_trap (cairo_traps_t *traps, cairo_fixed_t top, cairo_fixed_t bottom, cairo_line_t *left, cairo_line_t *right); +cairo_private cairo_status_t +_cairo_bentley_ottmann_tessellate_rectilinear_polygon (cairo_traps_t *traps, + const cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule); + cairo_private cairo_status_t _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps, const cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule); +cairo_private cairo_status_t +_cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps, + cairo_fill_rule_t fill_rule); + +cairo_private cairo_status_t +_cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps, + cairo_fill_rule_t fill_rule); + +cairo_private cairo_status_t +_cairo_bentley_ottmann_tessellate_rectilinear_traps (cairo_traps_t *traps, + cairo_fill_rule_t fill_rule); + cairo_private int _cairo_traps_contain (const cairo_traps_t *traps, double x, double y); @@ -2460,8 +2476,8 @@ _cairo_traps_extents (const cairo_traps_t *traps, cairo_box_t *extents); cairo_private cairo_int_status_t -_cairo_traps_extract_region (const cairo_traps_t *tr, - cairo_region_t **region); +_cairo_traps_extract_region (cairo_traps_t *traps, + cairo_region_t **region); cairo_private cairo_status_t _cairo_traps_path (const cairo_traps_t *traps, @@ -2474,16 +2490,6 @@ _cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps, double tx, double ty, double sx, double sy); -/* cairo-slope.c */ -cairo_private void -_cairo_slope_init (cairo_slope_t *slope, - const cairo_point_t *a, - const cairo_point_t *b); - -cairo_private int -_cairo_slope_compare (const cairo_slope_t *a, - const cairo_slope_t *b) cairo_pure; - /* cairo-pattern.c */ cairo_private cairo_pattern_t * @@ -2550,7 +2556,6 @@ enum { cairo_private cairo_int_status_t _cairo_pattern_acquire_surface (const cairo_pattern_t *pattern, cairo_surface_t *dst, - cairo_content_t content, int x, int y, unsigned int width, @@ -2568,7 +2573,6 @@ cairo_private cairo_int_status_t _cairo_pattern_acquire_surfaces (const cairo_pattern_t *src, const cairo_pattern_t *mask, cairo_surface_t *dst, - cairo_content_t src_content, int src_x, int src_y, int mask_x, @@ -2581,7 +2585,7 @@ _cairo_pattern_acquire_surfaces (const cairo_pattern_t *src, cairo_surface_attributes_t *src_attributes, cairo_surface_attributes_t *mask_attributes); -cairo_private cairo_status_t +cairo_private void _cairo_pattern_get_extents (const cairo_pattern_t *pattern, cairo_rectangle_int_t *extents); @@ -2598,23 +2602,15 @@ _cairo_pattern_equal (const cairo_pattern_t *a, cairo_private void _cairo_pattern_reset_static_data (void); -/* cairo-region.c */ - -struct _cairo_region { - cairo_status_t status; - - pixman_region32_t rgn; -}; +#if CAIRO_HAS_DRM_SURFACE cairo_private void -_cairo_region_init (cairo_region_t *region); +_cairo_drm_device_reset_static_data (void); + +#endif cairo_private void -_cairo_region_init_rectangle (cairo_region_t *region, - const cairo_rectangle_int_t *rectangle); - -cairo_private void -_cairo_region_fini (cairo_region_t *region); +_cairo_clip_reset_static_data (void); /* cairo-unicode.c */ @@ -2685,6 +2681,7 @@ slim_hidden_proto (cairo_glyph_free); slim_hidden_proto (cairo_image_surface_create); slim_hidden_proto (cairo_image_surface_create_for_data); slim_hidden_proto (cairo_image_surface_get_data); +slim_hidden_proto (cairo_image_surface_get_format); slim_hidden_proto (cairo_image_surface_get_height); slim_hidden_proto (cairo_image_surface_get_stride); slim_hidden_proto (cairo_image_surface_get_width); @@ -2711,7 +2708,6 @@ slim_hidden_proto (cairo_pattern_destroy); slim_hidden_proto (cairo_pattern_get_extend); slim_hidden_proto_no_warn (cairo_pattern_reference); slim_hidden_proto (cairo_pattern_set_matrix); -slim_hidden_proto (cairo_pattern_status); slim_hidden_proto (cairo_pop_group); slim_hidden_proto (cairo_push_group_with_content); slim_hidden_proto (cairo_rel_line_to); @@ -2746,7 +2742,6 @@ slim_hidden_proto (cairo_status); slim_hidden_proto (cairo_stroke); slim_hidden_proto (cairo_stroke_preserve); slim_hidden_proto (cairo_surface_copy_page); -slim_hidden_proto (cairo_surface_create_similar); slim_hidden_proto (cairo_surface_destroy); slim_hidden_proto (cairo_surface_finish); slim_hidden_proto (cairo_surface_flush); @@ -2781,7 +2776,9 @@ slim_hidden_proto (cairo_region_create); slim_hidden_proto (cairo_region_create_rectangle); slim_hidden_proto (cairo_region_create_rectangles); slim_hidden_proto (cairo_region_copy); +slim_hidden_proto (cairo_region_reference); slim_hidden_proto (cairo_region_destroy); +slim_hidden_proto (cairo_region_equal); slim_hidden_proto (cairo_region_status); slim_hidden_proto (cairo_region_get_extents); slim_hidden_proto (cairo_region_num_rectangles); @@ -2826,4 +2823,10 @@ _cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface); #endif +cairo_private void +_cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path); + +cairo_private void +_cairo_debug_print_clip (FILE *stream, cairo_clip_t *clip); + #endif diff --git a/gfx/cairo/cairo/src/test-fallback-surface.c b/gfx/cairo/cairo/src/test-fallback-surface.c index 2a7f14891721..3b62e55bb930 100644 --- a/gfx/cairo/cairo/src/test-fallback-surface.c +++ b/gfx/cairo/cairo/src/test-fallback-surface.c @@ -172,7 +172,6 @@ _test_fallback_surface_release_dest_image (void *abstract_surface, static cairo_status_t _test_fallback_surface_clone_similar (void *abstract_surface, cairo_surface_t *src, - cairo_content_t content, int src_x, int src_y, int width, @@ -194,7 +193,7 @@ _test_fallback_surface_clone_similar (void *abstract_surface, return CAIRO_INT_STATUS_UNSUPPORTED; } -static cairo_int_status_t +static cairo_bool_t _test_fallback_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { @@ -219,8 +218,6 @@ static const cairo_surface_backend_t test_fallback_surface_backend = { NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ - NULL, /* set_clip_region */ - NULL, /* intersect_clip_path */ _test_fallback_surface_get_extents, NULL, /* old_show_glyphs */ NULL, /* get_font_options */ diff --git a/gfx/cairo/cairo/src/test-paginated-surface.c b/gfx/cairo/cairo/src/test-paginated-surface.c index d42700c8a22f..164d4a7147f4 100644 --- a/gfx/cairo/cairo/src/test-paginated-surface.c +++ b/gfx/cairo/cairo/src/test-paginated-surface.c @@ -61,37 +61,27 @@ static const cairo_surface_backend_t test_paginated_surface_backend; static const cairo_paginated_surface_backend_t test_paginated_surface_paginated_backend; cairo_surface_t * -_cairo_test_paginated_surface_create_for_data (unsigned char *data, - cairo_content_t content, - int width, - int height, - int stride) +_cairo_test_paginated_surface_create (cairo_surface_t *target) { cairo_status_t status; - cairo_surface_t *target; cairo_surface_t *paginated; test_paginated_surface_t *surface; - target = _cairo_image_surface_create_for_data_with_content (data, content, - width, height, - stride); status = cairo_surface_status (target); - if (status) - return target; + if (unlikely (status)) + return _cairo_surface_create_in_error (status); surface = malloc (sizeof (test_paginated_surface_t)); - if (unlikely (surface == NULL)) { - cairo_surface_destroy (target); + if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } _cairo_surface_init (&surface->base, &test_paginated_surface_backend, - content); + target->content); - surface->target = target; + surface->target = cairo_surface_reference (target); paginated = _cairo_paginated_surface_create (&surface->base, - content, width, height, + target->content, &test_paginated_surface_paginated_backend); status = paginated->status; if (status == CAIRO_STATUS_SUCCESS) { @@ -115,49 +105,7 @@ _test_paginated_surface_finish (void *abstract_surface) return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_test_paginated_surface_set_clip_region (void *abstract_surface, - cairo_region_t *region) -{ - test_paginated_surface_t *surface = abstract_surface; - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return CAIRO_STATUS_SUCCESS; - - /* XXX: The whole surface backend clipping interface is a giant - * disaster right now. In particular, its uncleanness shows up - * when trying to implement one surface that wraps another one (as - * we are doing here). - * - * Here are two of the problems that show up: - * - * 1. The most critical piece of information in all this stuff, - * the "clip" isn't getting passed to the backend - * functions. Instead the generic surface layer is caching that as - * surface->clip. This is a problem for surfaces like this one - * that do wrapping. Our base surface will have the clip set, but - * our target's surface will not. - * - * 2. We're here in our backend's set_clip_region function, and we - * want to call into our target surface's set_clip_region. - * Generally, we would do this by calling an equivalent - * _cairo_surface function, but _cairo_surface_set_clip_region - * does not have the same signature/semantics, (it has the - * clip_serial stuff as well). - * - * We kludge around each of these by manually copying the clip - * object from our base surface into the target's base surface - * (yuck!) and by reaching directly into the image surface's - * set_clip_region instead of calling into the generic - * _cairo_surface_set_clip_region (double yuck!). - */ - - surface->target->clip = surface->base.clip; - - return _cairo_image_surface_set_clip_region (surface->target, region); -} - -static cairo_int_status_t +static cairo_bool_t _test_paginated_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { @@ -170,14 +118,14 @@ static cairo_int_status_t _test_paginated_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { test_paginated_surface_t *surface = abstract_surface; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return CAIRO_STATUS_SUCCESS; - return _cairo_surface_paint (surface->target, op, source, extents); + return _cairo_surface_paint (surface->target, op, source, clip); } static cairo_int_status_t @@ -185,14 +133,15 @@ _test_paginated_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { test_paginated_surface_t *surface = abstract_surface; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return CAIRO_STATUS_SUCCESS; - return _cairo_surface_mask (surface->target, op, source, mask, extents); + return _cairo_surface_mask (surface->target, + op, source, mask, clip); } static cairo_int_status_t @@ -205,7 +154,7 @@ _test_paginated_surface_stroke (void *abstract_surface, cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { test_paginated_surface_t *surface = abstract_surface; @@ -215,7 +164,8 @@ _test_paginated_surface_stroke (void *abstract_surface, return _cairo_surface_stroke (surface->target, op, source, path, style, ctm, ctm_inverse, - tolerance, antialias, extents); + tolerance, antialias, + clip); } static cairo_int_status_t @@ -226,7 +176,7 @@ _test_paginated_surface_fill (void *abstract_surface, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { test_paginated_surface_t *surface = abstract_surface; @@ -235,7 +185,8 @@ _test_paginated_surface_fill (void *abstract_surface, return _cairo_surface_fill (surface->target, op, source, path, fill_rule, - tolerance, antialias, extents); + tolerance, antialias, + clip); } static cairo_bool_t @@ -258,7 +209,7 @@ _test_paginated_surface_show_text_glyphs (void *abstract_surface, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { test_paginated_surface_t *surface = abstract_surface; @@ -268,8 +219,10 @@ _test_paginated_surface_show_text_glyphs (void *abstract_surface, return _cairo_surface_show_text_glyphs (surface->target, op, source, utf8, utf8_len, glyphs, num_glyphs, - clusters, num_clusters, cluster_flags, - scaled_font, extents); + clusters, num_clusters, + cluster_flags, + scaled_font, + clip); } @@ -302,8 +255,6 @@ static const cairo_surface_backend_t test_paginated_surface_backend = { NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ - _test_paginated_surface_set_clip_region, - NULL, /* intersect_clip_path */ _test_paginated_surface_get_extents, NULL, /* old_show_glyphs */ NULL, /* get_font_options */ @@ -319,11 +270,10 @@ static const cairo_surface_backend_t test_paginated_surface_backend = { _test_paginated_surface_mask, _test_paginated_surface_stroke, _test_paginated_surface_fill, - NULL, /* show_glyphs */ + NULL, /* replaced by show_text_glyphs */ NULL, /* snapshot */ NULL, /* is_similar */ - NULL, /* reset */ NULL, /* fill_stroke */ NULL, /* create_solid_pattern_surface */ NULL, /* can_repaint_solid_pattern_surface */ diff --git a/gfx/cairo/cairo/src/test-paginated-surface.h b/gfx/cairo/cairo/src/test-paginated-surface.h index 4879cfcd1001..76ce6890e74f 100644 --- a/gfx/cairo/cairo/src/test-paginated-surface.h +++ b/gfx/cairo/cairo/src/test-paginated-surface.h @@ -41,11 +41,7 @@ CAIRO_BEGIN_DECLS cairo_surface_t * -_cairo_test_paginated_surface_create_for_data (unsigned char *data, - cairo_content_t content, - int width, - int height, - int stride); +_cairo_test_paginated_surface_create (cairo_surface_t *target); CAIRO_END_DECLS diff --git a/gfx/cairo/clip-invariant.patch b/gfx/cairo/clip-invariant.patch new file mode 100644 index 000000000000..08ba4d4deaf6 --- /dev/null +++ b/gfx/cairo/clip-invariant.patch @@ -0,0 +1,1255 @@ +diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c +index 2acc8b5..019249e 100644 +--- a/src/cairo-gl-surface.c ++++ b/src/cairo-gl-surface.c +@@ -2012,13 +2012,14 @@ typedef struct _cairo_gl_surface_span_renderer { + + cairo_gl_composite_setup_t setup; + ++ int xmin, xmax; ++ + cairo_operator_t op; + cairo_antialias_t antialias; + + cairo_gl_surface_t *dst; + cairo_region_t *clip; + +- cairo_composite_rectangles_t composite_rectangles; + GLuint vbo; + void *vbo_base; + unsigned int vbo_size; +@@ -2049,11 +2050,11 @@ _cairo_gl_span_renderer_flush (cairo_gl_surface_span_renderer_t *renderer) + cairo_region_get_rectangle (renderer->clip, i, &rect); + + glScissor (rect.x, rect.y, rect.width, rect.height); +- glDrawArrays (GL_LINES, 0, count); ++ glDrawArrays (GL_QUADS, 0, count); + } + glDisable (GL_SCISSOR_TEST); + } else { +- glDrawArrays (GL_LINES, 0, count); ++ glDrawArrays (GL_QUADS, 0, count); + } + } + +@@ -2134,72 +2135,87 @@ _cairo_gl_emit_span_vertex (cairo_gl_surface_span_renderer_t *renderer, + + static void + _cairo_gl_emit_span (cairo_gl_surface_span_renderer_t *renderer, +- int x1, int x2, int y, uint8_t alpha) ++ int x, int y1, int y2, ++ uint8_t alpha) + { + float *vertices = _cairo_gl_span_renderer_get_vbo (renderer, 2); + +- _cairo_gl_emit_span_vertex (renderer, x1, y, alpha, vertices); +- _cairo_gl_emit_span_vertex (renderer, x2, y, alpha, ++ _cairo_gl_emit_span_vertex (renderer, x, y1, alpha, vertices); ++ _cairo_gl_emit_span_vertex (renderer, x, y2, alpha, + vertices + renderer->vertex_size / 4); + } + +-/* Emits the contents of the span renderer rows as GL_LINES with the span's +- * alpha. +- * +- * Unlike the image surface, which is compositing into a temporary, we emit +- * coverage even for alpha == 0, in case we're using an unbounded operator. +- * But it means we avoid having to do the fixup. +- */ ++static void ++_cairo_gl_emit_rectangle (cairo_gl_surface_span_renderer_t *renderer, ++ int x1, int y1, ++ int x2, int y2, ++ int coverage) ++{ ++ _cairo_gl_emit_span (renderer, x1, y1, y2, coverage); ++ _cairo_gl_emit_span (renderer, x2, y2, y1, coverage); ++} ++ + static cairo_status_t +-_cairo_gl_surface_span_renderer_render_row ( +- void *abstract_renderer, +- int y, +- const cairo_half_open_span_t *spans, +- unsigned num_spans) ++_cairo_gl_render_bounded_spans (void *abstract_renderer, ++ int y, int height, ++ const cairo_half_open_span_t *spans, ++ unsigned num_spans) + { + cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; +- int xmin = renderer->composite_rectangles.mask.x; +- int xmax = xmin + renderer->composite_rectangles.width; +- int prev_x = xmin; +- int prev_alpha = 0; +- unsigned i; +- int x_translate; +- +- /* Make sure we're within y-range. */ +- if (y < renderer->composite_rectangles.mask.y || +- y >= renderer->composite_rectangles.mask.y + +- renderer->composite_rectangles.height) ++ ++ if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + +- x_translate = renderer->composite_rectangles.dst.x - +- renderer->composite_rectangles.mask.x; +- y += renderer->composite_rectangles.dst.y - +- renderer->composite_rectangles.mask.y; ++ do { ++ if (spans[0].coverage) { ++ _cairo_gl_emit_rectangle (renderer, ++ spans[0].x, y, ++ spans[1].x, y + height, ++ spans[0].coverage); ++ } + +- /* 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; ++ spans++; ++ } while (--num_spans > 1); + +- /* Set the intermediate spans. */ +- for (; i < num_spans; i++) { +- int x = spans[i].x; ++ return CAIRO_STATUS_SUCCESS; ++} + +- if (x >= xmax) +- break; ++static cairo_status_t ++_cairo_gl_render_unbounded_spans (void *abstract_renderer, ++ int y, int height, ++ const cairo_half_open_span_t *spans, ++ unsigned num_spans) ++{ ++ cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; + +- _cairo_gl_emit_span (renderer, +- prev_x + x_translate, x + x_translate, y, +- prev_alpha); ++ if (num_spans == 0) { ++ _cairo_gl_emit_rectangle (renderer, ++ renderer->xmin, y, ++ renderer->xmax, y + height, ++ 0); ++ return CAIRO_STATUS_SUCCESS; ++ } + +- prev_x = x; +- prev_alpha = spans[i].coverage; ++ if (spans[0].x != renderer->xmin) { ++ _cairo_gl_emit_rectangle (renderer, ++ renderer->xmin, y, ++ spans[0].x, y + height, ++ 0); + } + +- if (prev_x < xmax) { +- _cairo_gl_emit_span (renderer, +- prev_x + x_translate, xmax + x_translate, y, +- prev_alpha); ++ do { ++ _cairo_gl_emit_rectangle (renderer, ++ spans[0].x, y, ++ spans[1].x, y + height, ++ spans[0].coverage); ++ spans++; ++ } while (--num_spans > 1); ++ ++ if (spans[0].x != renderer->xmax) { ++ _cairo_gl_emit_rectangle (renderer, ++ spans[0].x, y, ++ renderer->xmax, y + height, ++ 0); + } + + return CAIRO_STATUS_SUCCESS; +@@ -2274,8 +2290,6 @@ _cairo_gl_surface_create_span_renderer (cairo_operator_t op, + cairo_gl_surface_t *dst = abstract_dst; + cairo_gl_surface_span_renderer_t *renderer; + cairo_status_t status; +- int width = rects->width; +- int height = rects->height; + cairo_surface_attributes_t *src_attributes; + GLenum err; + +diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c +index 48d8013..d52979d 100644 +--- a/src/cairo-image-surface.c ++++ b/src/cairo-image-surface.c +@@ -1390,11 +1390,13 @@ typedef struct _cairo_image_surface_span_renderer { + const cairo_pattern_t *pattern; + cairo_antialias_t antialias; + ++ uint8_t *mask_data; ++ uint32_t mask_stride; ++ + 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; + +@@ -1403,66 +1405,46 @@ _cairo_image_surface_span_render_row ( + int y, + const cairo_half_open_span_t *spans, + unsigned num_spans, +- cairo_image_surface_t *mask, +- const cairo_composite_rectangles_t *rects) ++ uint8_t *data, ++ uint32_t stride) + { +- int xmin = rects->mask.x; +- int xmax = xmin + rects->width; + uint8_t *row; +- int prev_x = xmin; +- int prev_alpha = 0; + unsigned i; + +- /* Make sure we're within y-range. */ +- y -= rects->mask.y; +- if (y < 0 || y >= rects->height) ++ if (num_spans == 0) + return; + +- row = (uint8_t*)(mask->data) + y*(size_t)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); +- } ++ row = data + y * stride; ++ for (i = 0; i < num_spans - 1; i++) { ++ if (! spans[i].coverage) ++ continue; ++ ++ /* We implement setting 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 (spans[i+1].x == spans[i].x + 1) { ++ row[spans[i].x] = spans[i].coverage; ++ } else { ++ memset (row + spans[i].x, ++ spans[i].coverage, ++ spans[i+1].x - spans[i].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); + } + } + + static cairo_status_t +-_cairo_image_surface_span_renderer_render_row ( ++_cairo_image_surface_span_renderer_render_rows ( + void *abstract_renderer, + int y, ++ int height, + const cairo_half_open_span_t *spans, + unsigned num_spans) + { + cairo_image_surface_span_renderer_t *renderer = abstract_renderer; +- _cairo_image_surface_span_render_row (y, spans, num_spans, renderer->mask, &renderer->composite_rectangles); ++ while (height--) ++ _cairo_image_surface_span_render_row (y++, spans, num_spans, renderer->mask_data, renderer->mask_stride); + return CAIRO_STATUS_SUCCESS; + } + +@@ -1517,11 +1499,11 @@ _cairo_image_surface_span_renderer_finish (void *abstract_renderer) + &dst->base, + src_attributes, + src->width, src->height, +- rects->width, rects->height, ++ width, height, + rects->src.x, rects->src.y, + 0, 0, /* mask.x, mask.y */ + rects->dst.x, rects->dst.y, +- rects->width, rects->height, ++ width, height, + dst->clip_region); + } + } +@@ -1567,7 +1549,7 @@ _cairo_image_surface_create_span_renderer (cairo_operator_t op, + + 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->base.render_rows = _cairo_image_surface_span_renderer_render_rows; + renderer->op = op; + renderer->pattern = pattern; + renderer->antialias = antialias; +@@ -1604,6 +1586,9 @@ _cairo_image_surface_create_span_renderer (cairo_operator_t op, + _cairo_image_surface_span_renderer_destroy (renderer); + return _cairo_span_renderer_create_in_error (status); + } ++ ++ renderer->mask_data = renderer->mask->data - rects->mask.x - rects->mask.y * renderer->mask->stride; ++ renderer->mask_stride = renderer->mask->stride; + return &renderer->base; + } + +diff --git a/src/cairo-spans-private.h b/src/cairo-spans-private.h +index e29a567..af3b38c 100644 +--- a/src/cairo-spans-private.h ++++ b/src/cairo-spans-private.h +@@ -47,26 +47,24 @@ typedef struct _cairo_half_open_span { + * surfaces if they want to composite spans instead of trapezoids. */ + typedef struct _cairo_span_renderer cairo_span_renderer_t; + struct _cairo_span_renderer { ++ /* Private status variable. */ ++ cairo_status_t status; ++ + /* Called to destroy the renderer. */ + cairo_destroy_func_t destroy; + +- /* Render the spans on row y of the source by whatever compositing +- * method is required. The function should ignore spans outside +- * the bounding box set by the init() function. */ +- cairo_status_t (*render_row)( +- void *abstract_renderer, +- int y, +- const cairo_half_open_span_t *coverages, +- unsigned num_coverages); ++ /* Render the spans on row y of the destination by whatever compositing ++ * method is required. */ ++ cairo_warn cairo_status_t ++ (*render_rows) (void *abstract_renderer, ++ int y, int height, ++ const cairo_half_open_span_t *coverages, ++ unsigned num_coverages); + + /* Called after all rows have been rendered to perform whatever + * final rendering step is required. This function is called just + * once before the renderer is destroyed. */ +- cairo_status_t (*finish)( +- void *abstract_renderer); +- +- /* Private status variable. */ +- cairo_status_t status; ++ cairo_status_t (*finish) (void *abstract_renderer); + }; + + /* Scan converter interface. */ +diff --git a/src/cairo-spans.c b/src/cairo-spans.c +index af3b85f..69894c1 100644 +--- a/src/cairo-spans.c ++++ b/src/cairo-spans.c +@@ -275,13 +275,15 @@ _cairo_scan_converter_create_in_error (cairo_status_t status) + } + + static cairo_status_t +-_cairo_nil_span_renderer_render_row ( ++_cairo_nil_span_renderer_render_rows ( + void *abstract_renderer, + int y, ++ int height, + const cairo_half_open_span_t *coverages, + unsigned num_coverages) + { + (void) y; ++ (void) height; + (void) coverages; + (void) num_coverages; + return _cairo_span_renderer_status (abstract_renderer); +@@ -310,7 +312,7 @@ _cairo_span_renderer_set_error ( + ASSERT_NOT_REACHED; + } + if (renderer->status == CAIRO_STATUS_SUCCESS) { +- renderer->render_row = _cairo_nil_span_renderer_render_row; ++ renderer->render_rows = _cairo_nil_span_renderer_render_rows; + renderer->finish = _cairo_nil_span_renderer_finish; + renderer->status = error; + } +diff --git a/src/cairo-tor-scan-converter.c b/src/cairo-tor-scan-converter.c +index 29262c2..2b9fb1b 100644 +--- a/src/cairo-tor-scan-converter.c ++++ b/src/cairo-tor-scan-converter.c +@@ -128,27 +128,29 @@ blit_with_span_renderer( + cairo_span_renderer_t *span_renderer, + struct pool *span_pool, + int y, ++ int height, + int xmin, + int xmax); + + static glitter_status_t +-blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y); ++blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height); + + #define GLITTER_BLIT_COVERAGES_ARGS \ + cairo_span_renderer_t *span_renderer, \ + struct pool *span_pool + +-#define GLITTER_BLIT_COVERAGES(cells, y, xmin, xmax) do { \ ++#define GLITTER_BLIT_COVERAGES(cells, y, height,xmin, xmax) do { \ + cairo_status_t status = blit_with_span_renderer (cells, \ + span_renderer, \ + span_pool, \ +- y, xmin, xmax); \ ++ y, height, \ ++ xmin, xmax); \ + if (unlikely (status)) \ + return status; \ + } while (0) + +-#define GLITTER_BLIT_COVERAGES_EMPTY(y, xmin, xmax) do { \ +- cairo_status_t status = blit_empty_with_span_renderer (span_renderer, y); \ ++#define GLITTER_BLIT_COVERAGES_EMPTY(y, height, xmin, xmax) do { \ ++ cairo_status_t status = blit_empty_with_span_renderer (span_renderer, y, height); \ + if (unlikely (status)) \ + return status; \ + } while (0) +@@ -309,8 +311,8 @@ typedef int grid_area_t; + #define UNROLL3(x) x x x + + struct quorem { +- int quo; +- int rem; ++ int32_t quo; ++ int32_t rem; + }; + + /* Header for a chunk of memory in a memory pool. */ +@@ -382,6 +384,7 @@ struct edge { + /* Original sign of the edge: +1 for downwards, -1 for upwards + * edges. */ + int dir; ++ int vertical; + }; + + /* Number of subsample rows per y-bucket. Must be GRID_Y. */ +@@ -389,18 +392,28 @@ struct edge { + + #define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT) + ++struct bucket { ++ /* Unsorted list of edges starting within this bucket. */ ++ struct edge *edges; ++ ++ /* Set to non-zero if there are edges starting strictly within the ++ * bucket. */ ++ unsigned have_inside_edges; ++}; ++ + /* A collection of sorted and vertically clipped edges of the polygon. + * Edges are moved from the polygon to an active list while scan + * converting. */ + struct polygon { +- /* The vertical clip extents. */ ++ /* The clip extents. */ ++ grid_scaled_x_t xmin, xmax; + grid_scaled_y_t ymin, ymax; + + /* Array of edges all starting in the same bucket. An edge is put + * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when + * it is added to the polygon. */ +- struct edge **y_buckets; +- struct edge *y_buckets_embedded[64]; ++ struct bucket *y_buckets; ++ struct bucket y_buckets_embedded[64]; + + struct { + struct pool base[1]; +@@ -702,7 +715,6 @@ static void + cell_list_fini(struct cell_list *cells) + { + pool_fini (cells->cell_pool.base); +- cell_list_init (cells); + } + + /* Empty the cell list. This is called at the start of every pixel +@@ -715,6 +727,26 @@ cell_list_reset (struct cell_list *cells) + pool_reset (cells->cell_pool.base); + } + ++static struct cell * ++cell_list_alloc (struct cell_list *cells, ++ struct cell **cursor, ++ struct cell *tail, ++ int x) ++{ ++ struct cell *cell; ++ ++ cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell)); ++ if (unlikely (NULL == cell)) ++ return NULL; ++ ++ *cursor = cell; ++ cell->next = tail; ++ cell->x = x; ++ cell->uncovered_area = 0; ++ cell->covered_height = 0; ++ return cell; ++} ++ + /* Find a cell at the given x-coordinate. Returns %NULL if a new cell + * needed to be allocated but couldn't be. Cells must be found with + * non-decreasing x-coordinate until the cell list is rewound using +@@ -737,22 +769,10 @@ cell_list_find (struct cell_list *cells, int x) + } + cells->cursor = cursor; + +- if (tail->x == x) { ++ if (tail->x == x) + return tail; +- } else { +- struct cell *cell; +- +- cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell)); +- if (unlikely (NULL == cell)) +- return NULL; + +- *cursor = cell; +- cell->next = tail; +- cell->x = x; +- cell->uncovered_area = 0; +- cell->covered_height = 0; +- return cell; +- } ++ return cell_list_alloc (cells, cursor, tail, x); + } + + /* Find two cells at x1 and x2. This is exactly equivalent +@@ -832,9 +852,8 @@ cell_list_find_pair(struct cell_list *cells, int x1, int x2) + /* Add an unbounded subpixel span covering subpixels >= x to the + * coverage cells. */ + static glitter_status_t +-cell_list_add_unbounded_subspan( +- struct cell_list *cells, +- grid_scaled_x_t x) ++cell_list_add_unbounded_subspan (struct cell_list *cells, ++ grid_scaled_x_t x) + { + struct cell *cell; + int ix, fx; +@@ -907,20 +926,24 @@ cell_list_render_edge( + struct edge *edge, + int sign) + { +- struct quorem x1 = edge->x; +- struct quorem x2 = x1; + grid_scaled_y_t y1, y2, dy; + grid_scaled_x_t dx; + int ix1, ix2; + grid_scaled_x_t fx1, fx2; + +- x2.quo += edge->dxdy_full.quo; +- x2.rem += edge->dxdy_full.rem; +- if (x2.rem >= 0) { +- ++x2.quo; +- x2.rem -= edge->dy; ++ struct quorem x1 = edge->x; ++ struct quorem x2 = x1; ++ ++ if (! edge->vertical) { ++ x2.quo += edge->dxdy_full.quo; ++ x2.rem += edge->dxdy_full.rem; ++ if (x2.rem >= 0) { ++ ++x2.quo; ++ x2.rem -= edge->dy; ++ } ++ ++ edge->x = x2; + } +- edge->x = x2; + + GRID_X_TO_INT_FRAC(x1.quo, ix1, fx1); + GRID_X_TO_INT_FRAC(x2.quo, ix2, fx2); +@@ -1026,6 +1049,7 @@ static void + polygon_init (struct polygon *polygon) + { + polygon->ymin = polygon->ymax = 0; ++ polygon->xmin = polygon->xmax = 0; + polygon->y_buckets = polygon->y_buckets_embedded; + pool_init (polygon->edge_pool.base, + 8192 - sizeof (struct _pool_chunk), +@@ -1045,10 +1069,11 @@ polygon_fini (struct polygon *polygon) + * receive new edges and clip them to the vertical range + * [ymin,ymax). */ + static glitter_status_t +-polygon_reset( +- struct polygon *polygon, +- grid_scaled_y_t ymin, +- grid_scaled_y_t ymax) ++polygon_reset (struct polygon *polygon, ++ grid_scaled_x_t xmin, ++ grid_scaled_x_t xmax, ++ grid_scaled_y_t ymin, ++ grid_scaled_y_t ymax) + { + unsigned h = ymax - ymin; + unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + EDGE_Y_BUCKET_HEIGHT-1, +@@ -1065,14 +1090,16 @@ polygon_reset( + polygon->y_buckets = polygon->y_buckets_embedded; + if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) { + polygon->y_buckets = _cairo_malloc_ab (num_buckets, +- sizeof (struct edge *)); ++ sizeof (struct bucket)); + if (unlikely (NULL == polygon->y_buckets)) + goto bail_no_mem; + } +- memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *)); ++ memset (polygon->y_buckets, 0, num_buckets * sizeof (struct bucket)); + + polygon->ymin = ymin; + polygon->ymax = ymax; ++ polygon->xmin = xmin; ++ polygon->xmax = xmax; + return GLITTER_STATUS_SUCCESS; + + bail_no_mem: +@@ -1086,10 +1113,13 @@ _polygon_insert_edge_into_its_y_bucket( + struct polygon *polygon, + struct edge *e) + { +- unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin); +- struct edge **ptail = &polygon->y_buckets[ix]; ++ unsigned j = e->ytop - polygon->ymin; ++ unsigned ix = j / EDGE_Y_BUCKET_HEIGHT; ++ unsigned offset = j % EDGE_Y_BUCKET_HEIGHT; ++ struct edge **ptail = &polygon->y_buckets[ix].edges; + e->next = *ptail; + *ptail = e; ++ polygon->y_buckets[ix].have_inside_edges |= offset; + } + + inline static glitter_status_t +@@ -1115,30 +1145,53 @@ polygon_add_edge (struct polygon *polygon, + dx = edge->line.p2.x - edge->line.p1.x; + dy = edge->line.p2.y - edge->line.p1.y; + e->dy = dy; +- e->dxdy = floored_divrem (dx, dy); +- +- if (ymin <= edge->top) +- ytop = edge->top; +- else +- ytop = ymin; +- if (ytop == edge->line.p1.y) { +- e->x.quo = edge->line.p1.x; +- e->x.rem = 0; +- } else { +- e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy); +- e->x.quo += edge->line.p1.x; +- } +- + e->dir = edge->dir; ++ ++ ytop = edge->top >= ymin ? edge->top : ymin; ++ ybot = edge->bottom <= ymax ? edge->bottom : ymax; + e->ytop = ytop; +- ybot = edge->bottom < ymax ? edge->bottom : ymax; + e->height_left = ybot - ytop; + +- if (e->height_left >= GRID_Y) { +- e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy); +- } else { ++ if (dx == 0) { ++ e->vertical = TRUE; ++ e->x.quo = edge->line.p1.x; ++ e->x.rem = 0; ++ e->dxdy.quo = 0; ++ e->dxdy.rem = 0; + e->dxdy_full.quo = 0; + e->dxdy_full.rem = 0; ++ ++ /* Drop edges to the right of the clip extents. */ ++ if (e->x.quo >= polygon->xmax) ++ return GLITTER_STATUS_SUCCESS; ++ ++ /* Offset vertical edges at the left side of the clip extents ++ * to just shy of the left side. We depend on this when ++ * checking for possible intersections within the clip ++ * rectangle. */ ++ if (e->x.quo <= polygon->xmin) { ++ e->x.quo = polygon->xmin - 1; ++ } ++ } else { ++ e->vertical = FALSE; ++ e->dxdy = floored_divrem (dx, dy); ++ if (ytop == edge->line.p1.y) { ++ e->x.quo = edge->line.p1.x; ++ e->x.rem = 0; ++ } else { ++ e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy); ++ e->x.quo += edge->line.p1.x; ++ } ++ ++ if (e->x.quo >= polygon->xmax && e->dxdy.quo >= 0) ++ return GLITTER_STATUS_SUCCESS; ++ ++ if (e->height_left >= GRID_Y) { ++ e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy); ++ } else { ++ e->dxdy_full.quo = 0; ++ e->dxdy_full.rem = 0; ++ } + } + + _polygon_insert_edge_into_its_y_bucket (polygon, e); +@@ -1161,31 +1214,30 @@ active_list_init(struct active_list *active) + active_list_reset(active); + } + +-static void +-active_list_fini( +- struct active_list *active) +-{ +- active_list_reset(active); +-} +- + /* Merge the edges in an unsorted list of edges into a sorted + * list. The sort order is edges ascending by edge->x.quo. Returns + * the new head of the sorted list. */ + static struct edge * + merge_unsorted_edges(struct edge *sorted_head, struct edge *unsorted_head) + { +- struct edge *head = unsorted_head; + struct edge **cursor = &sorted_head; + int x; + +- while (NULL != head) { ++ if (sorted_head == NULL) { ++ sorted_head = unsorted_head; ++ unsorted_head = unsorted_head->next; ++ sorted_head->next = NULL; ++ if (unsorted_head == NULL) ++ return sorted_head; ++ } ++ ++ do { ++ struct edge *next = unsorted_head->next; + struct edge *prev = *cursor; +- struct edge *next = head->next; +- x = head->x.quo; + +- if (NULL == prev || x < prev->x.quo) { ++ x = unsorted_head->x.quo; ++ if (x < prev->x.quo) + cursor = &sorted_head; +- } + + while (1) { + UNROLL3({ +@@ -1196,26 +1248,29 @@ merge_unsorted_edges(struct edge *sorted_head, struct edge *unsorted_head) + }); + } + +- head->next = *cursor; +- *cursor = head; ++ unsorted_head->next = *cursor; ++ *cursor = unsorted_head; ++ unsorted_head = next; ++ } while (unsorted_head != NULL); + +- head = next; +- } + return sorted_head; + } + + /* Test if the edges on the active list can be safely advanced by a + * full row without intersections or any edges ending. */ + inline static int +-active_list_can_step_full_row( +- struct active_list *active) ++active_list_can_step_full_row (struct active_list *active, ++ grid_scaled_x_t xmin) + { ++ const struct edge *e; ++ grid_scaled_x_t prev_x = INT_MIN; ++ + /* Recomputes the minimum height of all edges on the active + * list if we have been dropping edges. */ + if (active->min_height <= 0) { +- struct edge *e = active->head; + int min_height = INT_MAX; + ++ e = active->head; + while (NULL != e) { + if (e->height_left < min_height) + min_height = e->height_left; +@@ -1225,27 +1280,38 @@ active_list_can_step_full_row( + active->min_height = min_height; + } + +- /* Check for intersections only if no edges end during the next +- * row. */ +- if (active->min_height >= GRID_Y) { +- grid_scaled_x_t prev_x = INT_MIN; +- struct edge *e = active->head; +- while (NULL != e) { +- struct quorem x = e->x; ++ if (active->min_height < GRID_Y) ++ return 0; + ++ /* Check for intersections as no edges end during the next row. */ ++ e = active->head; ++ while (NULL != e) { ++ struct quorem x = e->x; ++ ++ if (! e->vertical) { + x.quo += e->dxdy_full.quo; + x.rem += e->dxdy_full.rem; + if (x.rem >= 0) + ++x.quo; ++ } + +- if (x.quo <= prev_x) ++ /* There's may be an intersection if the edge sort order might ++ * change. */ ++ if (x.quo <= prev_x) { ++ /* Ignore intersections to the left of the clip extents. ++ * This assumes that all vertical edges on or at the left ++ * side of the clip rectangle have been shifted slightly ++ * to the left in polygon_add_edge(). */ ++ if (prev_x >= xmin || x.quo >= xmin || e->x.quo >= xmin) + return 0; ++ } ++ else { + prev_x = x.quo; +- e = e->next; + } +- return 1; ++ e = e->next; + } +- return 0; ++ ++ return 1; + } + + /* Merges edges on the given subpixel row from the polygon to the +@@ -1261,7 +1327,7 @@ active_list_merge_edges_from_polygon( + unsigned ix = EDGE_Y_BUCKET_INDEX(y, polygon->ymin); + int min_height = active->min_height; + struct edge *subrow_edges = NULL; +- struct edge **ptail = &polygon->y_buckets[ix]; ++ struct edge **ptail = &polygon->y_buckets[ix].edges; + + while (1) { + struct edge *tail = *ptail; +@@ -1277,8 +1343,10 @@ active_list_merge_edges_from_polygon( + ptail = &tail->next; + } + } +- active->head = merge_unsorted_edges(active->head, subrow_edges); +- active->min_height = min_height; ++ if (subrow_edges) { ++ active->head = merge_unsorted_edges(active->head, subrow_edges); ++ active->min_height = min_height; ++ } + } + + /* Advance the edges on the active list by one subsample row by +@@ -1439,11 +1507,13 @@ apply_nonzero_fill_rule_and_step_edges (struct active_list *active, + } + } + +- right_edge->x.quo += right_edge->dxdy_full.quo; +- right_edge->x.rem += right_edge->dxdy_full.rem; +- if (right_edge->x.rem >= 0) { +- ++right_edge->x.quo; +- right_edge->x.rem -= right_edge->dy; ++ if (! right_edge->vertical) { ++ right_edge->x.quo += right_edge->dxdy_full.quo; ++ right_edge->x.rem += right_edge->dxdy_full.rem; ++ if (right_edge->x.rem >= 0) { ++ ++right_edge->x.quo; ++ right_edge->x.rem -= right_edge->dy; ++ } + } + } + +@@ -1472,6 +1542,7 @@ apply_evenodd_fill_rule_and_step_edges (struct active_list *active, + left_edge = *cursor; + while (NULL != left_edge) { + struct edge *right_edge; ++ int winding = left_edge->dir; + + left_edge->height_left -= GRID_Y; + if (left_edge->height_left) +@@ -1490,17 +1561,22 @@ apply_evenodd_fill_rule_and_step_edges (struct active_list *active, + else + *cursor = right_edge->next; + ++ winding += right_edge->dir; ++ if ((winding & 1) == 0) { + if (right_edge->next == NULL || + right_edge->next->x.quo != right_edge->x.quo) + { + break; + } ++ } + +- right_edge->x.quo += right_edge->dxdy_full.quo; +- right_edge->x.rem += right_edge->dxdy_full.rem; +- if (right_edge->x.rem >= 0) { +- ++right_edge->x.quo; +- right_edge->x.rem -= right_edge->dy; ++ if (! right_edge->vertical) { ++ right_edge->x.quo += right_edge->dxdy_full.quo; ++ right_edge->x.rem += right_edge->dxdy_full.rem; ++ if (right_edge->x.rem >= 0) { ++ ++right_edge->x.quo; ++ right_edge->x.rem -= right_edge->dy; ++ } + } + } + +@@ -1537,8 +1613,14 @@ blit_span( + } + } + +-#define GLITTER_BLIT_COVERAGES(coverages, y, xmin, xmax) \ +- blit_cells(coverages, raster_pixels + (y)*raster_stride, xmin, xmax) ++#define GLITTER_BLIT_COVERAGES(coverages, y, height, xmin, xmax) \ ++ do { \ ++ int __y = y; \ ++ int __h = height; \ ++ do { \ ++ blit_cells(coverages, raster_pixels + (__y)*raster_stride, xmin, xmax); \ ++ } while (--__h); \ ++ } while (0) + + static void + blit_cells( +@@ -1597,7 +1679,6 @@ static void + _glitter_scan_converter_fini(glitter_scan_converter_t *converter) + { + polygon_fini(converter->polygon); +- active_list_fini(converter->active); + cell_list_fini(converter->coverages); + converter->xmin=0; + converter->ymin=0; +@@ -1641,7 +1722,7 @@ glitter_scan_converter_reset( + + active_list_reset(converter->active); + cell_list_reset(converter->coverages); +- status = polygon_reset(converter->polygon, ymin, ymax); ++ status = polygon_reset(converter->polygon, xmin, xmax, ymin, ymax); + if (status) + return status; + +@@ -1711,19 +1792,48 @@ glitter_scan_converter_add_edge (glitter_scan_converter_t *converter, + #endif + + #ifndef GLITTER_BLIT_COVERAGES_EMPTY +-# define GLITTER_BLIT_COVERAGES_EMPTY(y, xmin, xmax) ++# define GLITTER_BLIT_COVERAGES_EMPTY(y0, y1, xmin, xmax) + #endif + ++static cairo_bool_t ++active_list_is_vertical (struct active_list *active) ++{ ++ struct edge *e; ++ ++ for (e = active->head; e != NULL; e = e->next) { ++ if (! e->vertical) ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++static void ++step_edges (struct active_list *active, int count) ++{ ++ struct edge **cursor = &active->head; ++ struct edge *edge; ++ ++ for (edge = *cursor; edge != NULL; edge = *cursor) { ++ edge->height_left -= GRID_Y * count; ++ if (edge->height_left) ++ cursor = &edge->next; ++ else ++ *cursor = edge->next; ++ } ++} ++ + I glitter_status_t + glitter_scan_converter_render( + glitter_scan_converter_t *converter, + int nonzero_fill, + GLITTER_BLIT_COVERAGES_ARGS) + { +- int i; ++ int i, j; + int ymax_i = converter->ymax / GRID_Y; + int ymin_i = converter->ymin / GRID_Y; + int xmin_i, xmax_i; ++ grid_scaled_x_t xmin = converter->xmin; + int h = ymax_i - ymin_i; + struct polygon *polygon = converter->polygon; + struct cell_list *coverages = converter->coverages; +@@ -1738,22 +1848,28 @@ glitter_scan_converter_render( + GLITTER_BLIT_COVERAGES_BEGIN; + + /* Render each pixel row. */ +- for (i=0; iy_buckets[i]) { ++ if (polygon->y_buckets[i].edges == NULL) { + if (! active->head) { +- GLITTER_BLIT_COVERAGES_EMPTY (i+ymin_i, xmin_i, xmax_i); ++ for (; j < h && ! polygon->y_buckets[j].edges; j++) ++ ; ++ GLITTER_BLIT_COVERAGES_EMPTY (i+ymin_i, j-i, xmin_i, xmax_i); + continue; + } +- +- do_full_step = active_list_can_step_full_row (active); ++ do_full_step = active_list_can_step_full_row (active, xmin); ++ } ++ else if (! polygon->y_buckets[i].have_inside_edges) { ++ grid_scaled_y_t y = (i+ymin_i)*GRID_Y; ++ active_list_merge_edges_from_polygon (active, y, polygon); ++ do_full_step = active_list_can_step_full_row (active, xmin); + } +- +- cell_list_reset (coverages); + + if (do_full_step) { + /* Step by a full pixel row's worth. */ +@@ -1764,8 +1880,20 @@ glitter_scan_converter_render( + status = apply_evenodd_fill_rule_and_step_edges (active, + coverages); + } ++ ++ if (active_list_is_vertical (active)) { ++ while (j < h && ++ polygon->y_buckets[j].edges == NULL && ++ active->min_height >= 2*GRID_Y) ++ { ++ active->min_height -= GRID_Y; ++ j++; ++ } ++ if (j != i + 1) ++ step_edges (active, j - (i + 1)); ++ } + } else { +- /* Subsample this row. */ ++ /* Supersample this row. */ + grid_scaled_y_t suby; + for (suby = 0; suby < GRID_Y; suby++) { + grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby; +@@ -1787,13 +1915,13 @@ glitter_scan_converter_render( + if (unlikely (status)) + return status; + +- GLITTER_BLIT_COVERAGES(coverages, i+ymin_i, xmin_i, xmax_i); ++ GLITTER_BLIT_COVERAGES(coverages, i+ymin_i, j-i, xmin_i, xmax_i); ++ cell_list_reset (coverages); + +- if (! active->head) { ++ if (! active->head) + active->min_height = INT_MAX; +- } else { ++ else + active->min_height -= GRID_Y; +- } + } + + /* Clean up the coverage blitter. */ +@@ -1807,21 +1935,20 @@ glitter_scan_converter_render( + * scan converter subclass. */ + + static glitter_status_t +-blit_with_span_renderer( +- struct cell_list *cells, +- cairo_span_renderer_t *renderer, +- struct pool *span_pool, +- int y, +- int xmin, +- int xmax) ++blit_with_span_renderer (struct cell_list *cells, ++ cairo_span_renderer_t *renderer, ++ struct pool *span_pool, ++ int y, int height, ++ int xmin, int xmax) + { + struct cell *cell = cells->head; + int prev_x = xmin; + int cover = 0; + cairo_half_open_span_t *spans; + unsigned num_spans; ++ + if (cell == NULL) +- return CAIRO_STATUS_SUCCESS; ++ return blit_empty_with_span_renderer (renderer, y, height); + + /* Skip cells to the left of the clip region. */ + while (cell != NULL && cell->x < xmin) { +@@ -1833,12 +1960,12 @@ blit_with_span_renderer( + /* Count number of cells remaining. */ + { + struct cell *next = cell; +- num_spans = 0; +- while (next) { ++ num_spans = 1; ++ while (next != NULL) { + next = next->next; + ++num_spans; + } +- num_spans = 2*num_spans + 1; ++ num_spans = 2*num_spans; + } + + /* Allocate enough spans for the row. */ +@@ -1853,6 +1980,7 @@ blit_with_span_renderer( + for (; cell != NULL; cell = cell->next) { + int x = cell->x; + int area; ++ + if (x >= xmax) + break; + +@@ -1872,20 +2000,26 @@ blit_with_span_renderer( + prev_x = x+1; + } + +- if (prev_x < xmax) { ++ if (prev_x <= xmax) { + spans[num_spans].x = prev_x; + spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover); + ++num_spans; + } + ++ if (prev_x < xmax && cover) { ++ spans[num_spans].x = xmax; ++ spans[num_spans].coverage = 0; ++ ++num_spans; ++ } ++ + /* Dump them into the renderer. */ +- return renderer->render_row (renderer, y, spans, num_spans); ++ return renderer->render_rows (renderer, y, height, spans, num_spans); + } + + static glitter_status_t +-blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y) ++blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height) + { +- return renderer->render_row (renderer, y, NULL, 0); ++ return renderer->render_rows (renderer, y, height, NULL, 0); + } + + struct _cairo_tor_scan_converter { +diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c +index 82d1cf5..d4575a3 100644 +--- a/src/cairo-win32-surface.c ++++ b/src/cairo-win32-surface.c +@@ -1954,6 +1954,9 @@ typedef struct _cairo_win32_surface_span_renderer { + const cairo_pattern_t *pattern; + cairo_antialias_t antialias; + ++ uint8_t *mask_data; ++ uint32_t mask_stride; ++ + cairo_image_surface_t *mask; + cairo_win32_surface_t *dst; + cairo_region_t *clip_region; +@@ -1962,14 +1965,16 @@ typedef struct _cairo_win32_surface_span_renderer { + } cairo_win32_surface_span_renderer_t; + + static cairo_status_t +-_cairo_win32_surface_span_renderer_render_row ( ++_cairo_win32_surface_span_renderer_render_rows ( + void *abstract_renderer, + int y, ++ int height, + const cairo_half_open_span_t *spans, + unsigned num_spans) + { + cairo_win32_surface_span_renderer_t *renderer = abstract_renderer; +- _cairo_image_surface_span_render_row (y, spans, num_spans, renderer->mask, &renderer->composite_rectangles); ++ while (height--) ++ _cairo_image_surface_span_render_row (y++, spans, num_spans, renderer->mask_data, renderer->mask_stride); + return CAIRO_STATUS_SUCCESS; + } + +@@ -2066,8 +2071,7 @@ _cairo_win32_surface_create_span_renderer (cairo_operator_t op, + + renderer->base.destroy = _cairo_win32_surface_span_renderer_destroy; + renderer->base.finish = _cairo_win32_surface_span_renderer_finish; +- renderer->base.render_row = +- _cairo_win32_surface_span_renderer_render_row; ++ renderer->base.render_rows = _cairo_win32_surface_span_renderer_render_rows; + renderer->op = op; + renderer->pattern = pattern; + renderer->antialias = antialias; +@@ -2088,6 +2092,9 @@ _cairo_win32_surface_create_span_renderer (cairo_operator_t op, + _cairo_win32_surface_span_renderer_destroy (renderer); + return _cairo_span_renderer_create_in_error (status); + } ++ ++ renderer->mask_data = renderer->mask->data - rects->mask.x - rects->mask.y * renderer->mask->stride; ++ renderer->mask_stride = renderer->mask->stride; + return &renderer->base; + } + +diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c +index a7a40b8..566d9fb 100644 +--- a/src/cairo-xlib-display.c ++++ b/src/cairo-xlib-display.c +@@ -407,6 +407,10 @@ _cairo_xlib_display_get (Display *dpy, + display->buggy_pad_reflect = TRUE; + } + ++ /* gradients don't seem to work */ ++ display->buggy_gradients = TRUE; ++ ++ + /* XXX workaround; see https://bugzilla.mozilla.org/show_bug.cgi?id=413583 */ + /* If buggy_repeat_force == -1, then initialize. + * - set to -2, meaning "nothing was specified", and we trust the above detection. +diff --git a/src/cairoint.h b/src/cairoint.h +index 58850ab..1cdf6ff 100644 +--- a/src/cairoint.h ++++ b/src/cairoint.h +@@ -2257,8 +2257,8 @@ cairo_private void + _cairo_image_surface_span_render_row (int y, + const cairo_half_open_span_t *spans, + unsigned num_spans, +- cairo_image_surface_t *mask, +- const cairo_composite_rectangles_t *rects); ++ uint8_t *data, ++ uint32_t stride); + + cairo_private cairo_image_transparency_t + _cairo_image_analyze_transparency (cairo_image_surface_t *image); diff --git a/gfx/cairo/d2d.patch b/gfx/cairo/d2d.patch new file mode 100644 index 000000000000..83db386254d0 --- /dev/null +++ b/gfx/cairo/d2d.patch @@ -0,0 +1,466 @@ +commit 4a412c0b144ed1fdde668e0e91241bac8bedd579 +Author: Jeff Muizelaar +Date: Sun Jan 24 14:04:33 2010 -0500 + + d2d + +diff --git a/src/cairo-fixed-private.h b/src/cairo-fixed-private.h +index c299def..a37ca6a 100644 +--- a/src/cairo-fixed-private.h ++++ b/src/cairo-fixed-private.h +@@ -50,6 +50,7 @@ + + #define CAIRO_FIXED_ONE ((cairo_fixed_t)(1 << CAIRO_FIXED_FRAC_BITS)) + #define CAIRO_FIXED_ONE_DOUBLE ((double)(1 << CAIRO_FIXED_FRAC_BITS)) ++#define CAIRO_FIXED_ONE_FLOAT ((float)(1 << CAIRO_FIXED_FRAC_BITS)) + #define CAIRO_FIXED_EPSILON ((cairo_fixed_t)(1)) + + #define CAIRO_FIXED_FRAC_MASK (((cairo_fixed_unsigned_t)(-1)) >> (CAIRO_FIXED_BITS - CAIRO_FIXED_FRAC_BITS)) +@@ -141,6 +142,12 @@ _cairo_fixed_to_double (cairo_fixed_t f) + return ((double) f) / CAIRO_FIXED_ONE_DOUBLE; + } + ++static inline float ++_cairo_fixed_to_float (cairo_fixed_t f) ++{ ++ return ((float) f) / CAIRO_FIXED_ONE_FLOAT; ++} ++ + static inline int + _cairo_fixed_is_integer (cairo_fixed_t f) + { +diff --git a/src/cairo-win32-private.h b/src/cairo-win32-private.h +index b9926bb..ba57595 100644 +--- a/src/cairo-win32-private.h ++++ b/src/cairo-win32-private.h +@@ -231,4 +231,19 @@ inline BOOL ModifyWorldTransform(HDC hdc, CONST XFORM * lpxf, DWORD mode) { retu + + #endif + ++#ifdef CAIRO_HAS_DWRITE_FONT ++CAIRO_BEGIN_DECLS ++ ++cairo_public cairo_int_status_t ++cairo_dwrite_show_glyphs_on_surface(void *surface, ++ cairo_operator_t op, ++ const cairo_pattern_t *source, ++ cairo_glyph_t *glyphs, ++ int num_glyphs, ++ cairo_scaled_font_t *scaled_font, ++ cairo_rectangle_int_t *extents); ++ ++ ++CAIRO_END_DECLS ++#endif /* CAIRO_HAS_DWRITE_FONT */ + #endif /* CAIRO_WIN32_PRIVATE_H */ +diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c +index 0dc5e76..bee00b1 100644 +--- a/src/cairo-win32-surface.c ++++ b/src/cairo-win32-surface.c +@@ -1547,152 +1547,158 @@ _cairo_win32_surface_show_glyphs (void *surface, + int *remaining_glyphs) + { + #if defined(CAIRO_HAS_WIN32_FONT) && !defined(WINCE) +- cairo_win32_surface_t *dst = surface; +- +- WORD glyph_buf_stack[STACK_GLYPH_SIZE]; +- WORD *glyph_buf = glyph_buf_stack; +- int dxy_buf_stack[2 * STACK_GLYPH_SIZE]; +- int *dxy_buf = dxy_buf_stack; +- +- BOOL win_result = 0; +- int i, j; ++ if (scaled_font->backend->type == CAIRO_FONT_TYPE_DWRITE) { ++#ifdef CAIRO_HAS_DWRITE_FONT ++ return cairo_dwrite_show_glyphs_on_surface(surface, op, source, glyphs, num_glyphs, scaled_font, clip); ++#endif ++ } else { ++ cairo_win32_surface_t *dst = surface; ++ ++ WORD glyph_buf_stack[STACK_GLYPH_SIZE]; ++ WORD *glyph_buf = glyph_buf_stack; ++ int dxy_buf_stack[2 * STACK_GLYPH_SIZE]; ++ int *dxy_buf = dxy_buf_stack; + +- cairo_solid_pattern_t *solid_pattern; +- COLORREF color; ++ BOOL win_result = 0; ++ int i, j; + +- cairo_matrix_t device_to_logical; ++ cairo_solid_pattern_t *solid_pattern; ++ COLORREF color; + +- int start_x, start_y; +- double user_x, user_y; +- int logical_x, logical_y; +- unsigned int glyph_index_option; ++ cairo_matrix_t device_to_logical; + +- /* We can only handle win32 fonts */ +- if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_WIN32) +- return CAIRO_INT_STATUS_UNSUPPORTED; ++ int start_x, start_y; ++ double user_x, user_y; ++ int logical_x, logical_y; ++ unsigned int glyph_index_option; + +- /* We can only handle opaque solid color sources */ +- if (!_cairo_pattern_is_opaque_solid(source)) +- return CAIRO_INT_STATUS_UNSUPPORTED; ++ /* We can only handle win32 fonts */ ++ if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_WIN32) ++ return CAIRO_INT_STATUS_UNSUPPORTED; + +- /* We can only handle operator SOURCE or OVER with the destination +- * having no alpha */ +- if ((op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) || +- (dst->format != CAIRO_FORMAT_RGB24)) +- return CAIRO_INT_STATUS_UNSUPPORTED; ++ /* We can only handle opaque solid color sources */ ++ if (!_cairo_pattern_is_opaque_solid(source)) ++ return CAIRO_INT_STATUS_UNSUPPORTED; + +- /* If we have a fallback mask clip set on the dst, we have +- * to go through the fallback path, but only if we're not +- * doing this for printing */ +- if (clip != NULL) { +- if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) == 0) { +- cairo_region_t *clip_region; +- cairo_status_t status; ++ /* We can only handle operator SOURCE or OVER with the destination ++ * having no alpha */ ++ if ((op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) || ++ (dst->format != CAIRO_FORMAT_RGB24)) ++ return CAIRO_INT_STATUS_UNSUPPORTED; + +- status = _cairo_clip_get_region (clip, &clip_region); +- assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); +- if (status) +- return status; ++ /* If we have a fallback mask clip set on the dst, we have ++ * to go through the fallback path, but only if we're not ++ * doing this for printing */ ++ if (clip != NULL) { ++ if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) == 0) { ++ cairo_region_t *clip_region; ++ cairo_status_t status; ++ ++ status = _cairo_clip_get_region (clip, &clip_region); ++ assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); ++ if (status) ++ return status; + +- _cairo_win32_surface_set_clip_region (surface, clip_region); ++ _cairo_win32_surface_set_clip_region (surface, clip_region); ++ } + } +- } + +- solid_pattern = (cairo_solid_pattern_t *)source; +- color = RGB(((int)solid_pattern->color.red_short) >> 8, +- ((int)solid_pattern->color.green_short) >> 8, +- ((int)solid_pattern->color.blue_short) >> 8); ++ solid_pattern = (cairo_solid_pattern_t *)source; ++ color = RGB(((int)solid_pattern->color.red_short) >> 8, ++ ((int)solid_pattern->color.green_short) >> 8, ++ ((int)solid_pattern->color.blue_short) >> 8); + +- cairo_win32_scaled_font_get_device_to_logical(scaled_font, &device_to_logical); ++ cairo_win32_scaled_font_get_device_to_logical(scaled_font, &device_to_logical); + +- SaveDC(dst->dc); ++ SaveDC(dst->dc); + +- cairo_win32_scaled_font_select_font(scaled_font, dst->dc); +- SetTextColor(dst->dc, color); +- SetTextAlign(dst->dc, TA_BASELINE | TA_LEFT); +- SetBkMode(dst->dc, TRANSPARENT); ++ cairo_win32_scaled_font_select_font(scaled_font, dst->dc); ++ SetTextColor(dst->dc, color); ++ SetTextAlign(dst->dc, TA_BASELINE | TA_LEFT); ++ SetBkMode(dst->dc, TRANSPARENT); + +- if (num_glyphs > STACK_GLYPH_SIZE) { +- glyph_buf = (WORD *) _cairo_malloc_ab (num_glyphs, sizeof(WORD)); +- dxy_buf = (int *) _cairo_malloc_abc (num_glyphs, sizeof(int), 2); +- } ++ if (num_glyphs > STACK_GLYPH_SIZE) { ++ glyph_buf = (WORD *) _cairo_malloc_ab (num_glyphs, sizeof(WORD)); ++ dxy_buf = (int *) _cairo_malloc_abc (num_glyphs, sizeof(int), 2); ++ } + +- /* It is vital that dx values for dxy_buf are calculated from the delta of +- * _logical_ x coordinates (not user x coordinates) or else the sum of all +- * previous dx values may start to diverge from the current glyph's x +- * coordinate due to accumulated rounding error. As a result strings could +- * be painted shorter or longer than expected. */ ++ /* It is vital that dx values for dxy_buf are calculated from the delta of ++ * _logical_ x coordinates (not user x coordinates) or else the sum of all ++ * previous dx values may start to diverge from the current glyph's x ++ * coordinate due to accumulated rounding error. As a result strings could ++ * be painted shorter or longer than expected. */ + +- user_x = glyphs[0].x; +- user_y = glyphs[0].y; ++ user_x = glyphs[0].x; ++ user_y = glyphs[0].y; + +- cairo_matrix_transform_point(&device_to_logical, +- &user_x, &user_y); ++ cairo_matrix_transform_point(&device_to_logical, ++ &user_x, &user_y); + +- logical_x = _cairo_lround (user_x); +- logical_y = _cairo_lround (user_y); ++ logical_x = _cairo_lround (user_x); ++ logical_y = _cairo_lround (user_y); + +- start_x = logical_x; +- start_y = logical_y; ++ start_x = logical_x; ++ start_y = logical_y; + +- for (i = 0, j = 0; i < num_glyphs; ++i, j = 2 * i) { +- glyph_buf[i] = (WORD) glyphs[i].index; +- if (i == num_glyphs - 1) { +- dxy_buf[j] = 0; +- dxy_buf[j+1] = 0; +- } else { +- double next_user_x = glyphs[i+1].x; +- double next_user_y = glyphs[i+1].y; +- int next_logical_x, next_logical_y; ++ for (i = 0, j = 0; i < num_glyphs; ++i, j = 2 * i) { ++ glyph_buf[i] = (WORD) glyphs[i].index; ++ if (i == num_glyphs - 1) { ++ dxy_buf[j] = 0; ++ dxy_buf[j+1] = 0; ++ } else { ++ double next_user_x = glyphs[i+1].x; ++ double next_user_y = glyphs[i+1].y; ++ int next_logical_x, next_logical_y; + +- cairo_matrix_transform_point(&device_to_logical, +- &next_user_x, &next_user_y); ++ cairo_matrix_transform_point(&device_to_logical, ++ &next_user_x, &next_user_y); + +- next_logical_x = _cairo_lround (next_user_x); +- next_logical_y = _cairo_lround (next_user_y); ++ next_logical_x = _cairo_lround (next_user_x); ++ next_logical_y = _cairo_lround (next_user_y); + +- dxy_buf[j] = _cairo_lround (next_logical_x - logical_x); +- dxy_buf[j+1] = _cairo_lround (logical_y - next_logical_y); +- /* note that GDI coordinate system is inverted */ ++ dxy_buf[j] = _cairo_lround (next_logical_x - logical_x); ++ dxy_buf[j+1] = _cairo_lround (logical_y - next_logical_y); ++ /* note that GDI coordinate system is inverted */ + +- logical_x = next_logical_x; +- logical_y = next_logical_y; +- } +- } ++ logical_x = next_logical_x; ++ logical_y = next_logical_y; ++ } ++ } + +- /* Using glyph indices for a Type 1 font does not work on a +- * printer DC. The win32 printing surface will convert the the +- * glyph indices of Type 1 fonts to the unicode values. +- */ +- if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) && +- _cairo_win32_scaled_font_is_type1 (scaled_font)) +- { +- glyph_index_option = 0; +- } +- else +- { +- glyph_index_option = ETO_GLYPH_INDEX; +- } ++ /* Using glyph indices for a Type 1 font does not work on a ++ * printer DC. The win32 printing surface will convert the the ++ * glyph indices of Type 1 fonts to the unicode values. ++ */ ++ if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) && ++ _cairo_win32_scaled_font_is_type1 (scaled_font)) ++ { ++ glyph_index_option = 0; ++ } ++ else ++ { ++ glyph_index_option = ETO_GLYPH_INDEX; ++ } + +- win_result = ExtTextOutW(dst->dc, +- start_x, +- start_y, +- glyph_index_option | ETO_PDY, +- NULL, +- glyph_buf, +- num_glyphs, +- dxy_buf); +- if (!win_result) { +- _cairo_win32_print_gdi_error("_cairo_win32_surface_show_glyphs(ExtTextOutW failed)"); +- } ++ win_result = ExtTextOutW(dst->dc, ++ start_x, ++ start_y, ++ glyph_index_option | ETO_PDY, ++ NULL, ++ glyph_buf, ++ num_glyphs, ++ dxy_buf); ++ if (!win_result) { ++ _cairo_win32_print_gdi_error("_cairo_win32_surface_show_glyphs(ExtTextOutW failed)"); ++ } + +- RestoreDC(dst->dc, -1); ++ RestoreDC(dst->dc, -1); + +- if (glyph_buf != glyph_buf_stack) { +- free(glyph_buf); +- free(dxy_buf); ++ if (glyph_buf != glyph_buf_stack) { ++ free(glyph_buf); ++ free(dxy_buf); ++ } ++ return (win_result) ? CAIRO_STATUS_SUCCESS : CAIRO_INT_STATUS_UNSUPPORTED; + } +- return (win_result) ? CAIRO_STATUS_SUCCESS : CAIRO_INT_STATUS_UNSUPPORTED; + #else + return CAIRO_INT_STATUS_UNSUPPORTED; + #endif +diff --git a/src/cairo-win32.h b/src/cairo-win32.h +index 6b86d4e..fcf20b8 100644 +--- a/src/cairo-win32.h ++++ b/src/cairo-win32.h +@@ -109,6 +109,63 @@ cairo_win32_scaled_font_get_device_to_logical (cairo_scaled_font_t *scaled_font, + + #endif /* CAIRO_HAS_WIN32_FONT */ + ++#if CAIRO_HAS_DWRITE_FONT ++ ++/* ++ * Win32 DirectWrite font support ++ */ ++cairo_public cairo_font_face_t * ++cairo_dwrite_font_face_create_for_dwrite_fontface(void *dwrite_font, void *dwrite_font_face); ++ ++#endif /* CAIRO_HAS_DWRITE_FONT */ ++ ++#if CAIRO_HAS_D2D_SURFACE ++ ++/** ++ * Create a D2D surface for an HWND ++ * ++ * \param wnd Handle for the window ++ * \return New cairo surface ++ */ ++cairo_public cairo_surface_t * ++cairo_d2d_surface_create_for_hwnd(HWND wnd); ++ ++/** ++ * Create a D2D surface of a certain size. ++ * ++ * \param format Cairo format of the surface ++ * \param width Width of the surface ++ * \param height Height of the surface ++ * \return New cairo surface ++ */ ++cairo_public cairo_surface_t * ++cairo_d2d_surface_create(cairo_format_t format, ++ int width, ++ int height); ++ ++/** ++ * Present the backbuffer for a surface create for an HWND. This needs ++ * to be called when the owner of the original window surface wants to ++ * actually present the executed drawing operations to the screen. ++ * ++ * \param surface D2D surface. ++ */ ++void cairo_d2d_present_backbuffer(cairo_surface_t *surface); ++ ++/** ++ * Scroll the surface, this only moves the surface graphics, it does not ++ * actually scroll child windows or anything like that. Nor does it invalidate ++ * that area of the window. ++ * ++ * \param surface The d2d surface this operation should apply to. ++ * \param x The x delta for the movement ++ * \param y The y delta for the movement ++ * \param clip The clip rectangle, the is the 'part' of the surface that needs ++ * scrolling. ++ */ ++void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t *clip); ++#endif ++ + CAIRO_END_DECLS + + #else /* CAIRO_HAS_WIN32_SURFACE */ +diff --git a/src/cairo.h b/src/cairo.h +index 3a8b8a6..21827aa 100644 +--- a/src/cairo.h ++++ b/src/cairo.h +@@ -1370,7 +1370,8 @@ typedef enum _cairo_font_type { + CAIRO_FONT_TYPE_FT, + CAIRO_FONT_TYPE_WIN32, + CAIRO_FONT_TYPE_QUARTZ, +- CAIRO_FONT_TYPE_USER ++ CAIRO_FONT_TYPE_USER, ++ CAIRO_FONT_TYPE_DWRITE + } cairo_font_type_t; + + cairo_public cairo_font_type_t +@@ -2009,7 +2010,8 @@ typedef enum _cairo_surface_type { + CAIRO_SURFACE_TYPE_TEE, + CAIRO_SURFACE_TYPE_XML, + CAIRO_SURFACE_TYPE_SKIA, +- CAIRO_SURFACE_TYPE_DDRAW ++ CAIRO_SURFACE_TYPE_DDRAW, ++ CAIRO_SURFACE_TYPE_D2D + } cairo_surface_type_t; + + cairo_public cairo_surface_type_t +diff --git a/src/cairoint.h b/src/cairoint.h +index b942b4b..58850ab 100644 +--- a/src/cairoint.h ++++ b/src/cairoint.h +@@ -587,6 +587,12 @@ extern const cairo_private struct _cairo_font_face_backend _cairo_win32_font_fac + + #endif + ++#if CAIRO_HAS_DWRITE_FONT ++ ++extern const cairo_private struct _cairo_font_face_backend _cairo_dwrite_font_face_backend; ++ ++#endif ++ + #if CAIRO_HAS_QUARTZ_FONT + + extern const cairo_private struct _cairo_font_face_backend _cairo_quartz_font_face_backend; +@@ -932,7 +938,12 @@ typedef struct _cairo_traps { + #define CAIRO_FT_FONT_FAMILY_DEFAULT "" + #define CAIRO_USER_FONT_FAMILY_DEFAULT "@cairo:" + +-#if CAIRO_HAS_WIN32_FONT ++#if CAIRO_HAS_DWRITE_FONT ++ ++#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_WIN32_FONT_FAMILY_DEFAULT ++#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_dwrite_font_face_backend ++ ++#elif CAIRO_HAS_WIN32_FONT + + #define CAIRO_FONT_FAMILY_DEFAULT CAIRO_WIN32_FONT_FAMILY_DEFAULT + #define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_win32_font_face_backend +@@ -2617,7 +2628,7 @@ cairo_private int + _cairo_ucs4_to_utf8 (uint32_t unicode, + char *utf8); + +-#if CAIRO_HAS_WIN32_FONT || CAIRO_HAS_QUARTZ_FONT || CAIRO_HAS_PDF_OPERATORS ++#if CAIRO_HAS_WIN32_FONT || CAIRO_HAS_QUARTZ_FONT || CAIRO_HAS_PDF_OPERATORS || CAIRO_HAS_DW_FONT + # define CAIRO_HAS_UTF8_TO_UTF16 1 + #endif + #if CAIRO_HAS_UTF8_TO_UTF16 diff --git a/gfx/cairo/disable-server-gradients.patch b/gfx/cairo/disable-server-gradients.patch new file mode 100644 index 000000000000..2d8fb5b6d34c --- /dev/null +++ b/gfx/cairo/disable-server-gradients.patch @@ -0,0 +1,21 @@ +commit 31579379422b75c3fe01b75d363e03f9b1e85604 +Author: Jeff Muizelaar +Date: Fri Mar 12 16:07:59 2010 -0500 + + xlib bugs + +diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c +index a7a40b8..566d9fb 100644 +--- a/src/cairo-xlib-display.c ++++ b/src/cairo-xlib-display.c +@@ -407,6 +407,10 @@ _cairo_xlib_display_get (Display *dpy, + display->buggy_pad_reflect = TRUE; + } + ++ /* gradients don't seem to work */ ++ display->buggy_gradients = TRUE; ++ ++ + /* XXX workaround; see https://bugzilla.mozilla.org/show_bug.cgi?id=413583 */ + /* If buggy_repeat_force == -1, then initialize. + * - set to -2, meaning "nothing was specified", and we trust the above detection. diff --git a/gfx/cairo/fix-clip-copy.patch b/gfx/cairo/fix-clip-copy.patch new file mode 100644 index 000000000000..c2c3b2fc87ad --- /dev/null +++ b/gfx/cairo/fix-clip-copy.patch @@ -0,0 +1,30 @@ +commit f49a9740350d2f0d69ed59e913f0263a899cfb2a +Author: Jeff Muizelaar +Date: Fri Jan 29 14:39:24 2010 -0500 + + Fix clip copy + +diff --git a/src/cairo-clip.c b/src/cairo-clip.c +index 8d66a5f..6acbcff 100644 +--- a/src/cairo-clip.c ++++ b/src/cairo-clip.c +@@ -280,13 +280,12 @@ cairo_clip_t * + _cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other) + { + if (other != NULL) { +- if (other->path == NULL) { +- _cairo_clip_init (clip); +- clip = NULL; +- } else { +- clip->all_clipped = other->all_clipped; +- clip->path = _cairo_clip_path_reference (other->path); +- } ++ clip->all_clipped = other->all_clipped; ++ clip->path = _cairo_clip_path_reference (other->path); ++ ++ /* this guy is here because of the weird return semantics of _cairo_clip_init_copy */ ++ if (!other->path) ++ return NULL; + } else { + _cairo_clip_init (clip); + } diff --git a/gfx/cairo/fix-clip-region-simplification.patch b/gfx/cairo/fix-clip-region-simplification.patch new file mode 100644 index 000000000000..825130a2efb1 --- /dev/null +++ b/gfx/cairo/fix-clip-region-simplification.patch @@ -0,0 +1 @@ +stg show: fix-clip-region-simplication: Unknown patch or revision name diff --git a/gfx/cairo/fix-unnecessary-fallback.patch b/gfx/cairo/fix-unnecessary-fallback.patch new file mode 100644 index 000000000000..9b0d63b4fc5a --- /dev/null +++ b/gfx/cairo/fix-unnecessary-fallback.patch @@ -0,0 +1,22 @@ +diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c +index 353cbcd..1a053d0 100644 +--- a/src/cairo-xlib-surface.c ++++ b/src/cairo-xlib-surface.c +@@ -1804,7 +1804,6 @@ _recategorize_composite_operation (cairo_xlib_surface_t *dst, + { + /* Can we use the core protocol? */ + if (! have_mask && +- src->owns_pixmap && + src->depth == dst->depth && + _cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) && + ! _operator_needs_alpha_composite (op, +@@ -1818,7 +1817,8 @@ _recategorize_composite_operation (cairo_xlib_surface_t *dst, + return DO_XTILE; + } + +- if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT) ++ if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT && ++ (src->width != 1 || src->height != 1)) + return DO_UNSUPPORTED; + + if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src)) diff --git a/gfx/cairo/fix-zero-length-gradient.patch b/gfx/cairo/fix-zero-length-gradient.patch new file mode 100644 index 000000000000..a920d76e98a5 --- /dev/null +++ b/gfx/cairo/fix-zero-length-gradient.patch @@ -0,0 +1 @@ +stg show: fix-zero-len-graident: Unknown patch or revision name diff --git a/gfx/cairo/handle-a1.patch b/gfx/cairo/handle-a1.patch new file mode 100644 index 000000000000..eaad3d69ef22 --- /dev/null +++ b/gfx/cairo/handle-a1.patch @@ -0,0 +1,25 @@ +commit 82aab44a9005047743538d52e9fbc27fd6ce408a +Author: Chris Wilson +Date: Fri Mar 19 17:23:20 2010 -0400 + + commit f07195860620959c27d43080a7b987e28222735a + + xlib: Handle a1 image uploads through converter + + Fixes test/large-source [xlib] + +diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c +index 1a053d0..8f773b0 100644 +--- a/src/cairo-xlib-surface.c ++++ b/src/cairo-xlib-surface.c +@@ -1155,7 +1155,9 @@ _draw_image_surface (cairo_xlib_surface_t *surface, + int dither_adjustment = dither_row[x_off]; + int a, r, g, b; + +- if (image_masks.bpp <= 8) ++ if (image_masks.bpp == 1) ++ in_pixel = !! (((uint8_t*)row)[x/8] & (1 << (x & 7))); ++ else if (image_masks.bpp <= 8) + in_pixel = ((uint8_t*)row)[x]; + else if (image_masks.bpp <= 16) + in_pixel = ((uint16_t*)row)[x]; diff --git a/gfx/cairo/surface-clipper.patch b/gfx/cairo/surface-clipper.patch new file mode 100644 index 000000000000..608841147013 --- /dev/null +++ b/gfx/cairo/surface-clipper.patch @@ -0,0 +1,26 @@ +commit 061cc774a861f349334117203c301dee202f9f26 +Author: Jeff Muizelaar +Date: Wed Apr 7 23:05:48 2010 -0400 + + Remove an incorrect optimization that was causing the clip + not to be set when it should've been. + + This happens when the path is equal but the parents aren't shared. + +diff --git a/src/cairo-surface-clipper.c b/src/cairo-surface-clipper.c +index d536f0c..03610d1 100644 +--- a/src/cairo-surface-clipper.c ++++ b/src/cairo-surface-clipper.c +@@ -78,12 +78,6 @@ _cairo_surface_clipper_set_clip (cairo_surface_clipper_t *clipper, + if (clip != NULL && clip->path == clipper->clip.path) + return CAIRO_STATUS_SUCCESS; + +- if (clip != NULL && clipper->clip.path != NULL && +- _cairo_path_fixed_equal (&clip->path->path, &clipper->clip.path->path)) +- { +- return CAIRO_STATUS_SUCCESS; +- } +- + /* all clipped out state should never propagate this far */ + assert (clip == NULL || clip->path != NULL); + diff --git a/gfx/thebes/src/gfxContext.cpp b/gfx/thebes/src/gfxContext.cpp index 75f6f8631c75..5862a6615b7b 100644 --- a/gfx/thebes/src/gfxContext.cpp +++ b/gfx/thebes/src/gfxContext.cpp @@ -628,7 +628,10 @@ void gfxContext::UpdateSurfaceClip() { NewPath(); - Rectangle(gfxRect(0,0,0,0)); + // we paint an empty rectangle to ensure the clip is propagated to + // the destination surface + SetDeviceColor(gfxRGBA(0,0,0,0)); + Rectangle(gfxRect(0,1,1,0)); Fill(); } diff --git a/layout/reftests/bugs/456219-1-mask-wArA.png b/layout/reftests/bugs/456219-1-mask-wArA.png index 28f1e24f1cba..f3901b4aa216 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wArA.png and b/layout/reftests/bugs/456219-1-mask-wArA.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wArB.png b/layout/reftests/bugs/456219-1-mask-wArB.png index 577776062797..703b9ad4d714 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wArB.png and b/layout/reftests/bugs/456219-1-mask-wArB.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wArC.png b/layout/reftests/bugs/456219-1-mask-wArC.png index 70c45a421bde..21f943b42534 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wArC.png and b/layout/reftests/bugs/456219-1-mask-wArC.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wArD.png b/layout/reftests/bugs/456219-1-mask-wArD.png index 3cc868f36233..2da66e5dcfbe 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wArD.png and b/layout/reftests/bugs/456219-1-mask-wArD.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wArE.png b/layout/reftests/bugs/456219-1-mask-wArE.png index 87b59d591045..27bd8ea9bb94 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wArE.png and b/layout/reftests/bugs/456219-1-mask-wArE.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wBrA.png b/layout/reftests/bugs/456219-1-mask-wBrA.png index 9d3eb3c91b88..b0df7a20653a 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wBrA.png and b/layout/reftests/bugs/456219-1-mask-wBrA.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wBrB.png b/layout/reftests/bugs/456219-1-mask-wBrB.png index bbc88f1dd0d8..9a88c6596417 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wBrB.png and b/layout/reftests/bugs/456219-1-mask-wBrB.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wBrC.png b/layout/reftests/bugs/456219-1-mask-wBrC.png index b3c63283b22f..9a7bfa1abb53 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wBrC.png and b/layout/reftests/bugs/456219-1-mask-wBrC.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wBrD.png b/layout/reftests/bugs/456219-1-mask-wBrD.png index b9b0bd3b922b..8d9408544e39 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wBrD.png and b/layout/reftests/bugs/456219-1-mask-wBrD.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wBrE.png b/layout/reftests/bugs/456219-1-mask-wBrE.png index d0e45e74accb..024991e81d8a 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wBrE.png and b/layout/reftests/bugs/456219-1-mask-wBrE.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wCrA.png b/layout/reftests/bugs/456219-1-mask-wCrA.png index 864ed8c155ec..6826f1df8aad 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wCrA.png and b/layout/reftests/bugs/456219-1-mask-wCrA.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wCrB.png b/layout/reftests/bugs/456219-1-mask-wCrB.png index 76437651daee..e9ecfe55cc3e 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wCrB.png and b/layout/reftests/bugs/456219-1-mask-wCrB.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wCrC.png b/layout/reftests/bugs/456219-1-mask-wCrC.png index c36864a8c3c7..74189414f710 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wCrC.png and b/layout/reftests/bugs/456219-1-mask-wCrC.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wCrD.png b/layout/reftests/bugs/456219-1-mask-wCrD.png index e7f52fe6eaa0..823d64b1496f 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wCrD.png and b/layout/reftests/bugs/456219-1-mask-wCrD.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wCrE.png b/layout/reftests/bugs/456219-1-mask-wCrE.png index e7f52fe6eaa0..823d64b1496f 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wCrE.png and b/layout/reftests/bugs/456219-1-mask-wCrE.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wDrA.png b/layout/reftests/bugs/456219-1-mask-wDrA.png index 62916f1f8d08..a655ddf3176c 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wDrA.png and b/layout/reftests/bugs/456219-1-mask-wDrA.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wDrB.png b/layout/reftests/bugs/456219-1-mask-wDrB.png index fbaebff90bc6..1ee39e75adcd 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wDrB.png and b/layout/reftests/bugs/456219-1-mask-wDrB.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wDrC.png b/layout/reftests/bugs/456219-1-mask-wDrC.png index e35a2e85ade0..de5005204bd6 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wDrC.png and b/layout/reftests/bugs/456219-1-mask-wDrC.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wDrD.png b/layout/reftests/bugs/456219-1-mask-wDrD.png index 6779712327a6..5377910bfc99 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wDrD.png and b/layout/reftests/bugs/456219-1-mask-wDrD.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wDrE.png b/layout/reftests/bugs/456219-1-mask-wDrE.png index 0277af80bb54..33fefadfd2bf 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wDrE.png and b/layout/reftests/bugs/456219-1-mask-wDrE.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wErA.png b/layout/reftests/bugs/456219-1-mask-wErA.png index d607fc22997a..69b3588499a0 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wErA.png and b/layout/reftests/bugs/456219-1-mask-wErA.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wErB.png b/layout/reftests/bugs/456219-1-mask-wErB.png index eadfbb61ef41..01e356c5d096 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wErB.png and b/layout/reftests/bugs/456219-1-mask-wErB.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wErC.png b/layout/reftests/bugs/456219-1-mask-wErC.png index 42fa1ebdef20..d80a5e6e5229 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wErC.png and b/layout/reftests/bugs/456219-1-mask-wErC.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wErD.png b/layout/reftests/bugs/456219-1-mask-wErD.png index 20a8e9aaa384..44744463d14d 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wErD.png and b/layout/reftests/bugs/456219-1-mask-wErD.png differ diff --git a/layout/reftests/bugs/456219-1-mask-wErE.png b/layout/reftests/bugs/456219-1-mask-wErE.png index cd11e9ddae6d..464d7d1f7bfa 100644 Binary files a/layout/reftests/bugs/456219-1-mask-wErE.png and b/layout/reftests/bugs/456219-1-mask-wErE.png differ diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 0ec55ddd1979..adba342f19c0 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -263,7 +263,7 @@ skip-if(MOZ_WIDGET_TOOLKIT=="cocoa") != 240536-resizer-ltr.xul 240536-resizer-rt == 283686-1.html about:blank == 283686-2.html 283686-2-ref.html == 283686-3.html about:blank -fails-if(MOZ_WIDGET_TOOLKIT!="cocoa") HTTP == 289480.html#top 289480-ref.html # basically-verbatim acid2 test, HTTP for a 404 page -- bug 409329 for the non-Mac failures +fails-if(MOZ_WIDGET_TOOLKIT=="gtk2") HTTP == 289480.html#top 289480-ref.html # basically-verbatim acid2 test, HTTP for a 404 page -- bug 409329 for the non-Mac failures == 290129-1.html 290129-1-ref.html == 291078-1.html 291078-1-ref.html == 291078-2.html 291078-2-ref.html diff --git a/layout/svg/base/src/nsSVGImageFrame.cpp b/layout/svg/base/src/nsSVGImageFrame.cpp index 728f270ad71c..dc3d8a644c1b 100644 --- a/layout/svg/base/src/nsSVGImageFrame.cpp +++ b/layout/svg/base/src/nsSVGImageFrame.cpp @@ -257,6 +257,7 @@ nsSVGImageFrame::PaintSVG(nsSVGRenderState *aContext, if (thebesPattern) { thebesPattern->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this)); + thebesPattern->SetExtend(gfxPattern::EXTEND_PAD_EDGE); gfxContext *gfx = aContext->GetGfxContext();