diff options
-rw-r--r-- | src/cairo-svg-surface-private.h | 1 | ||||
-rw-r--r-- | src/cairo-svg-surface.c | 231 | ||||
-rw-r--r-- | test/Makefile.sources | 1 | ||||
-rw-r--r-- | test/meson.build | 1 | ||||
-rw-r--r-- | test/record-transform-paint.c | 64 | ||||
-rw-r--r-- | test/reference/record-transform-paint.ref.png | bin | 0 -> 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 Binary files differnew file mode 100644 index 000000000..051db262e --- /dev/null +++ b/test/reference/record-transform-paint.ref.png |