[cairo-commit] src/cairo-pdf-surface.c src/cairo-pdf-surface-private.h

Adrian Johnson ajohnson at kemper.freedesktop.org
Sat Sep 28 20:40:36 PDT 2013


 src/cairo-pdf-surface-private.h |    2 
 src/cairo-pdf-surface.c         |  466 +++++++++++++++++++++++++++++-----------
 2 files changed, 347 insertions(+), 121 deletions(-)

New commits:
commit 07940ecd6055e29addcdb4c14e4449a06a30bf4a
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sun Sep 29 12:42:59 2013 +0930

    pdf: combine source and mask images into single image
    
    Since PDF stores the alpha component of images in a separate stream
    which can have a different resolution and encoding to the color
    components we can optimize the case where the source and mask are both images.
    
    If the source and mask are both images with the same extents and the
    source is opaque, combine the images into a single PDF image (ie the
    source is written the the image stream and the mask is written to the
    smask stream).

diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h
index 0032947..618ca4e 100644
--- a/src/cairo-pdf-surface-private.h
+++ b/src/cairo-pdf-surface-private.h
@@ -73,7 +73,9 @@ typedef struct _cairo_pdf_source_surface_entry {
     cairo_operator_t operator;
     cairo_bool_t interpolate;
     cairo_bool_t stencil_mask;
+    cairo_bool_t smask;
     cairo_pdf_resource_t surface_res;
+    cairo_pdf_resource_t smask_res;
     int width;
     int height;
     cairo_rectangle_int_t extents;
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index 4c9dd5a..1d6b8ec 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -1329,7 +1329,9 @@ _get_source_surface_size (cairo_surface_t         *source,
  * @op: the operator used to composite this source
  * @filter: filter type of the source pattern
  * @stencil_mask: if true, the surface will be written to the PDF as an /ImageMask
+ * @smask: if true, only the alpha channel will be written (images only)
  * @extents: extents of the operation that is using this source
+ * @smask_res: if not NULL, the image written will specify this resource as the smask for the image (images only)
  * @surface_res: return PDF resource number of the surface
  * @width: returns width of surface
  * @height: returns height of surface
@@ -1354,7 +1356,9 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t	    *surface,
 				       cairo_operator_t              op,
 				       cairo_filter_t		     filter,
 				       cairo_bool_t                  stencil_mask,
+				       cairo_bool_t                  smask,
 				       const cairo_rectangle_int_t  *extents,
+				       cairo_pdf_resource_t	    *smask_res,
 				       cairo_pdf_resource_t	    *surface_res,
 				       int                          *width,
 				       int                          *height,
@@ -1455,11 +1459,16 @@ release_source:
     surface_entry->operator = op;
     surface_entry->interpolate = interpolate;
     surface_entry->stencil_mask = stencil_mask;
+    surface_entry->smask = smask;
     surface_entry->unique_id_length = unique_id_length;
     surface_entry->unique_id = unique_id;
     surface_entry->width = *width;
     surface_entry->height = *height;
     surface_entry->extents = *source_extents;
+    if (smask_res)
+	surface_entry->smask_res = *smask_res;
+    else
+	surface_entry->smask_res.id = 0;
     _cairo_pdf_source_surface_init_key (surface_entry);
 
     src_surface.hash_entry = surface_entry;
@@ -2240,7 +2249,9 @@ _cairo_pdf_surface_add_padded_image_surface (cairo_pdf_surface_t          *surfa
 						    FALSE,
 						    source->filter,
 						    FALSE,
+						    FALSE,
 						    extents,
+						    NULL,
 						    surface_res,
 						    width,
 						    height,
@@ -2271,21 +2282,13 @@ BAIL:
     return status;
 }
 
-/* Emit alpha channel from the image into the given data, providing
- * an id that can be used to reference the resulting SMask object.
- *
- * In the case that the alpha channel happens to be all opaque, then
- * no SMask object will be emitted and *id_ret will be set to 0.
- *
- * When stencil_mask is TRUE, stream_res is an an input specifying the
- * resource to use. When stencil_mask is FALSE, a new resource will be
- * created and returned in stream_res.
+/* Emit alpha channel from the image into stream_res.
  */
 static cairo_int_status_t
 _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t	*surface,
 			       cairo_image_surface_t	*image,
 			       cairo_bool_t              stencil_mask,
-			       const char               *interpolate,
+			       cairo_bool_t              interpolate,
 			       cairo_pdf_resource_t	*stream_res)
 {
     cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
@@ -2377,11 +2380,11 @@ _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t	*surface,
 						 "   /Interpolate %s\n"
 						 "   /BitsPerComponent 1\n"
 						 "   /Decode [1 0]\n",
-						 image->width, image->height, interpolate);
+						 image->width, image->height,
+						 interpolate ? "true" : "false");
     } else {
-	stream_res->id = 0;
 	status = _cairo_pdf_surface_open_stream (surface,
-						 NULL,
+						 stream_res,
 						 TRUE,
 						 "   /Type /XObject\n"
 						 "   /Subtype /Image\n"
@@ -2390,15 +2393,13 @@ _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t	*surface,
 						 "   /ColorSpace /DeviceGray\n"
 						 "   /Interpolate %s\n"
 						 "   /BitsPerComponent %d\n",
-						 image->width, image->height, interpolate,
+						 image->width, image->height,
+						 interpolate ? "true" : "false",
 						 transparency == CAIRO_IMAGE_HAS_ALPHA ? 8 : 1);
     }
     if (unlikely (status))
 	goto CLEANUP_ALPHA;
 
-    if (!stencil_mask)
-	*stream_res = surface->pdf_stream.self;
-
     _cairo_output_stream_write (surface->output, alpha, alpha_size);
     status = _cairo_pdf_surface_close_stream (surface);
 
@@ -2408,14 +2409,21 @@ _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t	*surface,
     return status;
 }
 
-/* Emit image data into the given surface, providing a resource that
- * can be used to reference the data in image_ret. */
+/**
+ * _cairo_pdf_surface_emit_image:
+ * @surface: the pdf surface
+ * @image_surf: The image to write
+ * @surface_entry: Contains image resource, smask resource, interpolate and stencil mask parameters.
+ *
+ * Emit an image stream using the @image_res resource and write out
+ * the image data from @image_surf. If @smask_res is not null, @smask_res will
+ * be specified as the smask for the image. Otherwise emit the an smask if
+ * the image is requires one.
+ **/
 static cairo_int_status_t
-_cairo_pdf_surface_emit_image (cairo_pdf_surface_t     *surface,
-                               cairo_image_surface_t   *image_surf,
-                               cairo_pdf_resource_t    *image_res,
-			       cairo_filter_t           filter,
-			       cairo_bool_t             stencil_mask)
+_cairo_pdf_surface_emit_image (cairo_pdf_surface_t              *surface,
+                               cairo_image_surface_t            *image_surf,
+			       cairo_pdf_source_surface_entry_t *surface_entry)
 {
     cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
     char *data;
@@ -2424,9 +2432,10 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t     *surface,
     int i, x, y, bit;
     cairo_pdf_resource_t smask = {0}; /* squelch bogus compiler warning */
     cairo_bool_t need_smask;
-    const char *interpolate = "true";
     cairo_image_color_t color;
     cairo_image_surface_t *image;
+    cairo_image_transparency_t transparency;
+    char smask_buf[30];
 
     image  = image_surf;
     if (image->format != CAIRO_FORMAT_RGB24 &&
@@ -2455,22 +2464,13 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t     *surface,
             goto CLEANUP;
     }
 
-    switch (filter) {
-    case CAIRO_FILTER_GOOD:
-    case CAIRO_FILTER_BEST:
-    case CAIRO_FILTER_BILINEAR:
-	interpolate = "true";
-	break;
-    case CAIRO_FILTER_FAST:
-    case CAIRO_FILTER_NEAREST:
-    case CAIRO_FILTER_GAUSSIAN:
-	interpolate = "false";
-	break;
+    if (surface_entry->stencil_mask) {
+	return _cairo_pdf_surface_emit_smask (surface, image,
+					      surface_entry->stencil_mask,
+					      surface_entry->interpolate,
+					      &surface_entry->surface_res);
     }
 
-    if (stencil_mask)
-	return _cairo_pdf_surface_emit_smask (surface, image, stencil_mask, interpolate, image_res);
-
     color = _cairo_image_analyze_color (image);
     switch (color) {
         default:
@@ -2555,46 +2555,53 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t     *surface,
 	    i++;
     }
 
-    need_smask = FALSE;
-    if (image->format == CAIRO_FORMAT_ARGB32 ||
-	image->format == CAIRO_FORMAT_A8 ||
-	image->format == CAIRO_FORMAT_A1) {
-	status = _cairo_pdf_surface_emit_smask (surface, image, FALSE, interpolate, &smask);
-	if (unlikely (status))
-	    goto CLEANUP_RGB;
+    if (surface_entry->smask_res.id != 0) {
+	need_smask = TRUE;
+	smask = surface_entry->smask_res;
+    } else {
+	need_smask = FALSE;
+	if (image->format == CAIRO_FORMAT_ARGB32 ||
+	    image->format == CAIRO_FORMAT_A8 ||
+	    image->format == CAIRO_FORMAT_A1)
+	{
+	    transparency = _cairo_image_analyze_transparency (image);
+	    if (transparency != CAIRO_IMAGE_IS_OPAQUE) {
+		need_smask = TRUE;
+		smask = _cairo_pdf_surface_new_object (surface);
+		if (smask.id == 0) {
+		    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+		    goto CLEANUP_RGB;
+		}
 
-	if (smask.id)
-	    need_smask = TRUE;
+		status = _cairo_pdf_surface_emit_smask (surface, image, FALSE, surface_entry->interpolate, &smask);
+		if (unlikely (status))
+		    goto CLEANUP_RGB;
+	    }
+	}
     }
 
-#define IMAGE_DICTIONARY	"   /Type /XObject\n"		\
-				"   /Subtype /Image\n"	\
-				"   /Width %d\n"		\
-				"   /Height %d\n"		\
-				"   /ColorSpace %s\n"	\
-	                        "   /Interpolate %s\n" \
-				"   /BitsPerComponent %d\n"
-
     if (need_smask)
-	status = _cairo_pdf_surface_open_stream (surface,
-						 image_res,
-						 TRUE,
-						 IMAGE_DICTIONARY
-						 "   /SMask %d 0 R\n",
-						 image->width, image->height,
-						 color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray",
-						 interpolate,
-						 color == CAIRO_IMAGE_IS_MONOCHROME? 1 : 8,
-						 smask.id);
+	snprintf(smask_buf, sizeof(smask_buf), "   /SMask %d 0 R\n", smask.id);
     else
-	status = _cairo_pdf_surface_open_stream (surface,
-						 image_res,
-						 TRUE,
-						 IMAGE_DICTIONARY,
-						 image->width, image->height,
-						 color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray",
-						 interpolate,
-						 color == CAIRO_IMAGE_IS_MONOCHROME? 1 : 8);
+	smask_buf[0] = 0;
+
+    status = _cairo_pdf_surface_open_stream (surface,
+					     &surface_entry->surface_res,
+					     TRUE,
+					     "   /Type /XObject\n"
+					     "   /Subtype /Image\n"
+					     "   /Width %d\n"
+					     "   /Height %d\n"
+					     "   /ColorSpace %s\n"
+					     "   /Interpolate %s\n"
+					     "   /BitsPerComponent %d\n"
+					     "%s",
+					     image->width,
+					     image->height,
+					     color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray",
+					     surface_entry->interpolate ? "true" : "false",
+					     color == CAIRO_IMAGE_IS_MONOCHROME? 1 : 8,
+					     smask_buf);
     if (unlikely (status))
 	goto CLEANUP_RGB;
 
@@ -2649,9 +2656,9 @@ _cairo_pdf_surface_lookup_jbig2_global (cairo_pdf_surface_t       *surface,
 }
 
 static cairo_int_status_t
-_cairo_pdf_surface_emit_jbig2_image (cairo_pdf_surface_t   *surface,
-				     cairo_surface_t	   *source,
-				     cairo_pdf_resource_t   res)
+_cairo_pdf_surface_emit_jbig2_image (cairo_pdf_surface_t              *surface,
+				     cairo_surface_t	              *source,
+				     cairo_pdf_source_surface_entry_t *surface_entry)
 {
     cairo_int_status_t status;
     const unsigned char *mime_data;
@@ -2662,6 +2669,8 @@ _cairo_pdf_surface_emit_jbig2_image (cairo_pdf_surface_t   *surface,
     const unsigned char *global_data;
     unsigned long global_data_length;
     cairo_pdf_jbig2_global_t *global_entry;
+    char smask_buf[30];
+    char decode_parms_buf[100];
 
     cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JBIG2,
 				 &mime_data, &mime_data_length);
@@ -2696,34 +2705,35 @@ _cairo_pdf_surface_emit_jbig2_image (cairo_pdf_surface_t   *surface,
 	    }
 	}
 
-	status = _cairo_pdf_surface_open_stream (surface,
-						 &res,
-						 FALSE,
-						 "   /Type /XObject\n"
-						 "   /Subtype /Image\n"
-						 "   /Width %d\n"
-						 "   /Height %d\n"
-						 "   /ColorSpace /DeviceGray\n"
-						 "   /BitsPerComponent 1\n"
-						 "   /Filter /JBIG2Decode\n"
-						 "   /DecodeParms << /JBIG2Globals %d 0 R >>\n",
-						 info.width,
-						 info.height,
-						 global_entry->res.id);
+	snprintf(decode_parms_buf, sizeof(decode_parms_buf),
+		 "   /DecodeParms << /JBIG2Globals %d 0 R >>\n", global_entry->res.id);
     } else {
-	status = _cairo_pdf_surface_open_stream (surface,
-						 &res,
-						 FALSE,
-						 "   /Type /XObject\n"
-						 "   /Subtype /Image\n"
-						 "   /Width %d\n"
-						 "   /Height %d\n"
-						 "   /ColorSpace /DeviceGray\n"
-						 "   /BitsPerComponent 1\n"
-						 "   /Filter /JBIG2Decode\n",
-						 info.width,
-						 info.height);
+	decode_parms_buf[0] = 0;
     }
+
+    if (surface_entry->smask_res.id)
+	snprintf(smask_buf, sizeof(smask_buf), "   /SMask %d 0 R\n", surface_entry->smask_res.id);
+    else
+	smask_buf[0] = 0;
+
+    status = _cairo_pdf_surface_open_stream (surface,
+					     &surface_entry->surface_res,
+					     FALSE,
+					     "   /Type /XObject\n"
+					     "   /Subtype /Image\n"
+					     "   /Width %d\n"
+					     "   /Height %d\n"
+					     "   /ColorSpace /DeviceGray\n"
+					     "   /BitsPerComponent 1\n"
+					     "   /Interpolate %s\n"
+					     "%s"
+					     "   /Filter /JBIG2Decode\n"
+					     "%s",
+					     info.width,
+					     info.height,
+					     surface_entry->interpolate ? "true" : "false",
+					     smask_buf,
+					     decode_parms_buf);
     if (unlikely(status))
 	return status;
 
@@ -2734,14 +2744,15 @@ _cairo_pdf_surface_emit_jbig2_image (cairo_pdf_surface_t   *surface,
 }
 
 static cairo_int_status_t
-_cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t   *surface,
-				   cairo_surface_t	 *source,
-				   cairo_pdf_resource_t   res)
+_cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t              *surface,
+				   cairo_surface_t	            *source,
+				   cairo_pdf_source_surface_entry_t *surface_entry)
 {
     cairo_int_status_t status;
     const unsigned char *mime_data;
     unsigned long mime_data_length;
     cairo_image_info_t info;
+    char smask_buf[30];
 
     if (surface->pdf_version < CAIRO_PDF_VERSION_1_5)
 	return CAIRO_INT_STATUS_UNSUPPORTED;
@@ -2755,16 +2766,25 @@ _cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t   *surface,
     if (status)
 	return status;
 
+    if (surface_entry->smask_res.id)
+	snprintf(smask_buf, sizeof(smask_buf), "   /SMask %d 0 R\n", surface_entry->smask_res.id);
+    else
+	smask_buf[0] = 0;
+
     status = _cairo_pdf_surface_open_stream (surface,
-					     &res,
+					     &surface_entry->surface_res,
 					     FALSE,
 					     "   /Type /XObject\n"
 					     "   /Subtype /Image\n"
 					     "   /Width %d\n"
 					     "   /Height %d\n"
+					     "   /Interpolate %s\n"
+					     "%s"
 					     "   /Filter /JPXDecode\n",
 					     info.width,
-					     info.height);
+					     info.height,
+					     surface_entry->interpolate ? "true" : "false",
+					     smask_buf);
     if (status)
 	return status;
 
@@ -2775,15 +2795,16 @@ _cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t   *surface,
 }
 
 static cairo_int_status_t
-_cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t   *surface,
-				    cairo_surface_t	  *source,
-				    cairo_pdf_resource_t   res)
+_cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t              *surface,
+				    cairo_surface_t	             *source,
+				    cairo_pdf_source_surface_entry_t *surface_entry)
 {
     cairo_int_status_t status;
     const unsigned char *mime_data;
     unsigned long mime_data_length;
     cairo_image_info_t info;
     const char *colorspace;
+    char smask_buf[30];
 
     cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG,
 				 &mime_data, &mime_data_length);
@@ -2810,20 +2831,29 @@ _cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t   *surface,
 	    return CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
+    if (surface_entry->smask_res.id)
+	snprintf(smask_buf, sizeof(smask_buf), "   /SMask %d 0 R\n", surface_entry->smask_res.id);
+    else
+	smask_buf[0] = 0;
+
     status = _cairo_pdf_surface_open_stream (surface,
-					     &res,
+					     &surface_entry->surface_res,
 					     FALSE,
 					     "   /Type /XObject\n"
 					     "   /Subtype /Image\n"
 					     "   /Width %d\n"
 					     "   /Height %d\n"
 					     "   /ColorSpace %s\n"
+					     "   /Interpolate %s\n"
 					     "   /BitsPerComponent %d\n"
+					     "%s"
 					     "   /Filter /DCTDecode\n",
 					     info.width,
 					     info.height,
 					     colorspace,
-					     info.bits_per_component);
+					     surface_entry->interpolate ? "true" : "false",
+					     info.bits_per_component,
+					     smask_buf);
     if (unlikely (status))
 	return status;
 
@@ -2843,15 +2873,15 @@ _cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t        *surface,
 
     if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
 	if (!source->hash_entry->stencil_mask) {
-	    status = _cairo_pdf_surface_emit_jbig2_image (surface, source->surface, source->hash_entry->surface_res);
+	    status = _cairo_pdf_surface_emit_jbig2_image (surface, source->surface, source->hash_entry);
 	    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
 		return status;
 
-	    status = _cairo_pdf_surface_emit_jpx_image (surface, source->surface, source->hash_entry->surface_res);
+	    status = _cairo_pdf_surface_emit_jpx_image (surface, source->surface, source->hash_entry);
 	    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
 		return status;
 
-	    status = _cairo_pdf_surface_emit_jpeg_image (surface, source->surface, source->hash_entry->surface_res);
+	    status = _cairo_pdf_surface_emit_jpeg_image (surface, source->surface, source->hash_entry);
 	    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
 		return status;
 	}
@@ -2866,10 +2896,17 @@ _cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t        *surface,
     if (unlikely (status))
 	return status;
 
-    status = _cairo_pdf_surface_emit_image (surface, image,
-					    &source->hash_entry->surface_res,
-					    source->hash_entry->interpolate,
-					    source->hash_entry->stencil_mask);
+    if (source->hash_entry->smask) {
+	status = _cairo_pdf_surface_emit_smask (surface,
+						image,
+						FALSE,
+						source->hash_entry->interpolate,
+						&source->hash_entry->surface_res);
+    } else {
+	status = _cairo_pdf_surface_emit_image (surface,
+						image,
+						source->hash_entry);
+    }
 
     if (source->type == CAIRO_PATTERN_TYPE_SURFACE)
 	_cairo_surface_release_source_image (source->surface, image, image_extra);
@@ -3032,7 +3069,9 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t	*surface,
 							pdf_pattern->operator,
 							pattern->filter,
 							FALSE,
+							FALSE,
 							&pdf_pattern->extents,
+							NULL,
 							&pattern_resource,
 							&pattern_width,
 							&pattern_height,
@@ -4137,6 +4176,7 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t          *surface,
 					  cairo_operator_t              op,
 					  const cairo_pattern_t        *source,
 					  const cairo_rectangle_int_t  *extents,
+					  cairo_pdf_resource_t	       *smask_res,
 					  cairo_bool_t                  stencil_mask)
 {
     cairo_pdf_resource_t surface_res;
@@ -4167,7 +4207,9 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t          *surface,
 							op,
 							source->filter,
 							stencil_mask,
+							FALSE,
 							extents,
+							smask_res,
 							&surface_res,
 							&width,
 							&height,
@@ -4302,6 +4344,7 @@ _cairo_pdf_surface_paint_pattern (cairo_pdf_surface_t          *surface,
 							 op,
 							 source,
 							 extents,
+							 NULL,
 							 mask);
     case CAIRO_PATTERN_TYPE_LINEAR:
     case CAIRO_PATTERN_TYPE_RADIAL:
@@ -6567,6 +6610,182 @@ _cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface)
     return _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, TRUE, TRUE);
 }
 
+/* If source is an opaque image and mask is an image and both images
+ * have the same bounding box we can emit them as a image/smask pair.
+ */
+static cairo_int_status_t
+_cairo_pdf_surface_emit_combined_smask (cairo_pdf_surface_t         *surface,
+					cairo_operator_t	     op,
+					const cairo_pattern_t       *source,
+					const cairo_pattern_t       *mask,
+					const cairo_rectangle_int_t *extents)
+{
+    cairo_int_status_t status;
+    cairo_image_surface_t  *image;
+    void		   *image_extra;
+    cairo_image_transparency_t transparency;
+    cairo_pdf_resource_t smask_res;
+    int src_width, src_height;
+    int mask_width, mask_height;
+    double src_x_offset, src_y_offset;
+    double mask_x_offset, mask_y_offset;
+    double src_x1, src_y1, src_x2, src_y2;
+    double mask_x1, mask_y1, mask_x2, mask_y2;
+    cairo_matrix_t p2u;
+    double src_radius, mask_radius, e;
+    cairo_rectangle_int_t extents2;
+    cairo_bool_t need_smask;
+
+    /* Check that source and mask are images */
+
+    if (!((source->type == CAIRO_PATTERN_TYPE_SURFACE || source->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) &&
+	  (mask->type == CAIRO_PATTERN_TYPE_SURFACE || mask->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+	((cairo_surface_pattern_t *) source)->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
+    {
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
+	((cairo_surface_pattern_t *) mask)->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
+    {
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (source->extend != CAIRO_EXTEND_NONE || mask->extend != CAIRO_EXTEND_NONE)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Check that source is opaque and get image sizes */
+
+    status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source,
+								   &image, &image_extra);
+    if (unlikely (status))
+	return status;
+
+    if (image->base.status)
+	return image->base.status;
+
+    src_width = image->width;
+    src_height = image->height;
+    if (source->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
+	cairo_surface_get_device_offset (&image->base, &src_x_offset, &src_y_offset);
+    } else {
+	src_x_offset = 0;
+	src_y_offset = 0;
+    }
+
+    transparency = _cairo_image_analyze_transparency (image);
+    _cairo_pdf_surface_release_source_image_from_pattern (surface, source, image, image_extra);
+
+    if (transparency != CAIRO_IMAGE_IS_OPAQUE)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, mask,
+								   &image, &image_extra);
+    if (unlikely (status))
+	return status;
+
+    if (image->base.status)
+	return image->base.status;
+
+    mask_width = image->width;
+    mask_height = image->height;
+    if (mask->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
+	cairo_surface_get_device_offset (&image->base, &mask_x_offset, &mask_y_offset);
+    } else {
+	mask_x_offset = 0;
+	mask_y_offset = 0;
+    }
+
+    transparency = _cairo_image_analyze_transparency (image);
+    need_smask = transparency != CAIRO_IMAGE_IS_OPAQUE;
+
+    _cairo_pdf_surface_release_source_image_from_pattern (surface, mask, image, image_extra);
+
+    /* Check that both images have the same extents with a tolerance
+     * of half the smallest source pixel. */
+
+    p2u = source->matrix;
+    status = cairo_matrix_invert (&p2u);
+    /* cairo_pattern_set_matrix ensures the matrix is invertible */
+    assert (status == CAIRO_INT_STATUS_SUCCESS);
+    src_x1 = 0;
+    src_y1 = 0;
+    src_x2 = src_width;
+    src_y2 = src_height;
+    cairo_matrix_transform_point (&p2u, &src_x1, &src_y1);
+    cairo_matrix_transform_point (&p2u, &src_x2, &src_y2);
+    src_radius = _cairo_matrix_transformed_circle_major_axis (&p2u, 0.5);
+
+    p2u = mask->matrix;
+    status = cairo_matrix_invert (&p2u);
+    /* cairo_pattern_set_matrix ensures the matrix is invertible */
+    assert (status == CAIRO_INT_STATUS_SUCCESS);
+    mask_x1 = 0;
+    mask_y1 = 0;
+    mask_x2 = mask_width;
+    mask_y2 = mask_height;
+    cairo_matrix_transform_point (&p2u, &mask_x1, &mask_y1);
+    cairo_matrix_transform_point (&p2u, &mask_x2, &mask_y2);
+    mask_radius = _cairo_matrix_transformed_circle_major_axis (&p2u, 0.5);
+
+    if (src_radius < mask_radius)
+	e = src_radius;
+    else
+	e = mask_radius;
+
+    if (fabs(src_x1 - mask_x1) > e ||
+	fabs(src_x2 - mask_x2) > e ||
+	fabs(src_y1 - mask_y1) > e ||
+	fabs(src_y2 - mask_y2) > e)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Check both images have same device offset */
+    if (fabs(src_x_offset - mask_x_offset) > e ||
+	fabs(src_y_offset - mask_y_offset) > e)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (need_smask) {
+	status = _cairo_pdf_surface_add_source_surface (surface,
+							NULL,
+							mask,
+							op,
+							source->filter,
+							FALSE,
+							TRUE,
+							extents,
+							NULL,
+							&smask_res,
+							&mask_width,
+							&mask_height,
+							&mask_x_offset,
+							&mask_y_offset,
+							&extents2);
+	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, op, source, extents,
+						       need_smask ? &smask_res : NULL,
+						       FALSE);
+    if (unlikely (status))
+	return status;
+
+    _cairo_output_stream_printf (surface->output, "Q\n");
+
+    status = _cairo_output_stream_get_status (surface->output);
+
+
+    return status;
+}
+
 /* 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,
@@ -6617,7 +6836,7 @@ _cairo_pdf_surface_emit_stencil_mask (cairo_pdf_surface_t         *surface,
 	return status;
 
     _cairo_output_stream_printf (surface->output, "q\n");
-    status = _cairo_pdf_surface_paint_surface_pattern (surface, op, mask, extents, TRUE);
+    status = _cairo_pdf_surface_paint_surface_pattern (surface, op, mask, extents, NULL, TRUE);
     if (unlikely (status))
 	return status;
 
@@ -6847,6 +7066,11 @@ _cairo_pdf_surface_mask (void			*abstract_surface,
     if (unlikely (status))
 	goto cleanup;
 
+    /* Check if we can combine source and mask into a smask image */
+    status = _cairo_pdf_surface_emit_combined_smask (surface, op, source, mask, &extents.bounded);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+	goto cleanup;
+
     /* Check if we can use a stencil mask */
     status = _cairo_pdf_surface_emit_stencil_mask (surface, op, source, mask, &extents.bounded);
     if (status != CAIRO_INT_STATUS_UNSUPPORTED)


More information about the cairo-commit mailing list