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

Adrian Johnson ajohnson at kemper.freedesktop.org
Sat Oct 14 10:30:25 UTC 2017


 src/cairo-svg-surface-private.h |    9 +
 src/cairo-svg-surface.c         |  185 ++++++++++++++++++++++++++++++++--------
 2 files changed, 159 insertions(+), 35 deletions(-)

New commits:
commit 965ba86bbf87fb0d8df666cbba7c8823cc393b0b
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat Oct 14 19:47:39 2017 +1030

    svg: use hash table instead of user_data to track emitted surfaces
    
    Setting a key on the source surface->user_data prevents the surface
    from being reused to create another svg file.
    
    The hash table also supports CAIRO_MIME_TYPE_UNIQUE_ID.
    
    https://lists.cairographics.org/archives/cairo/2017-October/028406.html

diff --git a/src/cairo-svg-surface-private.h b/src/cairo-svg-surface-private.h
index 9c9f908b..9dce1742 100644
--- a/src/cairo-svg-surface-private.h
+++ b/src/cairo-svg-surface-private.h
@@ -48,6 +48,14 @@
 
 typedef struct cairo_svg_document cairo_svg_document_t;
 
+typedef struct _cairo_svg_source_surface {
+    cairo_hash_entry_t base;
+    unsigned int id;
+    unsigned char *unique_id;
+    unsigned long unique_id_length;
+    cairo_surface_t *source;
+} cairo_svg_source_surface_t;
+
 typedef struct cairo_svg_surface {
     cairo_surface_t base;
 
@@ -61,6 +69,7 @@ typedef struct cairo_svg_surface {
 
     cairo_output_stream_t *xml_node;
     cairo_array_t	   page_set;
+    cairo_hash_table_t    *source_surfaces;
 
     cairo_surface_clipper_t clipper;
     unsigned int clip_level;
diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c
index a9b32e6d..f5f42be3 100644
--- a/src/cairo-svg-surface.c
+++ b/src/cairo-svg-surface.c
@@ -94,6 +94,7 @@ static const char *_cairo_svg_supported_mime_types[] =
 {
     CAIRO_MIME_TYPE_JPEG,
     CAIRO_MIME_TYPE_PNG,
+    CAIRO_MIME_TYPE_UNIQUE_ID,
     CAIRO_MIME_TYPE_URI,
     NULL
 };
@@ -254,6 +255,9 @@ cairo_svg_surface_create_for_stream (cairo_write_func_t		 write_func,
  * or %CAIRO_MIME_TYPE_PNG is specified, the corresponding data is
  * Base64-encoded and emitted.
  *
+ * If %CAIRO_MIME_TYPE_UNIQUE_ID is present, all surfaces with the same
+ * unique identifier will only be embedded once.
+ *
  * Return value: a pointer to the newly created surface. The caller
  * owns the surface and should call cairo_surface_destroy() when done
  * with it.
@@ -401,6 +405,103 @@ cairo_svg_version_to_string (cairo_svg_version_t version)
     return _cairo_svg_version_strings[version];
 }
 
+static void
+_cairo_svg_source_surface_init_key (cairo_svg_source_surface_t *key)
+{
+    if (key->unique_id && key->unique_id_length > 0) {
+	key->base.hash = _cairo_hash_bytes (_CAIRO_HASH_INIT_VALUE,
+					    key->unique_id, key->unique_id_length);
+    } else {
+	key->base.hash = key->id;
+    }
+}
+
+static cairo_bool_t
+_cairo_svg_source_surface_equal (const void *key_a, const void *key_b)
+{
+    const cairo_svg_source_surface_t *a = key_a;
+    const cairo_svg_source_surface_t *b = key_b;
+
+    if (a->unique_id && b->unique_id && a->unique_id_length == b->unique_id_length)
+	return (memcmp (a->unique_id, b->unique_id, a->unique_id_length) == 0);
+
+    return (a->id == b->id);
+}
+
+static void
+_cairo_svg_source_surface_pluck (void *entry, void *closure)
+{
+    cairo_svg_source_surface_t *surface_entry = entry;
+    cairo_hash_table_t *patterns = closure;
+
+    _cairo_hash_table_remove (patterns, &surface_entry->base);
+    cairo_surface_destroy (surface_entry->source);
+    free (surface_entry->unique_id);
+    free (surface_entry);
+}
+
+static cairo_status_t
+_cairo_svg_surface_add_source_surface (cairo_svg_surface_t  *surface,
+				       cairo_surface_t	    *source_surface,
+				       int                  *source_id,
+				       cairo_bool_t         *is_new)
+{
+    cairo_svg_source_surface_t source_key;
+    cairo_svg_source_surface_t *source_entry;
+    unsigned char *unique_id = NULL;
+    unsigned long unique_id_length = 0;
+    cairo_status_t status;
+
+    source_key.id  = source_surface->unique_id;
+    cairo_surface_get_mime_data (source_surface, CAIRO_MIME_TYPE_UNIQUE_ID,
+				 (const unsigned char **) &source_key.unique_id,
+				 &source_key.unique_id_length);
+    _cairo_svg_source_surface_init_key (&source_key);
+    source_entry = _cairo_hash_table_lookup (surface->source_surfaces, &source_key.base);
+    if (source_entry) {
+	*source_id = source_entry->id;
+	*is_new = FALSE;
+	return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (source_key.unique_id && source_key.unique_id_length > 0) {
+	unique_id = _cairo_malloc (source_key.unique_id_length);
+	if (unique_id == NULL) {
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	}
+
+	unique_id_length = source_key.unique_id_length;
+	memcpy (unique_id, source_key.unique_id, unique_id_length);
+    } else {
+	unique_id = NULL;
+	unique_id_length = 0;
+    }
+
+    source_entry = malloc (sizeof (cairo_svg_source_surface_t));
+    if (source_entry == NULL) {
+	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	goto fail;
+    }
+
+    source_entry->id = source_key.id;
+    source_entry->unique_id_length = unique_id_length;
+    source_entry->unique_id = unique_id;
+    source_entry->source = cairo_surface_reference (source_surface);
+    _cairo_svg_source_surface_init_key (source_entry);
+    status = _cairo_hash_table_insert (surface->source_surfaces, &source_entry->base);
+    if (unlikely(status))
+	goto fail;
+
+    *is_new = TRUE;
+    *source_id = source_entry->id;
+    return CAIRO_STATUS_SUCCESS;
+
+  fail:
+    free (unique_id);
+    free (source_entry);
+    return status;
+}
+
 static cairo_bool_t
 _cliprect_covers_surface (cairo_svg_surface_t *surface,
 			  cairo_path_fixed_t *path)
@@ -524,6 +625,12 @@ _cairo_svg_surface_create_for_document (cairo_svg_document_t	*document,
     surface->force_fallbacks = FALSE;
     surface->content = content;
 
+    surface->source_surfaces = _cairo_hash_table_create (_cairo_svg_source_surface_equal);
+    if (unlikely (surface->source_surfaces == NULL)) {
+	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	goto CLEANUP;
+    }
+
     paginated = _cairo_paginated_surface_create (&surface->base,
 					         surface->content,
 						 &cairo_svg_surface_paginated_backend);
@@ -1015,6 +1122,11 @@ _cairo_svg_surface_finish (void *abstract_surface)
 
     _cairo_surface_clipper_reset (&surface->clipper);
 
+    _cairo_hash_table_foreach (surface->source_surfaces,
+			       _cairo_svg_source_surface_pluck,
+			       surface->source_surfaces);
+    _cairo_hash_table_destroy (surface->source_surfaces);
+
     status2 = _cairo_svg_document_destroy (document);
     if (status == CAIRO_STATUS_SUCCESS)
 	status = status2;
@@ -1286,7 +1398,8 @@ _cairo_svg_surface_emit_attr_value (cairo_output_stream_t *stream,
 
 static cairo_status_t
 _cairo_svg_surface_emit_surface (cairo_svg_document_t *document,
-				 cairo_surface_t *surface)
+				 cairo_surface_t *surface,
+				 int source_id)
 {
     cairo_rectangle_int_t extents;
     cairo_bool_t is_bounded;
@@ -1294,18 +1407,12 @@ _cairo_svg_surface_emit_surface (cairo_svg_document_t *document,
     const unsigned char *uri;
     unsigned long uri_len;
 
-    if (_cairo_user_data_array_get_data (&surface->user_data,
-					 (cairo_user_data_key_t *) document))
-    {
-	return CAIRO_STATUS_SUCCESS;
-    }
-
     is_bounded = _cairo_surface_get_extents (surface, &extents);
     assert (is_bounded);
 
     _cairo_output_stream_printf (document->xml_node_defs,
 				 "<image id=\"image%d\" width=\"%d\" height=\"%d\"",
-				 surface->unique_id,
+				 source_id,
 				 extents.width, extents.height);
 
     _cairo_output_stream_printf (document->xml_node_defs, " xlink:href=\"");
@@ -1324,10 +1431,7 @@ _cairo_svg_surface_emit_surface (cairo_svg_document_t *document,
 
     _cairo_output_stream_printf (document->xml_node_defs, "\"/>\n");
 
-    /* and tag it */
-    return _cairo_user_data_array_set_data (&surface->user_data,
-					    (cairo_user_data_key_t *) document,
-					    document, NULL);
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_status_t
@@ -1341,17 +1445,29 @@ _cairo_svg_surface_emit_composite_surface_pattern (cairo_output_stream_t   *outp
 {
     cairo_status_t status;
     cairo_matrix_t p2u;
+    int source_id;
+    cairo_bool_t is_new;
 
     p2u = pattern->base.matrix;
     status = cairo_matrix_invert (&p2u);
     /* cairo_pattern_set_matrix ensures the matrix is invertible */
     assert (status == CAIRO_STATUS_SUCCESS);
 
-    status = _cairo_svg_surface_emit_surface (svg_surface->document,
-					      pattern->surface);
+    status = _cairo_svg_surface_add_source_surface (svg_surface,
+						    pattern->surface,
+						    &source_id,
+						    &is_new);
     if (unlikely (status))
 	return status;
 
+    if (is_new) {
+	status = _cairo_svg_surface_emit_surface (svg_surface->document,
+						  pattern->surface,
+						  source_id);
+	if (unlikely (status))
+	    return status;
+    }
+
     if (pattern_id != invalid_pattern_id) {
 	cairo_rectangle_int_t extents;
 	cairo_bool_t is_bounded;
@@ -1373,7 +1489,7 @@ _cairo_svg_surface_emit_composite_surface_pattern (cairo_output_stream_t   *outp
 
     _cairo_output_stream_printf (output,
 				 "<use xlink:href=\"#image%d\"",
-				 pattern->surface->unique_id);
+				 source_id);
     if (extra_attributes)
 	_cairo_output_stream_printf (output, " %s", extra_attributes);
 
@@ -1394,7 +1510,8 @@ _cairo_svg_surface_emit_composite_surface_pattern (cairo_output_stream_t   *outp
 
 static cairo_status_t
 _cairo_svg_surface_emit_recording_surface (cairo_svg_document_t      *document,
-					   cairo_recording_surface_t *source)
+					   cairo_recording_surface_t *source,
+					   int                        source_id)
 {
     cairo_status_t status;
     cairo_surface_t *paginated_surface;
@@ -1402,15 +1519,8 @@ _cairo_svg_surface_emit_recording_surface (cairo_svg_document_t      *document,
     cairo_array_t *page_set;
     cairo_rectangle_int_t extents;
     cairo_bool_t bounded;
-
     cairo_output_stream_t *contents;
 
-    if (_cairo_user_data_array_get_data (&source->base.user_data,
-					 (cairo_user_data_key_t *) document))
-    {
-	return CAIRO_STATUS_SUCCESS;
-    }
-
     bounded = _cairo_surface_get_extents (&source->base, &extents);
     paginated_surface = _cairo_svg_surface_create_for_document (document,
 								source->base.content,
@@ -1463,13 +1573,13 @@ _cairo_svg_surface_emit_recording_surface (cairo_svg_document_t      *document,
 				     "<g id=\"surface%d\" "
 				     "clip-path=\"url(#clip%d)\" "
 				     "filter=\"url(#alpha)\">\n",
-				     source->base.unique_id,
+				     source_id,
 				     svg_surface->base_clip);
     } else {
 	_cairo_output_stream_printf (document->xml_node_defs,
 				     "<g id=\"surface%d\" "
 				     "clip-path=\"url(#clip%d)\">\n",
-				     source->base.unique_id,
+				     source_id,
 				     svg_surface->base_clip);
     }
 
@@ -1495,13 +1605,7 @@ _cairo_svg_surface_emit_recording_surface (cairo_svg_document_t      *document,
     status = cairo_surface_status (paginated_surface);
     cairo_surface_destroy (paginated_surface);
 
-    if (unlikely (status))
-	return status;
-
-    /* and tag it */
-    return _cairo_user_data_array_set_data (&source->base.user_data,
-					    (cairo_user_data_key_t *) document,
-					    document, NULL);
+    return status;
 }
 
 static cairo_recording_surface_t *
@@ -1528,17 +1632,28 @@ _cairo_svg_surface_emit_composite_recording_pattern (cairo_output_stream_t	*outp
     cairo_recording_surface_t *recording_surface;
     cairo_matrix_t p2u;
     cairo_status_t status;
+    int source_id;
+    cairo_bool_t is_new;
 
     p2u = pattern->base.matrix;
     status = cairo_matrix_invert (&p2u);
     /* cairo_pattern_set_matrix ensures the matrix is invertible */
     assert (status == CAIRO_STATUS_SUCCESS);
 
-    recording_surface = to_recording_surface (pattern);
-    status = _cairo_svg_surface_emit_recording_surface (document, recording_surface);
+    status = _cairo_svg_surface_add_source_surface (surface,
+						    pattern->surface,
+						    &source_id,
+						    &is_new);
     if (unlikely (status))
 	return status;
 
+    if (is_new) {
+	recording_surface = to_recording_surface (pattern);
+	status = _cairo_svg_surface_emit_recording_surface (document, recording_surface, source_id);
+	if (unlikely (status))
+	    return status;
+    }
+
     if (pattern_id != invalid_pattern_id) {
 	_cairo_output_stream_printf (output,
 				     "<pattern id=\"pattern%d\" "
@@ -1553,7 +1668,7 @@ _cairo_svg_surface_emit_composite_recording_pattern (cairo_output_stream_t	*outp
 
     _cairo_output_stream_printf (output,
 				 "<use xlink:href=\"#surface%d\"",
-				 recording_surface->base.unique_id);
+				 source_id);
 
     if (pattern_id == invalid_pattern_id) {
 	_cairo_svg_surface_emit_operator (output, surface, op);


More information about the cairo-commit mailing list