From d3accefd3b9a4db5f07fb1f7bb05fb4238bf36c1 Mon Sep 17 00:00:00 2001 From: Adrian Johnson Date: Sat, 04 Dec 2010 13:28:48 +0000 Subject: 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. --- diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h index 11e026b..dd1c77c 100644 --- a/src/cairo-pdf-surface-private.h +++ b/src/cairo-pdf-surface-private.h @@ -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; diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index e0321a9..39dd395 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -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; -- cgit v0.8.3-6-g21f6