mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2025-12-30 03:50:13 +01:00
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.
This commit is contained in:
parent
6ad63946d4
commit
37a22669d8
5 changed files with 132 additions and 25 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue