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

Adrian Johnson ajohnson at kemper.freedesktop.org
Mon Oct 23 20:32:26 UTC 2017


 src/cairo-pattern-private.h     |    5 ++
 src/cairo-pattern.c             |   53 ++++++++++++++++++++++++++++
 src/cairo-pdf-surface-private.h |    1 
 src/cairo-pdf-surface.c         |   74 +++++++++++++++++++++++++++++++---------
 4 files changed, 117 insertions(+), 16 deletions(-)

New commits:
commit 378e8e2f59a109a40da8e40893652a4c003a4dc7
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Tue Oct 24 07:01:14 2017 +1030

    pdf: set ca/CA instead of using an smask when the mask has constant alpha

diff --git a/src/cairo-pattern-private.h b/src/cairo-pattern-private.h
index ad7e2e77..98f3be03 100644
--- a/src/cairo-pattern-private.h
+++ b/src/cairo-pattern-private.h
@@ -256,6 +256,11 @@ _cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient,
 				  const cairo_rectangle_int_t *extents,
 				  cairo_color_t *color);
 
+cairo_bool_t
+_cairo_pattern_is_constant_alpha (const cairo_pattern_t          *abstract_pattern,
+				  const cairo_rectangle_int_t    *extents,
+				  double                         *alpha);
+
 cairo_private void
 _cairo_gradient_pattern_fit_to_range (const cairo_gradient_pattern_t *gradient,
 				      double			      max_value,
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 7c2d5d31..11637fca 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -3134,6 +3134,59 @@ _cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient,
     return TRUE;
 }
 
+/**
+ * _cairo_pattern_is_constant_alpha:
+ *
+ * Convenience function to determine whether a pattern has constant
+ * alpha within the given extents. In this case the alpha argument is
+ * initialized to the alpha within the extents.
+ *
+ * Return value: %TRUE if the pattern has constant alpha.
+ **/
+cairo_bool_t
+_cairo_pattern_is_constant_alpha (const cairo_pattern_t         *abstract_pattern,
+				  const cairo_rectangle_int_t   *extents,
+				  double                        *alpha)
+{
+    const cairo_pattern_union_t *pattern;
+    cairo_color_t color;
+
+    if (_cairo_pattern_is_clear (abstract_pattern)) {
+	*alpha = 0.0;
+	return TRUE;
+    }
+
+    if (_cairo_pattern_is_opaque (abstract_pattern, extents)) {
+	*alpha = 1.0;
+	return TRUE;
+    }
+
+    pattern = (cairo_pattern_union_t *) abstract_pattern;
+    switch (pattern->base.type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+	*alpha = pattern->solid.color.alpha;
+	return TRUE;
+
+    case CAIRO_PATTERN_TYPE_LINEAR:
+    case CAIRO_PATTERN_TYPE_RADIAL:
+	if (_cairo_gradient_pattern_is_solid (&pattern->gradient.base, extents, &color)) {
+	    *alpha = color.alpha;
+	    return TRUE;
+	} else {
+	    return FALSE;
+	}
+
+	/* TODO: need to test these as well */
+    case CAIRO_PATTERN_TYPE_SURFACE:
+    case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+    case CAIRO_PATTERN_TYPE_MESH:
+	return FALSE;
+    }
+
+    ASSERT_NOT_REACHED;
+    return FALSE;
+}
+
 static cairo_bool_t
 _mesh_is_clear (const cairo_mesh_pattern_t *mesh)
 {
diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h
index 98f0cfdd..75d5259c 100644
--- a/src/cairo-pdf-surface-private.h
+++ b/src/cairo-pdf-surface-private.h
@@ -76,6 +76,7 @@ typedef struct _cairo_pdf_source_surface_entry {
     cairo_bool_t interpolate;
     cairo_bool_t stencil_mask;
     cairo_bool_t smask;
+    cairo_bool_t need_transp_group;
     cairo_pdf_resource_t surface_res;
     cairo_pdf_resource_t smask_res;
 
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index fa40bc30..d0bb953a 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -1456,6 +1456,7 @@ _get_source_surface_extents (cairo_surface_t         *source,
  * @filter: [in] filter type of the source pattern
  * @stencil_mask: [in] if true, the surface will be written to the PDF as an /ImageMask
  * @smask: [in] if true, only the alpha channel will be written (images only)
+ * @need_transp_group: [in] if true and an XObject is used, make it a Transparency group
  * @extents: [in] extents of the operation that is using this source
  * @smask_res: [in] if not NULL, the image written will specify this resource as the smask for
  * the image (images only)
@@ -1482,6 +1483,7 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t	         *surface,
 				       cairo_filter_t		          filter,
 				       cairo_bool_t                       stencil_mask,
 				       cairo_bool_t                       smask,
+				       cairo_bool_t                       need_transp_group,
 				       const cairo_rectangle_int_t       *extents,
 				       cairo_pdf_resource_t	         *smask_res,
 				       cairo_pdf_source_surface_entry_t **pdf_source,
@@ -1600,6 +1602,7 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t	         *surface,
     surface_entry->interpolate = interpolate;
     surface_entry->stencil_mask = stencil_mask;
     surface_entry->smask = smask;
+    surface_entry->need_transp_group = need_transp_group;
     surface_entry->unique_id_length = unique_id_length;
     surface_entry->unique_id = unique_id;
     if (smask_res)
@@ -2467,6 +2470,7 @@ _cairo_pdf_surface_add_padded_image_surface (cairo_pdf_surface_t          *surfa
 						    source->filter,
 						    FALSE, /* stencil mask */
 						    FALSE, /* smask */
+						    FALSE, /* need_transp_group */
 						    extents,
 						    NULL, /* smask_res */
 						    pdf_source,
@@ -3388,12 +3392,16 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t        *surface,
     }
 
     /* We can optimize away the transparency group allowing the viewer
-     * to replay the group in place when all operators are OVER and the
-     * recording contains only opaque and/or clear alpha.
+     * to replay the group in place when:
+     *  - ca/CA when painting this groups is 1.0 (need_transp_group is FALSE),
+     *  - all operators are OVER, and
+     *  - the recording contains only opaque and/or clear alpha.
      */
-    transparency_group = !(pdf_source->hash_entry->operator == CAIRO_OPERATOR_OVER &&
+    transparency_group = pdf_source->hash_entry->need_transp_group ||
+	!(pdf_source->hash_entry->operator == CAIRO_OPERATOR_OVER &&
 			   _cairo_recording_surface_has_only_bilevel_alpha (recording) &&
 			   _cairo_recording_surface_has_only_op_over (recording));
+
     status = _cairo_pdf_surface_open_content_stream (surface, &bbox, &pdf_source->hash_entry->surface_res,
 						     TRUE, transparency_group);
     if (unlikely (status))
@@ -3481,6 +3489,7 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t	*surface,
 							pattern->filter,
 							FALSE, /* stencil mask */
 							FALSE, /* smask */
+							FALSE, /* need_transp_group */
 							&pdf_pattern->extents,
 							NULL, /* smask_res */
 							&pdf_source,
@@ -4622,12 +4631,13 @@ _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,
+					  double                        alpha,
 					  cairo_pdf_resource_t	       *smask_res,
 					  cairo_bool_t                  stencil_mask)
 {
     cairo_matrix_t cairo_p2d, pdf_p2d;
     cairo_int_status_t status;
-    int alpha;
+    int alpha_id;
     double x_offset;
     double y_offset;
     cairo_pdf_source_surface_entry_t *pdf_source;
@@ -4651,6 +4661,7 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t          *surface,
 							source->filter,
 							stencil_mask,
 							FALSE, /* smask */
+							alpha != 1.0, /* need_transp_group */
 							extents,
 							smask_res,
 							&pdf_source,
@@ -4686,7 +4697,7 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t          *surface,
 	_cairo_output_stream_printf (surface->output, " cm\n");
     }
 
-    status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
+    status = _cairo_pdf_surface_add_alpha (surface, alpha, &alpha_id);
     if (unlikely (status))
 	return status;
 
@@ -4697,7 +4708,7 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t          *surface,
     } else {
 	_cairo_output_stream_printf (surface->output,
 				     "/a%d gs /x%d Do\n",
-				     alpha,
+				     alpha_id,
 				     pdf_source->surface_res.id);
     }
 
@@ -4708,12 +4719,13 @@ static cairo_int_status_t
 _cairo_pdf_surface_paint_gradient (cairo_pdf_surface_t         *surface,
 				   cairo_operator_t             op,
 				   const cairo_pattern_t       *source,
-				   const cairo_rectangle_int_t *extents)
+				   const cairo_rectangle_int_t *extents,
+				   double                       alpha)
 {
     cairo_pdf_resource_t shading_res, gstate_res;
     cairo_matrix_t pat_to_pdf;
     cairo_int_status_t status;
-    int alpha;
+    int alpha_id;
 
     status = _cairo_pdf_surface_add_pdf_shading (surface, source,
 						 op, extents,
@@ -4752,13 +4764,13 @@ _cairo_pdf_surface_paint_gradient (cairo_pdf_surface_t         *surface,
 				     gstate_res.id,
 				     shading_res.id);
     } else {
-	status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
+	status = _cairo_pdf_surface_add_alpha (surface, alpha, &alpha_id);
 	if (unlikely (status))
 	    return status;
 
 	_cairo_output_stream_printf (surface->output,
 				     "/a%d gs /sh%d sh\n",
-				     alpha,
+				     alpha_id,
 				     shading_res.id);
     }
 
@@ -4770,6 +4782,7 @@ _cairo_pdf_surface_paint_pattern (cairo_pdf_surface_t          *surface,
 				  cairo_operator_t              op,
 				  const cairo_pattern_t        *source,
 				  const cairo_rectangle_int_t  *extents,
+				  double                        alpha,
 				  cairo_bool_t                  mask)
 {
     switch (source->type) {
@@ -4779,6 +4792,7 @@ _cairo_pdf_surface_paint_pattern (cairo_pdf_surface_t          *surface,
 							 op,
 							 source,
 							 extents,
+							 alpha,
 							 NULL,
 							 mask);
     case CAIRO_PATTERN_TYPE_LINEAR:
@@ -4787,7 +4801,8 @@ _cairo_pdf_surface_paint_pattern (cairo_pdf_surface_t          *surface,
 	return _cairo_pdf_surface_paint_gradient (surface,
 						  op,
 						  source,
-						  extents);
+						  extents,
+						  alpha);
 
     case CAIRO_PATTERN_TYPE_SOLID:
     default:
@@ -6436,7 +6451,8 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t	*surface,
 						   CAIRO_OPERATOR_OVER,
 						   group->mask,
 						   &group->extents,
-						   FALSE);
+						   1.0, /* alpha */
+						   FALSE); /* mask */
 	if (unlikely (status))
 	    return status;
 
@@ -6512,7 +6528,8 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t	*surface,
 						   CAIRO_OPERATOR_OVER,
 						   group->source,
 						   &group->extents,
-						   FALSE);
+						   1.0, /* alpha */
+						   FALSE); /* mask */
 	if (unlikely (status))
 	    return status;
 
@@ -7262,6 +7279,7 @@ _cairo_pdf_surface_emit_combined_smask (cairo_pdf_surface_t         *surface,
 							source->filter,
 							FALSE, /* stencil mask */
 							TRUE, /* smask */
+							FALSE, /* need_transp_group */
 							extents,
 							NULL, /* smask_res */
 							&pdf_source,
@@ -7278,6 +7296,7 @@ _cairo_pdf_surface_emit_combined_smask (cairo_pdf_surface_t         *surface,
 
     _cairo_output_stream_printf (surface->output, "q\n");
     status = _cairo_pdf_surface_paint_surface_pattern (surface, op, source, extents,
+						       1.0, /* alpha */
 						       need_smask ? &pdf_source->surface_res : NULL,
 						       FALSE);
     if (unlikely (status))
@@ -7341,7 +7360,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, NULL, TRUE);
+    status = _cairo_pdf_surface_paint_surface_pattern (surface, op, mask, extents, 1.0, NULL, TRUE);
     if (unlikely (status))
 	return status;
 
@@ -7424,7 +7443,8 @@ _cairo_pdf_surface_paint (void			*abstract_surface,
 						   op,
 						   source,
 						   &extents.bounded,
-						   FALSE);
+						   1.0, /* alpha */
+						   FALSE); /* mask */
 	if (unlikely (status))
 	    goto cleanup;
 
@@ -7512,6 +7532,7 @@ _cairo_pdf_surface_mask (void			*abstract_surface,
     cairo_int_status_t status;
     cairo_rectangle_int_t r;
     cairo_box_t box;
+    double alpha;
 
     status = _cairo_composite_rectangles_init_for_mask (&extents,
 							&surface->base,
@@ -7592,6 +7613,26 @@ _cairo_pdf_surface_mask (void			*abstract_surface,
     if (status != CAIRO_INT_STATUS_UNSUPPORTED)
 	goto cleanup;
 
+    /* Check if we can set ca/CA instead of an smask. We could handle
+     * other source patterns as well but for now this is the easiest,
+     * and most common, case to handle. */
+    if (_cairo_pattern_is_constant_alpha (mask, &extents.bounded, &alpha) &&
+	_can_paint_pattern (source)) {
+	_cairo_output_stream_printf (surface->output, "q\n");
+	status = _cairo_pdf_surface_paint_pattern (surface,
+						   op,
+						   source,
+						   &extents.bounded,
+						   alpha,
+						   FALSE); /* mask */
+	if (unlikely (status))
+	    goto cleanup;
+
+	_cairo_output_stream_printf (surface->output, "Q\n");
+	_cairo_composite_rectangles_fini (&extents);
+	return _cairo_output_stream_get_status (surface->output);
+    }
+
     group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
     if (unlikely (group == NULL)) {
 	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
@@ -7869,7 +7910,8 @@ _cairo_pdf_surface_fill (void			*abstract_surface,
 						   op,
 						   source,
 						   &extents.bounded,
-						   FALSE);
+						   1.0, /* alpha */
+						   FALSE); /* mask */
 	if (unlikely (status))
 	    goto cleanup;
 


More information about the cairo-commit mailing list