From 665f5829541703b29e33f80bebf579a17f47a892 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 1 Sep 2009 18:24:42 +0100 Subject: [ps/pdf] Trim patterns to operation extents If we have to rasterise a pattern for use by PS/PDF (for example, to satisfy CAIRO_EXTENT_PAD) then only generate an image large enough to cover the operation extents. We ensure tight coverage by computing the extents afresh - we could do this lazily in the future, but we can not rely on the bounds as computed by the analysis surface as for native operations they may not be tight. --- src/cairo-pdf-surface.c | 67 +++++++++++++++++--- src/cairo-ps-surface.c | 76 +++++++++++++++-------- src/cairo-surface.c | 162 ++++++++++++++++++++++++++++++++++++++++++++++++ src/cairoint.h | 49 +++++++++++++++ 4 files changed, 319 insertions(+), 35 deletions(-) diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index 76016872..ac78f589 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -1162,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_clip_t *clip, + const cairo_rectangle_int_t *extents, cairo_pdf_resource_t *pattern_res, cairo_pdf_resource_t *gstate_res) { @@ -1221,8 +1221,8 @@ _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, pdf_pattern.width = surface->width; pdf_pattern.height = surface->height; - if (clip != NULL) { - pdf_pattern.extents = clip->path->extents; + if (extents != NULL) { + pdf_pattern.extents = *extents; } else { pdf_pattern.extents.x = 0; pdf_pattern.extents.y = 0; @@ -5315,6 +5315,7 @@ _cairo_pdf_surface_paint (void *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); @@ -5347,9 +5348,15 @@ _cairo_pdf_surface_paint (void *abstract_surface, 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, clip, + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, &extents, &pattern_res, &gstate_res); if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) return CAIRO_STATUS_SUCCESS; @@ -5512,6 +5519,7 @@ _cairo_pdf_surface_stroke (void *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); @@ -5522,9 +5530,17 @@ _cairo_pdf_surface_stroke (void *abstract_surface, 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, clip, + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, &extents, &pattern_res, &gstate_res); if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) return CAIRO_STATUS_SUCCESS; @@ -5613,6 +5629,7 @@ _cairo_pdf_surface_fill (void *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); @@ -5651,9 +5668,16 @@ _cairo_pdf_surface_fill (void *abstract_surface, 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, clip, + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, &extents, &pattern_res, &gstate_res); if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) return CAIRO_STATUS_SUCCESS; @@ -5740,6 +5764,7 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, 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 @@ -5771,10 +5796,18 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, 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, - clip, + &extents, &fill_pattern_res, &gstate_res); if (unlikely (status)) @@ -5782,11 +5815,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, - clip, + &extents, &stroke_pattern_res, &gstate_res); if (unlikely (status)) @@ -5846,6 +5887,7 @@ _cairo_pdf_surface_show_text_glyphs (void *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); @@ -5856,9 +5898,16 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, 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, clip, + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, &extents, &pattern_res, &gstate_res); if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) return CAIRO_STATUS_SUCCESS; diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index 6f1bcfcf..49d962ba 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -2277,12 +2277,14 @@ _cairo_ps_surface_emit_meta_surface (cairo_ps_surface_t *surface, double old_width, old_height; cairo_matrix_t old_cairo_to_ps; cairo_content_t old_content; + cairo_rectangle_int_t old_page_bbox; cairo_box_t bbox; cairo_status_t 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; status = @@ -2292,9 +2294,18 @@ _cairo_ps_surface_emit_meta_surface (cairo_ps_surface_t *surface, if (unlikely (status)) return status; - /* XXX is this still necessary? */ +#if DEBUG_PS + _cairo_output_stream_printf (surface->stream, + "%% _cairo_ps_surface_emit_meta_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); @@ -2325,6 +2336,7 @@ _cairo_ps_surface_emit_meta_surface (cairo_ps_surface_t *surface, 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; @@ -3235,21 +3247,6 @@ _cairo_ps_surface_get_font_options (void *abstract_surface, cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); } -static cairo_bool_t -_rectangle_intersect_clip (cairo_rectangle_int_t *extents, cairo_clip_t *clip) -{ - const cairo_rectangle_int_t *clip_extents; - - clip_extents = NULL; - if (clip != NULL) - clip_extents = _cairo_clip_get_extents (clip); - - if (clip_extents != NULL) - return _cairo_rectangle_intersect (extents, clip_extents); - - return TRUE; -} - static cairo_int_status_t _cairo_ps_surface_paint (void *abstract_surface, cairo_operator_t op, @@ -3271,8 +3268,13 @@ _cairo_ps_surface_paint (void *abstract_surface, "%% _cairo_ps_surface_paint\n"); #endif - extents = surface->page_bbox; - if (! _rectangle_intersect_clip (&extents, clip)) + status = _cairo_surface_paint_extents (&surface->base, + op, source, 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); @@ -3333,8 +3335,16 @@ _cairo_ps_surface_stroke (void *abstract_surface, "%% _cairo_ps_surface_stroke\n"); #endif - extents = surface->page_bbox; - if (! _rectangle_intersect_clip (&extents, clip)) + 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); @@ -3379,14 +3389,21 @@ _cairo_ps_surface_fill (void *abstract_surface, "%% _cairo_ps_surface_fill\n"); #endif - extents = surface->page_bbox; - if (! _rectangle_intersect_clip (&extents, clip)) - return CAIRO_STATUS_SUCCESS; - 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; @@ -3454,8 +3471,15 @@ _cairo_ps_surface_show_glyphs (void *abstract_surface, if (num_glyphs <= 0) return CAIRO_STATUS_SUCCESS; - extents = surface->page_bbox; - if (! _rectangle_intersect_clip (&extents, clip)) + 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); diff --git a/src/cairo-surface.c b/src/cairo-surface.c index 46abf25c..6dab8e22 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -2841,6 +2841,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) { diff --git a/src/cairoint.h b/src/cairoint.h index d0e34095..f6a45596 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -1949,6 +1949,55 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, cairo_scaled_font_t *scaled_font, 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, const cairo_pattern_t *pattern, -- cgit v1.2.3