mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-04-23 09:50:48 +02:00
PDF: Output a stencil mask for cairo_mask() with solid source and A1 mask
In https://bugs.launchpad.net/ubuntu/+source/libcairo/+bug/680628 a 65K PDF printed to PDF using poppler-cairo turns into an 8MB PDF. The original PDF contains a very large number of stencil mask images (an A1 image used as a mask for the current color). The PDF surface was not optimized for this particular case. It was drawing a solid color in a group and then using an smask with the image in another group. Fix this by checking for source = solid and mask = A1 image and emitting a stencil mask image.
This commit is contained in:
parent
6732dbf299
commit
d3accefd3b
2 changed files with 144 additions and 17 deletions
|
|
@ -70,6 +70,7 @@ typedef struct _cairo_pdf_source_surface_entry {
|
|||
unsigned char *unique_id;
|
||||
unsigned long unique_id_length;
|
||||
cairo_bool_t interpolate;
|
||||
cairo_bool_t mask;
|
||||
cairo_pdf_resource_t surface_res;
|
||||
int width;
|
||||
int height;
|
||||
|
|
|
|||
|
|
@ -1148,6 +1148,7 @@ static cairo_status_t
|
|||
_cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface,
|
||||
cairo_surface_t *source,
|
||||
cairo_filter_t filter,
|
||||
cairo_bool_t mask,
|
||||
cairo_pdf_resource_t *surface_res,
|
||||
int *width,
|
||||
int *height)
|
||||
|
|
@ -1195,6 +1196,7 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface,
|
|||
|
||||
surface_entry->id = surface_key.id;
|
||||
surface_entry->interpolate = interpolate;
|
||||
surface_entry->mask = mask;
|
||||
cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_UNIQUE_ID,
|
||||
&unique_id, &unique_id_length);
|
||||
if (unique_id && unique_id_length > 0) {
|
||||
|
|
@ -1820,6 +1822,46 @@ _cairo_pdf_surface_supports_fine_grained_fallbacks (void *abstract_surface)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
_cairo_pdf_surface_emit_imagemask (cairo_pdf_surface_t *surface,
|
||||
cairo_image_surface_t *image,
|
||||
cairo_pdf_resource_t *image_res)
|
||||
{
|
||||
cairo_status_t status;
|
||||
uint8_t *byte, output_byte;
|
||||
int row, col, num_cols;
|
||||
|
||||
|
||||
/* This is the only image format supported for stencil masking */
|
||||
assert (image->format == CAIRO_FORMAT_A1);
|
||||
|
||||
status = _cairo_pdf_surface_open_stream (surface,
|
||||
image_res,
|
||||
TRUE,
|
||||
" /Type /XObject\n"
|
||||
" /Subtype /Image\n"
|
||||
" /ImageMask true\n"
|
||||
" /Width %d\n"
|
||||
" /Height %d\n"
|
||||
" /BitsPerComponent 1\n",
|
||||
image->width, image->height);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
num_cols = (image->width + 7) / 8;
|
||||
for (row = 0; row < image->height; row++) {
|
||||
byte = image->data + row * image->stride;
|
||||
for (col = 0; col < num_cols; col++) {
|
||||
output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte);
|
||||
output_byte = ~output_byte;
|
||||
_cairo_output_stream_write (surface->output, &output_byte, 1);
|
||||
byte++;
|
||||
}
|
||||
}
|
||||
|
||||
return _cairo_pdf_surface_close_stream (surface);
|
||||
}
|
||||
|
||||
/* Emit alpha channel from the image into the given data, providing
|
||||
* an id that can be used to reference the resulting SMask object.
|
||||
*
|
||||
|
|
@ -1928,7 +1970,8 @@ static cairo_status_t
|
|||
_cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface,
|
||||
cairo_image_surface_t *image,
|
||||
cairo_pdf_resource_t *image_res,
|
||||
cairo_filter_t filter)
|
||||
cairo_filter_t filter,
|
||||
cairo_bool_t mask)
|
||||
{
|
||||
cairo_status_t status = CAIRO_STATUS_SUCCESS;
|
||||
char *rgb;
|
||||
|
|
@ -1949,6 +1992,9 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface,
|
|||
image->format == CAIRO_FORMAT_A8 ||
|
||||
image->format == CAIRO_FORMAT_A1);
|
||||
|
||||
if (mask)
|
||||
return _cairo_pdf_surface_emit_imagemask (surface, image, image_res);
|
||||
|
||||
rgb_size = image->height * image->width * 3;
|
||||
rgb = _cairo_malloc_abc (image->width, image->height, 3);
|
||||
if (unlikely (rgb == NULL)) {
|
||||
|
|
@ -2145,7 +2191,8 @@ static cairo_status_t
|
|||
_cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface,
|
||||
cairo_surface_t *source,
|
||||
cairo_pdf_resource_t resource,
|
||||
cairo_bool_t interpolate)
|
||||
cairo_bool_t interpolate,
|
||||
cairo_bool_t mask)
|
||||
{
|
||||
cairo_image_surface_t *image;
|
||||
void *image_extra;
|
||||
|
|
@ -2164,7 +2211,7 @@ _cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface,
|
|||
return status;
|
||||
|
||||
status = _cairo_pdf_surface_emit_image (surface, image,
|
||||
&resource, interpolate);
|
||||
&resource, interpolate, mask);
|
||||
if (unlikely (status))
|
||||
goto BAIL;
|
||||
|
||||
|
|
@ -2255,7 +2302,7 @@ _cairo_pdf_surface_emit_padded_image_surface (cairo_pdf_surface_t *surface,
|
|||
}
|
||||
|
||||
status = _cairo_pdf_surface_emit_image (surface, (cairo_image_surface_t *)pad_image,
|
||||
resource, interpolate);
|
||||
resource, interpolate, FALSE);
|
||||
if (unlikely (status))
|
||||
goto BAIL;
|
||||
|
||||
|
|
@ -2414,7 +2461,8 @@ _cairo_pdf_surface_emit_surface (cairo_pdf_surface_t *surface,
|
|||
return _cairo_pdf_surface_emit_image_surface (surface,
|
||||
src_surface->surface,
|
||||
src_surface->hash_entry->surface_res,
|
||||
src_surface->hash_entry->interpolate);
|
||||
src_surface->hash_entry->interpolate,
|
||||
src_surface->hash_entry->mask);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2451,6 +2499,7 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface,
|
|||
status = _cairo_pdf_surface_add_source_surface (surface,
|
||||
pattern->surface,
|
||||
pdf_pattern->pattern->filter,
|
||||
FALSE,
|
||||
&pattern_resource,
|
||||
&pattern_width,
|
||||
&pattern_height);
|
||||
|
|
@ -3412,7 +3461,8 @@ _cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern
|
|||
|
||||
static cairo_status_t
|
||||
_cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface,
|
||||
cairo_surface_pattern_t *source)
|
||||
cairo_surface_pattern_t *source,
|
||||
cairo_bool_t mask)
|
||||
{
|
||||
cairo_pdf_resource_t surface_res;
|
||||
int width, height;
|
||||
|
|
@ -3423,6 +3473,7 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface,
|
|||
status = _cairo_pdf_surface_add_source_surface (surface,
|
||||
source->surface,
|
||||
source->base.filter,
|
||||
mask,
|
||||
&surface_res,
|
||||
&width,
|
||||
&height);
|
||||
|
|
@ -3457,10 +3508,16 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface,
|
|||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
_cairo_output_stream_printf (surface->output,
|
||||
"/a%d gs /x%d Do\n",
|
||||
alpha,
|
||||
surface_res.id);
|
||||
if (mask) {
|
||||
_cairo_output_stream_printf (surface->output,
|
||||
"/x%d Do\n",
|
||||
surface_res.id);
|
||||
} else {
|
||||
_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);
|
||||
}
|
||||
|
|
@ -5563,6 +5620,68 @@ _cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface)
|
|||
return _cairo_pdf_surface_open_content_stream (surface, NULL, TRUE);
|
||||
}
|
||||
|
||||
/* A PDF stencil mask is an A1 mask used with the current color */
|
||||
static cairo_int_status_t
|
||||
_cairo_pdf_surface_emit_stencil_mask (cairo_pdf_surface_t *surface,
|
||||
const cairo_pattern_t *source,
|
||||
const cairo_pattern_t *mask)
|
||||
{
|
||||
cairo_status_t status;
|
||||
cairo_surface_pattern_t *surface_pattern;
|
||||
cairo_image_surface_t *image;
|
||||
void *image_extra;
|
||||
cairo_pdf_resource_t pattern_res = {0};
|
||||
|
||||
if (! (source->type == CAIRO_PATTERN_TYPE_SOLID &&
|
||||
mask->type == CAIRO_PATTERN_TYPE_SURFACE))
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
|
||||
surface_pattern = (cairo_surface_pattern_t *) mask;
|
||||
if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
|
||||
status = _cairo_surface_acquire_source_image (surface_pattern->surface,
|
||||
&image,
|
||||
&image_extra);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
if (image->base.status)
|
||||
return image->base.status;
|
||||
|
||||
if (image->format != CAIRO_FORMAT_A1) {
|
||||
status = CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
status = _cairo_pdf_surface_select_pattern (surface, source,
|
||||
pattern_res, FALSE);
|
||||
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\n");
|
||||
status = _cairo_pdf_surface_paint_surface_pattern (surface,
|
||||
(cairo_surface_pattern_t *) surface_pattern,
|
||||
TRUE);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
_cairo_output_stream_printf (surface->output, "Q\n");
|
||||
|
||||
_cairo_surface_release_source_image (surface_pattern->surface, image, image_extra);
|
||||
status = _cairo_output_stream_get_status (surface->output);
|
||||
|
||||
cleanup:
|
||||
_cairo_surface_release_source_image (surface_pattern->surface, image, image_extra);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static cairo_int_status_t
|
||||
_cairo_pdf_surface_paint (void *abstract_surface,
|
||||
cairo_operator_t op,
|
||||
|
|
@ -5612,7 +5731,8 @@ _cairo_pdf_surface_paint (void *abstract_surface,
|
|||
{
|
||||
_cairo_output_stream_printf (surface->output, "q\n");
|
||||
status = _cairo_pdf_surface_paint_surface_pattern (surface,
|
||||
(cairo_surface_pattern_t *) source);
|
||||
(cairo_surface_pattern_t *) source,
|
||||
FALSE);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
|
|
@ -5679,7 +5799,7 @@ _cairo_pdf_surface_paint (void *abstract_surface,
|
|||
}
|
||||
|
||||
static cairo_int_status_t
|
||||
_cairo_pdf_surface_mask (void *abstract_surface,
|
||||
_cairo_pdf_surface_mask (void *abstract_surface,
|
||||
cairo_operator_t op,
|
||||
const cairo_pattern_t *source,
|
||||
const cairo_pattern_t *mask,
|
||||
|
|
@ -5730,6 +5850,15 @@ _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;
|
||||
|
||||
/* Check if we can use a stencil mask */
|
||||
status = _cairo_pdf_surface_emit_stencil_mask (surface, source, mask);
|
||||
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
|
||||
return status;
|
||||
|
||||
group = _cairo_pdf_surface_create_smask_group (surface);
|
||||
if (unlikely (group == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
|
@ -5769,10 +5898,6 @@ _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,
|
||||
|
|
@ -5984,7 +6109,8 @@ _cairo_pdf_surface_fill (void *abstract_surface,
|
|||
return status;
|
||||
|
||||
status = _cairo_pdf_surface_paint_surface_pattern (surface,
|
||||
(cairo_surface_pattern_t *) source);
|
||||
(cairo_surface_pattern_t *) source,
|
||||
FALSE);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue