[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