summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cairo-svg-surface-private.h1
-rw-r--r--src/cairo-svg-surface.c231
-rw-r--r--test/Makefile.sources1
-rw-r--r--test/meson.build1
-rw-r--r--test/record-transform-paint.c64
-rw-r--r--test/reference/record-transform-paint.ref.pngbin0 -> 2187 bytes
6 files changed, 223 insertions, 75 deletions
diff --git a/src/cairo-svg-surface-private.h b/src/cairo-svg-surface-private.h
index b30948f33..32f032729 100644
--- a/src/cairo-svg-surface-private.h
+++ b/src/cairo-svg-surface-private.h
@@ -71,6 +71,7 @@ typedef struct cairo_svg_surface {
unsigned int clip_level;
cairo_bool_t paint_used;
+ cairo_bool_t transitive_paint_used;
cairo_paginated_mode_t paginated_mode;
diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c
index bc0b06a9f..c6eeb88d5 100644
--- a/src/cairo-svg-surface.c
+++ b/src/cairo-svg-surface.c
@@ -152,13 +152,24 @@ typedef struct _cairo_svg_source_surface {
unsigned int id;
unsigned char *unique_id;
unsigned long unique_id_length;
- cairo_bool_t paint_used;
+ cairo_bool_t transitive_paint_used;
} cairo_svg_source_surface_t;
+/*
+ * _cairo_svg_paint_element and _cairo_svg_paint are used to implement paints in transformed recording patterns.
+ */
+
+typedef struct _cairo_svg_paint_element {
+ unsigned int source_id;
+ cairo_matrix_t matrix;
+} cairo_svg_paint_element_t;
+
typedef struct _cairo_svg_paint {
cairo_hash_entry_t base;
unsigned int source_id;
- cairo_output_stream_t *xml_node;
+ cairo_bool_t paint_used;
+ cairo_array_t paint_elements;
+ cairo_box_double_t box;
} cairo_svg_paint_t;
typedef struct cairo_svg_page {
@@ -559,21 +570,65 @@ _cairo_svg_paint_pluck (void *entry, void *closure)
cairo_hash_table_t *patterns = closure;
_cairo_hash_table_remove (patterns, &paint->base);
- (void) _cairo_output_stream_destroy (paint->xml_node);
+ _cairo_array_fini (&paint->paint_elements);
free (paint);
}
static void
-_cairo_svg_paint_emit (void *entry, void *closure)
+_cairo_svg_paint_compute (cairo_svg_document_t *document, cairo_svg_paint_t *paint) {
+ for (unsigned int i = 0; i < paint->paint_elements.num_elements; i++) {
+ cairo_svg_paint_element_t *paint_element = _cairo_array_index (&paint->paint_elements, i);
+
+ cairo_svg_paint_t paint_key;
+ paint_key.source_id = paint_element->source_id;
+ _cairo_svg_paint_init_key (&paint_key);
+
+ cairo_svg_paint_t *found_paint_entry = _cairo_hash_table_lookup (document->paints,
+ &paint_key.base);
+ assert (found_paint_entry);
+
+ _cairo_svg_paint_compute (document, found_paint_entry);
+
+ cairo_box_double_t box = found_paint_entry->box;
+ _cairo_matrix_transform_bounding_box (&paint_element->matrix,
+ &box.p1.x, &box.p1.y,
+ &box.p2.x, &box.p2.y,
+ NULL);
+
+ if (i == 0) {
+ paint->box = box;
+ } else {
+ paint->box.p1.x = MIN (paint->box.p1.x, box.p1.x);
+ paint->box.p1.y = MIN (paint->box.p1.y, box.p1.y);
+ paint->box.p2.x = MAX (paint->box.p2.x, box.p2.x);
+ paint->box.p2.y = MAX (paint->box.p2.y, box.p2.y);
+ }
+ }
+ _cairo_array_truncate (&paint->paint_elements, 0);
+}
+
+static void
+_cairo_svg_paint_compute_func (void *entry, void *closure)
+{
+ cairo_svg_paint_t *paint = entry;
+ cairo_svg_document_t *document = closure;
+
+ _cairo_svg_paint_compute (document, paint);
+}
+
+static void
+_cairo_svg_paint_emit_func (void *entry, void *closure)
{
cairo_svg_paint_t *paint = entry;
cairo_output_stream_t *output = closure;
- _cairo_output_stream_printf (output,
- "<g id=\"paint-%d\">\n",
- paint->source_id);
- _cairo_memory_stream_copy (paint->xml_node, output);
- _cairo_output_stream_printf (output, "</g>\n");
+ if (paint->paint_used) {
+ _cairo_output_stream_printf (output,
+ "<rect id=\"paint-%d\" x=\"%f\" y=\"%f\" width=\"%f\" height=\"%f\"/>\n",
+ paint->source_id,
+ paint->box.p1.x, paint->box.p1.y,
+ paint->box.p2.x - paint->box.p1.x, paint->box.p2.y - paint->box.p1.y);
+ }
}
static cairo_status_t
@@ -768,6 +823,7 @@ _cairo_svg_surface_create_for_document (cairo_svg_document_t *document,
surface->current_clipper_output_stream = NULL;
surface->clip_level = 0;
surface->paint_used = FALSE;
+ surface->transitive_paint_used = FALSE;
surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
@@ -1285,31 +1341,31 @@ _cairo_svg_surface_emit_static_filter (cairo_svg_document_t *document, enum cair
#define _CAIRO_SVG_SURFACE_OUTPUT_FE_COMPOSITE_FILTER(operation) \
_cairo_output_stream_printf (document->xml_node_filters, \
- "<filter id=\"filter-%d\" filterUnits=\"userSpaceOnUse\" " \
- "x=\"0%%\" y=\"0%%\" width=\"100%%\" height=\"100%%\">\n" \
- "<feImage xlink:href=\"#compositing-group-%d\" result=\"source\"/>\n" \
- "<feImage xlink:href=\"#compositing-group-%d\" result=\"destination\"/>\n" \
- "<feComposite in=\"source\" in2=\"destination\" " \
- "operator=\"" operation "\" " \
- "color-interpolation-filters=\"sRGB\"/>\n" \
- "</filter>\n", \
- filter_id, \
- source_compositing_group_id, \
- destination_compositing_group_id)
+ "<filter id=\"filter-%d\" filterUnits=\"userSpaceOnUse\" " \
+ "x=\"0%%\" y=\"0%%\" width=\"100%%\" height=\"100%%\">\n" \
+ "<feImage xlink:href=\"#compositing-group-%d\" result=\"source\"/>\n" \
+ "<feImage xlink:href=\"#compositing-group-%d\" result=\"destination\"/>\n" \
+ "<feComposite in=\"source\" in2=\"destination\" " \
+ "operator=\"" operation "\" " \
+ "color-interpolation-filters=\"sRGB\"/>\n" \
+ "</filter>\n", \
+ filter_id, \
+ source_compositing_group_id, \
+ destination_compositing_group_id)
#define _CAIRO_SVG_SURFACE_OUTPUT_FE_BLEND_FILTER(mode) \
_cairo_output_stream_printf (document->xml_node_filters, \
- "<filter id=\"filter-%d\" filterUnits=\"userSpaceOnUse\" " \
- "x=\"0%%\" y=\"0%%\" width=\"100%%\" height=\"100%%\">\n" \
- "<feImage xlink:href=\"#compositing-group-%d\" result=\"source\"/>\n" \
- "<feImage xlink:href=\"#compositing-group-%d\" result=\"destination\"/>\n" \
- "<feBlend in=\"source\" in2=\"destination\" " \
- "mode=\"" mode "\" " \
- "color-interpolation-filters=\"sRGB\"/>\n" \
- "</filter>\n", \
- filter_id, \
- source_compositing_group_id, \
- destination_compositing_group_id)
+ "<filter id=\"filter-%d\" filterUnits=\"userSpaceOnUse\" " \
+ "x=\"0%%\" y=\"0%%\" width=\"100%%\" height=\"100%%\">\n" \
+ "<feImage xlink:href=\"#compositing-group-%d\" result=\"source\"/>\n" \
+ "<feImage xlink:href=\"#compositing-group-%d\" result=\"destination\"/>\n" \
+ "<feBlend in=\"source\" in2=\"destination\" " \
+ "mode=\"" mode "\" " \
+ "color-interpolation-filters=\"sRGB\"/>\n" \
+ "</filter>\n", \
+ filter_id, \
+ source_compositing_group_id, \
+ destination_compositing_group_id)
static unsigned int
_cairo_svg_surface_emit_parametric_filter (cairo_svg_document_t *document,
@@ -1335,7 +1391,7 @@ _cairo_svg_surface_emit_parametric_filter (cairo_svg_document_t *document,
_CAIRO_SVG_SURFACE_OUTPUT_FE_COMPOSITE_FILTER ("xor");
break;
case CAIRO_SVG_FILTER_ADD:
- // This can also be done with <feComposite operator="lighter"/>, but it is not from SVG 1.1
+ // This can also be done with <feComposite operator="lighter"/>, but it is not from SVG 1.1
_cairo_output_stream_printf (document->xml_node_filters,
"<filter id=\"filter-%d\" filterUnits=\"userSpaceOnUse\" "
"x=\"0%%\" y=\"0%%\" width=\"100%%\" height=\"100%%\">\n"
@@ -1395,7 +1451,7 @@ _cairo_svg_surface_emit_parametric_filter (cairo_svg_document_t *document,
_CAIRO_SVG_SURFACE_OUTPUT_FE_BLEND_FILTER ("luminosity");
break;
default:
- printf("%d\n", filter);
+ printf ("%d\n", filter);
ASSERT_NOT_REACHED;
}
return filter_id;
@@ -1742,7 +1798,8 @@ static cairo_status_t
_cairo_svg_surface_emit_recording_surface (cairo_svg_document_t *document,
cairo_recording_surface_t *source,
unsigned int source_id,
- cairo_bool_t *paint_used)
+ cairo_bool_t *paint_used,
+ cairo_bool_t *transitive_paint_used)
{
cairo_status_t status;
@@ -1827,6 +1884,7 @@ _cairo_svg_surface_emit_recording_surface (cairo_svg_document_t *document,
_cairo_output_stream_printf (document->xml_node_defs, "</g>\n");
*paint_used = svg_surface->paint_used;
+ *transitive_paint_used = svg_surface->transitive_paint_used;
status = cairo_surface_status (paginated_surface);
cairo_surface_destroy (paginated_surface);
@@ -1846,7 +1904,8 @@ _cairo_svg_surface_to_recording_surface (const cairo_surface_pattern_t *pattern)
}
static cairo_bool_t
-_cairo_svg_surface_svg_pattern_should_be_used (const cairo_pattern_t *pattern) {
+_cairo_svg_surface_svg_pattern_should_be_used (const cairo_pattern_t *pattern)
+{
cairo_rectangle_int_t extents;
return pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
pattern->extend == CAIRO_EXTEND_REPEAT &&
@@ -1854,7 +1913,8 @@ _cairo_svg_surface_svg_pattern_should_be_used (const cairo_pattern_t *pattern) {
}
static cairo_bool_t
-_cairo_svg_surface_svg_clip_or_svg_mask_should_be_used (const cairo_pattern_t *pattern) {
+_cairo_svg_surface_svg_clip_or_svg_mask_should_be_used (const cairo_pattern_t *pattern)
+{
return pattern->type == CAIRO_PATTERN_TYPE_SURFACE && !_cairo_svg_surface_svg_pattern_should_be_used (pattern);
}
@@ -1886,30 +1946,34 @@ _cairo_svg_surface_emit_composite_recording_pattern (cairo_output_stream_t *outp
cairo_recording_surface_t *recording_surface = _cairo_svg_surface_to_recording_surface (pattern);
if (is_new) {
+ cairo_bool_t paint_used;
+
status = _cairo_svg_surface_emit_recording_surface (document,
recording_surface,
source_id,
- &source_surface->paint_used);
+ &paint_used,
+ &source_surface->transitive_paint_used);
if (unlikely (status)) {
return status;
}
- }
- if (is_new && source_surface->paint_used) {
- cairo_svg_paint_t *paint_entry = malloc (sizeof (cairo_svg_paint_t));
- if (paint_entry == NULL) {
- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
- }
- paint_entry->source_id = source_id;
- paint_entry->xml_node = _cairo_memory_stream_create();
- _cairo_svg_paint_init_key (paint_entry);
- status = _cairo_hash_table_insert (document->paints, &paint_entry->base);
- if (unlikely (status)) {
- return status;
+ if (source_surface->transitive_paint_used) {
+ cairo_svg_paint_t *paint_entry = malloc (sizeof (cairo_svg_paint_t));
+ if (paint_entry == NULL) {
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ paint_entry->source_id = source_id;
+ paint_entry->paint_used = paint_used;
+ _cairo_array_init (&paint_entry->paint_elements, sizeof (cairo_svg_paint_element_t));
+ _cairo_svg_paint_init_key (paint_entry);
+ status = _cairo_hash_table_insert (document->paints, &paint_entry->base);
+ if (unlikely (status)) {
+ return status;
+ }
}
}
- if (source_surface->paint_used) {
+ if (source_surface->transitive_paint_used) {
cairo_svg_paint_t paint_key;
paint_key.source_id = source_id;
_cairo_svg_paint_init_key (&paint_key);
@@ -1918,25 +1982,26 @@ _cairo_svg_surface_emit_composite_recording_pattern (cairo_output_stream_t *outp
&paint_key.base);
assert (found_paint_entry);
- _cairo_output_stream_printf (found_paint_entry->xml_node,
- "<use xlink:href=\"#paint-%d\"",
- surface->source_id);
- cairo_matrix_t matrix = pattern->base.matrix;
+ cairo_svg_paint_element_t paint_element;
+ paint_element.source_id = surface->source_id;
+ paint_element.matrix = pattern->base.matrix;
if (parent_matrix != NULL) {
cairo_matrix_t parent_matrix_inverse = *parent_matrix;
status = cairo_matrix_invert (&parent_matrix_inverse);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
- cairo_matrix_multiply (&matrix, &parent_matrix_inverse, &matrix);
+ cairo_matrix_multiply (&paint_element.matrix, &parent_matrix_inverse, &paint_element.matrix);
+ }
+ status = _cairo_array_append (&found_paint_entry->paint_elements, &paint_element);
+ if (unlikely (status)) {
+ return status;
}
- _cairo_svg_surface_emit_transform (found_paint_entry->xml_node, "transform", &matrix, NULL);
- _cairo_output_stream_printf (found_paint_entry->xml_node, "/>\n");
- surface->paint_used = TRUE;
+ surface->transitive_paint_used = TRUE;
}
if (pattern_id != invalid_pattern_id) {
- assert (!recording_surface->unbounded);
+ assert (!recording_surface->unbounded);
_cairo_output_stream_printf (output,
"<pattern id=\"pattern-%d\" "
"patternUnits=\"userSpaceOnUse\" "
@@ -2559,6 +2624,8 @@ _cairo_svg_surface_emit_paint (cairo_output_stream_t *output,
}
surface->paint_used = TRUE;
+ surface->transitive_paint_used = TRUE;
+
_cairo_output_stream_printf (output,
"<use xlink:href=\"#paint-%d\"",
surface->source_id);
@@ -2584,10 +2651,10 @@ _cairo_svg_surface_do_operator (cairo_output_stream_t *output,
cairo_svg_document_t *document = surface->document;
if (op == CAIRO_OPERATOR_CLEAR) {
- /*
- * The result is the same as one of the SOURCE operation application with the same arguments,
- * but with an empty source.
- */
+ /*
+ * The result is the same as one of the SOURCE operation application with the same arguments,
+ * but with an empty source.
+ */
status = _cairo_output_stream_destroy (source_stream);
if (unlikely (status)) {
@@ -2704,9 +2771,9 @@ _cairo_svg_surface_do_operator (cairo_output_stream_t *output,
}
if (op == CAIRO_OPERATOR_DEST) {
- /*
- * The result is the destination.
- */
+ /*
+ * The result is the destination.
+ */
_cairo_memory_stream_copy (destination_stream, surface->xml_node);
status = _cairo_output_stream_destroy (destination_stream);
@@ -3750,13 +3817,33 @@ _cairo_svg_document_finish (cairo_svg_document_t *document)
final_status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
}
+
+ if (surface->transitive_paint_used) {
+ cairo_svg_paint_t *paint_entry = malloc (sizeof (cairo_svg_paint_t));
+ if (paint_entry == NULL) {
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ paint_entry->source_id = surface->source_id;
+ paint_entry->paint_used = surface->paint_used;
+ paint_entry->box.p1.x = 0;
+ paint_entry->box.p1.y = 0;
+ paint_entry->box.p2.x = document->width;
+ paint_entry->box.p2.y = document->height;
+ _cairo_array_init (&paint_entry->paint_elements, sizeof (cairo_svg_paint_element_t));
+ _cairo_svg_paint_init_key (paint_entry);
+ status = _cairo_hash_table_insert (document->paints, &paint_entry->base);
+ if (unlikely (status)) {
+ return status;
+ }
+ }
}
+ _cairo_hash_table_foreach (document->paints, _cairo_svg_paint_compute_func, document);
+
if (_cairo_memory_stream_length (document->xml_node_filters) > 0 ||
_cairo_memory_stream_length (document->xml_node_glyphs) > 0 ||
_cairo_memory_stream_length (document->xml_node_defs) > 0 ||
- _cairo_hash_table_size (document->paints) != 0 ||
- (surface != NULL && surface->paint_used)) {
+ _cairo_hash_table_size (document->paints) != 0) {
_cairo_output_stream_printf (output, "<defs>\n");
_cairo_memory_stream_copy (document->xml_node_filters, output);
if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0) {
@@ -3765,13 +3852,7 @@ _cairo_svg_document_finish (cairo_svg_document_t *document)
_cairo_output_stream_printf (output, "</g>\n");
}
_cairo_memory_stream_copy (document->xml_node_defs, output);
- _cairo_hash_table_foreach (document->paints, _cairo_svg_paint_emit, output);
- if (surface != NULL && surface->paint_used) {
- _cairo_output_stream_printf (output,
- "<rect id=\"paint-%d\" x=\"0\" y=\"0\" width=\"%f\" height=\"%f\"/>\n",
- surface->source_id,
- document->width, document->height);
- }
+ _cairo_hash_table_foreach (document->paints, _cairo_svg_paint_emit_func, output);
_cairo_output_stream_printf (output, "</defs>\n");
}
diff --git a/test/Makefile.sources b/test/Makefile.sources
index bb999169b..466d27cb8 100644
--- a/test/Makefile.sources
+++ b/test/Makefile.sources
@@ -281,6 +281,7 @@ test_sources = \
record-neg-extents.c \
record-mesh.c \
record-replay-extend.c \
+ record-transform-paint.c \
recording-ink-extents.c \
recording-surface-pattern.c \
recording-surface-extend.c \
diff --git a/test/meson.build b/test/meson.build
index cc5160876..c5cca669e 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -281,6 +281,7 @@ test_sources = [
'record-neg-extents.c',
'record-mesh.c',
'record-replay-extend.c',
+ 'record-transform-paint.c',
'recording-ink-extents.c',
'recording-surface-pattern.c',
'recording-surface-extend.c',
diff --git a/test/record-transform-paint.c b/test/record-transform-paint.c
new file mode 100644
index 000000000..0ccc2277f
--- /dev/null
+++ b/test/record-transform-paint.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright © 2021 Anton Danilkin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+ cairo_surface_t *surface2 = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
+ {
+ cairo_t *cr2 = cairo_create (surface2);
+ cairo_surface_t *surface3 = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
+ {
+ cairo_t *cr3 = cairo_create (surface3);
+ cairo_pattern_t *pattern4 = cairo_pattern_create_linear (0.0, 0.0, width, height);
+ cairo_pattern_add_color_stop_rgb (pattern4, 0, 0, 1, 0);
+ cairo_pattern_add_color_stop_rgb (pattern4, 1, 0, 0, 1);
+ cairo_set_source (cr3, pattern4);
+ cairo_paint (cr3);
+ }
+ cairo_pattern_t *pattern3 = cairo_pattern_create_for_surface (surface3);
+ cairo_matrix_t matrix3;
+ cairo_matrix_init_scale (&matrix3, 2, 2);
+ cairo_pattern_set_matrix (pattern3, &matrix3);
+ cairo_set_source (cr2, pattern3);
+ cairo_paint (cr2);
+ }
+ cairo_pattern_t *pattern2 = cairo_pattern_create_for_surface (surface2);
+ cairo_matrix_t matrix2;
+ cairo_matrix_init_scale (&matrix2, 5, 5);
+ cairo_pattern_set_matrix (pattern2, &matrix2);
+ cairo_set_source (cr, pattern2);
+ cairo_paint (cr);
+
+ return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (record_transform_paint,
+ "Tests paint in nested transformed recording patterns",
+ "record, paint", /* keywords */
+ NULL, /* requirements */
+ 512, 512,
+ NULL, draw)
diff --git a/test/reference/record-transform-paint.ref.png b/test/reference/record-transform-paint.ref.png
new file mode 100644
index 000000000..051db262e
--- /dev/null
+++ b/test/reference/record-transform-paint.ref.png
Binary files differ