[cairo-commit] src/cairo-pdf-surface.c src/cairo-pdf-surface-private.h
Adrian Johnson
ajohnson at kemper.freedesktop.org
Sat Dec 4 06:02:49 PST 2010
src/cairo-pdf-surface-private.h | 1
src/cairo-pdf-surface.c | 160 +++++++++++++++++++++++++++++++++++-----
2 files changed, 144 insertions(+), 17 deletions(-)
New commits:
commit d3accefd3b9a4db5f07fb1f7bb05fb4238bf36c1
Author: Adrian Johnson <ajohnson at redneon.com>
Date: Sat Dec 4 23:58:48 2010 +1030
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;
More information about the cairo-commit
mailing list