From 37a22669d80cf970dab3aa3e7a8ec673c1462342 Mon Sep 17 00:00:00 2001 From: Adrian Johnson Date: Sun, 4 Sep 2011 20:43:07 +0930 Subject: [PATCH] pdf: use ink extents for smask bbox There are some inkscape bugs reporting very slow rendering of inkscape generated PDFs (inkscape uses cairo for PDF output). These bugs are caused by cairo specifying a page sized bounding box in XObjects and Patterns. PDF renderers usually use the BBox as the image size when compositing. As PDFs generated from SVG tends to use a lot of XObjects and Patterns this can lead to very long rendering times. These three patches tighten up all the BBoxes in PDF output. --- src/cairo-pattern.c | 44 ++++++++++++++ src/cairo-pdf-surface-private.h | 2 + src/cairo-pdf-surface.c | 102 ++++++++++++++++++++++++-------- src/cairo-types-private.h | 5 ++ src/cairoint.h | 4 ++ 5 files changed, 132 insertions(+), 25 deletions(-) diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c index 343845414..19aa97830 100644 --- a/src/cairo-pattern.c +++ b/src/cairo-pattern.c @@ -4376,6 +4376,50 @@ _cairo_pattern_get_extents (const cairo_pattern_t *pattern, return; } +/** + * _cairo_pattern_get_ink_extents: + * + * Return the "target-space" inked extents of @pattern in @extents. + **/ +void +_cairo_pattern_get_ink_extents (const cairo_pattern_t *pattern, + cairo_rectangle_int_t *extents) +{ + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE && + pattern->extend == CAIRO_EXTEND_NONE) + { + const cairo_surface_pattern_t *surface_pattern = + (const cairo_surface_pattern_t *) pattern; + cairo_surface_t *surface = surface_pattern->surface; + + if (surface->type == CAIRO_SURFACE_TYPE_RECORDING) { + cairo_matrix_t imatrix; + double x1, y1, x2, y2, width, height; + cairo_status_t status; + + cairo_recording_surface_ink_extents (surface, &x1, &y1, &width, &height); + imatrix = pattern->matrix; + status = cairo_matrix_invert (&imatrix); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + x2 = x1 + width; + y2 = y1 + height; + _cairo_matrix_transform_bounding_box (&imatrix, + &x1, &y1, &x2, &y2, + NULL); + x1 = floor (x1); + y1 = floor (y1); + x2 = ceil (x2); + y2 = ceil (y2); + extents->x = x1; extents->width = x2 - x1; + extents->y = y1; extents->height = y2 - y1; + return; + } + } + + return _cairo_pattern_get_extents (pattern, extents); +} static unsigned long _cairo_solid_pattern_hash (unsigned long hash, diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h index dd1c77cb8..64bf7f0fc 100644 --- a/src/cairo-pdf-surface-private.h +++ b/src/cairo-pdf-surface-private.h @@ -101,6 +101,7 @@ typedef enum _cairo_pdf_operation { typedef struct _cairo_pdf_smask_group { double width; double height; + cairo_rectangle_int_t extents; cairo_pdf_resource_t group_res; cairo_pdf_operation_t operation; cairo_pattern_t *source; @@ -174,6 +175,7 @@ struct _cairo_pdf_surface { cairo_output_stream_t *mem_stream; cairo_output_stream_t *old_output; cairo_pdf_resource_t resource; + cairo_box_double_t bbox; cairo_bool_t is_knockout; } group_stream; diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index 9f15aee92..b4d48a49b 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -992,7 +992,8 @@ _cairo_pdf_surface_emit_group_resources (cairo_pdf_surface_t *surface, } static cairo_pdf_smask_group_t * -_cairo_pdf_surface_create_smask_group (cairo_pdf_surface_t *surface) +_cairo_pdf_surface_create_smask_group (cairo_pdf_surface_t *surface, + const cairo_rectangle_int_t *extents) { cairo_pdf_smask_group_t *group; @@ -1010,6 +1011,15 @@ _cairo_pdf_surface_create_smask_group (cairo_pdf_surface_t *surface) } group->width = surface->width; group->height = surface->height; + if (extents != NULL) { + group->extents = *extents; + } else { + group->extents.x = 0; + group->extents.y = 0; + group->extents.width = surface->width; + group->extents.height = surface->height; + } + group->extents = *extents; return group; } @@ -1322,6 +1332,18 @@ _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, return CAIRO_STATUS_SUCCESS; } +/* Get BBox in PDF coordinates from extents in cairo coordinates */ +static void +_get_bbox_from_extents (double surface_height, + const cairo_rectangle_int_t *extents, + cairo_box_double_t *bbox) +{ + bbox->p1.x = extents->x; + bbox->p1.y = surface_height - (extents->y + extents->height); + bbox->p2.x = extents->x + extents->width; + bbox->p2.y = surface_height - extents->y; +} + static cairo_status_t _cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface, cairo_pdf_resource_t *resource, @@ -1443,7 +1465,8 @@ _cairo_pdf_surface_write_memory_stream (cairo_pdf_surface_t *surface, cairo_output_stream_t *mem_stream, cairo_pdf_resource_t resource, cairo_pdf_group_resources_t *resources, - cairo_bool_t is_knockout_group) + cairo_bool_t is_knockout_group, + const cairo_box_double_t *bbox) { _cairo_pdf_surface_update_object (surface, resource); @@ -1461,13 +1484,12 @@ _cairo_pdf_surface_write_memory_stream (cairo_pdf_surface_t *surface, _cairo_output_stream_printf (surface->output, " /Subtype /Form\n" - " /BBox [ 0 0 %f %f ]\n" + " /BBox [ %f %f %f %f ]\n" " /Group <<\n" " /Type /Group\n" " /S /Transparency\n" " /CS /DeviceRGB\n", - surface->width, - surface->height); + bbox->p1.x, bbox->p1.y, bbox->p2.x, bbox->p2.y); if (is_knockout_group) _cairo_output_stream_printf (surface->output, @@ -1487,8 +1509,9 @@ _cairo_pdf_surface_write_memory_stream (cairo_pdf_surface_t *surface, } static cairo_status_t -_cairo_pdf_surface_open_group (cairo_pdf_surface_t *surface, - cairo_pdf_resource_t *resource) +_cairo_pdf_surface_open_group (cairo_pdf_surface_t *surface, + const cairo_box_double_t *bbox, + cairo_pdf_resource_t *resource) { cairo_status_t status; @@ -1523,16 +1546,18 @@ _cairo_pdf_surface_open_group (cairo_pdf_surface_t *surface, return _cairo_error (CAIRO_STATUS_NO_MEMORY); } surface->group_stream.is_knockout = FALSE; + surface->group_stream.bbox = *bbox; return status; } static cairo_status_t -_cairo_pdf_surface_open_knockout_group (cairo_pdf_surface_t *surface) +_cairo_pdf_surface_open_knockout_group (cairo_pdf_surface_t *surface, + const cairo_box_double_t *bbox) { cairo_status_t status; - status = _cairo_pdf_surface_open_group (surface, NULL); + status = _cairo_pdf_surface_open_group (surface, bbox, NULL); if (unlikely (status)) return status; @@ -1568,7 +1593,8 @@ _cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface, surface->group_stream.mem_stream, surface->group_stream.resource, &surface->resources, - surface->group_stream.is_knockout); + surface->group_stream.is_knockout, + &surface->group_stream.bbox); if (group) *group = surface->group_stream.resource; @@ -5182,9 +5208,11 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, cairo_pdf_smask_group_t *smask_group; cairo_pdf_resource_t pattern_res, gstate_res; cairo_status_t status; + cairo_box_double_t bbox; /* Create mask group */ - status = _cairo_pdf_surface_open_group (surface, NULL); + _get_bbox_from_extents (group->height, &group->extents, &bbox); + status = _cairo_pdf_surface_open_group (surface, &bbox, NULL); if (unlikely (status)) return status; @@ -5196,10 +5224,12 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, return status; if (gstate_res.id != 0) { - smask_group = _cairo_pdf_surface_create_smask_group (surface); + smask_group = _cairo_pdf_surface_create_smask_group (surface, &group->extents); if (unlikely (smask_group == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); + smask_group->width = group->width; + smask_group->height = group->height; smask_group->operation = PDF_PAINT; smask_group->source = cairo_pattern_reference (group->mask); smask_group->source_res = pattern_res; @@ -5227,8 +5257,11 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, return status; _cairo_output_stream_printf (surface->output, - "0 0 %f %f re f\n", - surface->width, surface->height); + "%f %f %f %f re f\n", + bbox.p1.x, + bbox.p1.y, + bbox.p2.x - bbox.p1.x, + bbox.p2.y - bbox.p1.y); status = _cairo_pdf_surface_unselect_pattern (surface); if (unlikely (status)) @@ -5240,7 +5273,7 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, return status; /* Create source group */ - status = _cairo_pdf_surface_open_group (surface, &group->source_res); + status = _cairo_pdf_surface_open_group (surface, &bbox, &group->source_res); if (unlikely (status)) return status; @@ -5252,7 +5285,7 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, return status; if (gstate_res.id != 0) { - smask_group = _cairo_pdf_surface_create_smask_group (surface); + smask_group = _cairo_pdf_surface_create_smask_group (surface, &group->extents); if (unlikely (smask_group == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -5283,8 +5316,11 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, return status; _cairo_output_stream_printf (surface->output, - "0 0 %f %f re f\n", - surface->width, surface->height); + "%f %f %f %f re f\n", + bbox.p1.x, + bbox.p1.y, + bbox.p2.x - bbox.p1.x, + bbox.p2.y - bbox.p1.y); status = _cairo_pdf_surface_unselect_pattern (surface); if (unlikely (status)) @@ -5333,6 +5369,7 @@ _cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t *surface, { double old_width, old_height; cairo_status_t status; + cairo_box_double_t bbox; old_width = surface->width; old_height = surface->height; @@ -5346,7 +5383,8 @@ _cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t *surface, goto RESTORE_SIZE; } - status = _cairo_pdf_surface_open_group (surface, &group->group_res); + _get_bbox_from_extents (group->height, &group->extents, &bbox); + status = _cairo_pdf_surface_open_group (surface, &bbox, &group->group_res); if (unlikely (status)) return status; @@ -5461,7 +5499,14 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) _cairo_pdf_group_resources_clear (&surface->resources); if (surface->has_fallback_images) { - status = _cairo_pdf_surface_open_knockout_group (surface); + cairo_rectangle_int_t extents; + + extents.x = 0; + extents.y = 0; + extents.width = ceil (surface->width); + extents.height = ceil (surface->height); + _get_bbox_from_extents (surface->height, &extents, &bbox); + status = _cairo_pdf_surface_open_knockout_group (surface, &bbox); if (unlikely (status)) return status; @@ -5899,7 +5944,7 @@ _cairo_pdf_surface_paint (void *abstract_surface, goto cleanup; if (gstate_res.id != 0) { - group = _cairo_pdf_surface_create_smask_group (surface); + group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded); if (unlikely (group == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto cleanup; @@ -6001,6 +6046,13 @@ _cairo_pdf_surface_mask (void *abstract_surface, assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); assert (_cairo_pdf_surface_operation_supported (surface, op, mask, &extents.bounded)); + /* get the accurate extents */ + _cairo_pattern_get_ink_extents (source, &extents.source); + _cairo_pattern_get_ink_extents (mask, &extents.mask); + extents.bounded = extents.source; + if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) + return CAIRO_STATUS_SUCCESS; + status = _cairo_pdf_surface_set_clip (surface, &extents); if (unlikely (status)) goto cleanup; @@ -6014,7 +6066,7 @@ _cairo_pdf_surface_mask (void *abstract_surface, if (status != CAIRO_INT_STATUS_UNSUPPORTED) goto cleanup; - group = _cairo_pdf_surface_create_smask_group (surface); + group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded); if (unlikely (group == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto cleanup; @@ -6139,7 +6191,7 @@ _cairo_pdf_surface_stroke (void *abstract_surface, goto cleanup; if (gstate_res.id != 0) { - group = _cairo_pdf_surface_create_smask_group (surface); + group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded); if (unlikely (group == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto cleanup; @@ -6303,7 +6355,7 @@ _cairo_pdf_surface_fill (void *abstract_surface, goto cleanup; if (gstate_res.id != 0) { - group = _cairo_pdf_surface_create_smask_group (surface); + group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded); if (unlikely (group == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto cleanup; @@ -6587,7 +6639,7 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, goto cleanup; if (gstate_res.id != 0) { - group = _cairo_pdf_surface_create_smask_group (surface); + group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded); if (unlikely (group == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto cleanup; diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h index 966cd8fb8..f18a20ea3 100644 --- a/src/cairo-types-private.h +++ b/src/cairo-types-private.h @@ -271,6 +271,11 @@ typedef struct _cairo_distance_double { double dy; } cairo_distance_double_t; +typedef struct _cairo_box_double { + cairo_point_double_t p1; + cairo_point_double_t p2; +} cairo_box_double_t; + typedef struct _cairo_line { cairo_point_t p1; cairo_point_t p2; diff --git a/src/cairoint.h b/src/cairoint.h index df2071bd1..5ae8b7a5c 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -407,6 +407,10 @@ _cairo_user_data_array_foreach (cairo_user_data_array_t *array, cairo_private unsigned long _cairo_hash_string (const char *c); +cairo_private void +_cairo_pattern_get_ink_extents (const cairo_pattern_t *pattern, + cairo_rectangle_int_t *extents); + cairo_private unsigned long _cairo_hash_bytes (unsigned long hash, const void *bytes,