From dcfb2d692ccd13e68855eb423eb115d44d0e37df Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 15 Feb 2024 11:46:17 +0000 Subject: [PATCH] SVG - improve handling of gradientUnits and gradientTransform attributes (radial gradient) + correct default values [skip ci] --- include/mapnik/svg/svg_renderer_agg.hpp | 26 ++++++++++++++++--------- src/gradient.cpp | 4 ++-- src/svg/svg_parser.cpp | 26 ++++++++++++------------- test/data-visual | 2 +- 4 files changed, 33 insertions(+), 25 deletions(-) diff --git a/include/mapnik/svg/svg_renderer_agg.hpp b/include/mapnik/svg/svg_renderer_agg.hpp index 48f45c1d0..8afed5c74 100644 --- a/include/mapnik/svg/svg_renderer_agg.hpp +++ b/include/mapnik/svg/svg_renderer_agg.hpp @@ -64,6 +64,8 @@ MAPNIK_DISABLE_WARNING_PUSH #include "agg_span_interpolator_linear.h" MAPNIK_DISABLE_WARNING_POP +#include + namespace mapnik { namespace svg { @@ -222,16 +224,14 @@ class renderer_agg : util::noncopyable } if (m_gradient_lut.build_lut()) { - agg::trans_affine transform = mtx; - double scale = mtx.scale(); - transform.invert(); - agg::trans_affine tr; - tr = grad.get_transform(); - tr.invert(); + agg::trans_affine tr = mtx; + agg::trans_affine transform = grad.get_transform(); transform *= tr; + transform.invert(); if (grad.get_units() != USER_SPACE_ON_USE) { + double scale = mtx.scale(); double bx1 = symbol_bbox.minx(); double by1 = symbol_bbox.miny(); double bx2 = symbol_bbox.maxx(); @@ -244,7 +244,17 @@ class renderer_agg : util::noncopyable transform.translate(-bx1 / scale, -by1 / scale); transform.scale(scale / (bx2 - bx1), scale / (by2 - by1)); } - + else + { + double scaledown = 255; + x1 /= scaledown; // fx + y1 /= scaledown; // fy + x2 /= scaledown; // cx + y2 /= scaledown; // cy + radius /= scaledown; + transform.translate(-symbol_bbox.minx(), -symbol_bbox.miny()); + transform.scale(1 / scaledown, 1 / scaledown); + } if (grad.get_gradient_type() == RADIAL) { using gradient_adaptor_type = agg::gradient_radial_focus; @@ -253,7 +263,6 @@ class renderer_agg : util::noncopyable // the agg radial gradient assumes it is centred on 0 transform.translate(-x2, -y2); - // scale everything up since agg turns things into integers a bit too soon int scaleup = 255; radius *= scaleup; @@ -261,7 +270,6 @@ class renderer_agg : util::noncopyable y1 *= scaleup; x2 *= scaleup; y2 *= scaleup; - transform.scale(scaleup, scaleup); interpolator_type span_interpolator(transform); diff --git a/src/gradient.cpp b/src/gradient.cpp index 6984d8b60..6d46960d9 100644 --- a/src/gradient.cpp +++ b/src/gradient.cpp @@ -70,8 +70,8 @@ gradient& gradient::operator=(gradient rhs) bool gradient::operator==(gradient const& other) const { return transform_ == other.transform_ && x1_ == other.x1_ && y1_ == other.y1_ && x2_ == other.x2_ && - y2_ == other.y2_ && r_ == other.r_ && std::equal(stops_.begin(), stops_.end(), other.stops_.begin()) && - units_ == other.units_ && gradient_type_ == other.gradient_type_; + y2_ == other.y2_ && r_ == other.r_ && std::equal(stops_.begin(), stops_.end(), other.stops_.begin()) && + units_ == other.units_ && gradient_type_ == other.gradient_type_; } void gradient::set_gradient_type(gradient_e grad) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index db94d3756..c67cf4466 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -1533,8 +1533,8 @@ void parse_radial_gradient(svg_parser& parser, rapidxml::xml_node const* n return; double cx = 0.5; double cy = 0.5; - double fx = 0.0; - double fy = 0.0; + double fx = 0.5; + double fy = 0.5; double r = 0.5; bool has_percent = false; @@ -1543,12 +1543,20 @@ void parse_radial_gradient(svg_parser& parser, rapidxml::xml_node const* n { cx = parse_svg_value(parser, attr->value(), has_percent); } + else if (gr.get_units() == USER_SPACE_ON_USE) + { + cx = 0.5 * (parser.vbox_ ? parser.vbox_->width : parser.path_.width()); // 50% + } attr = node->first_attribute("cy"); if (attr != nullptr) { cy = parse_svg_value(parser, attr->value(), has_percent); } + else if (gr.get_units() == USER_SPACE_ON_USE) + { + cy = 0.5 * (parser.vbox_ ? parser.vbox_->height : parser.path_.height()); // 50% + } attr = node->first_attribute("fx"); if (attr != nullptr) @@ -1573,19 +1581,11 @@ void parse_radial_gradient(svg_parser& parser, rapidxml::xml_node const* n { r = parse_svg_value(parser, attr->value(), has_percent); } - // this logic for detecting %'s will not support mixed coordinates. - if (gr.get_units() == USER_SPACE_ON_USE) + else if (gr.get_units() == USER_SPACE_ON_USE) { - if (!has_percent && parser.path_.width() > 0 && parser.path_.height() > 0) - { - fx /= parser.path_.width(); - fy /= parser.path_.height(); - cx /= parser.path_.width(); - cy /= parser.path_.height(); - r /= parser.path_.width(); - } - gr.set_units(USER_SPACE_ON_USE_BOUNDING_BOX); + r = 0.5 * parser.normalized_diagonal_; // 50% } + gr.set_gradient_type(RADIAL); gr.set_control_points(fx, fy, cx, cy, r); diff --git a/test/data-visual b/test/data-visual index ac363ee88..540561deb 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit ac363ee887d2a55039fd19390c186663d8f0f206 +Subproject commit 540561debb38280deb1b9598de1724e0ebd8df49