diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc | 91 |
1 files changed, 69 insertions, 22 deletions
diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc index 66bff8d5bd1..ca7059737f1 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc @@ -69,6 +69,19 @@ void LayoutSVGShape::StyleDidChange(StyleDifference diff, TransformHelper::DependsOnReferenceBox(StyleRef()); LayoutSVGModelObject::StyleDidChange(diff, old_style); SVGResources::UpdatePaints(*GetElement(), old_style, StyleRef()); + + // Most of the stroke attributes (caps, joins, miters, width, etc.) will cause + // a re-layout which will clear the stroke-path cache; however, there are a + // couple of additional properties that *won't* cause a layout, but are + // significant enough to require invalidating the cache. + if (!diff.NeedsFullLayout() && old_style && stroke_path_cache_) { + const auto& old_svg_style = old_style->SvgStyle(); + const auto& svg_style = StyleRef().SvgStyle(); + if (old_svg_style.StrokeDashOffset() != svg_style.StrokeDashOffset() || + *old_svg_style.StrokeDashArray() != *svg_style.StrokeDashArray()) { + stroke_path_cache_.reset(); + } + } } void LayoutSVGShape::WillBeDestroyed() { @@ -76,10 +89,20 @@ void LayoutSVGShape::WillBeDestroyed() { LayoutSVGModelObject::WillBeDestroyed(); } +void LayoutSVGShape::ClearPath() { + path_.reset(); + stroke_path_cache_.reset(); +} + void LayoutSVGShape::CreatePath() { if (!path_) path_ = std::make_unique<Path>(); *path_ = To<SVGGeometryElement>(GetElement())->AsPath(); + + // When the path changes, we need to ensure the stale stroke path cache is + // cleared. Because this is done in all callsites, we can just DCHECK that it + // has been cleared here. + DCHECK(!stroke_path_cache_); } float LayoutSVGShape::DashScaleFactor() const { @@ -150,24 +173,40 @@ FloatRect LayoutSVGShape::HitTestStrokeBoundingBox() const { bool LayoutSVGShape::ShapeDependentStrokeContains( const HitTestLocation& location) { - // In case the subclass didn't create path during UpdateShapeFromElement() - // for optimization but still calls this method. - if (!HasPath()) - CreatePath(); - - StrokeData stroke_data; - SVGLayoutSupport::ApplyStrokeStyleToStrokeData(stroke_data, StyleRef(), *this, - DashScaleFactor()); - - if (HasNonScalingStroke()) { - // The reason is similar to the above code about HasPath(). - if (!rare_data_) - UpdateNonScalingStrokeData(); - return NonScalingStrokePath().StrokeContains( - NonScalingStrokeTransform().MapPoint(location.TransformedPoint()), - stroke_data); + if (!stroke_path_cache_) { + // In case the subclass didn't create path during UpdateShapeFromElement() + // for optimization but still calls this method. + if (!HasPath()) + CreatePath(); + + StrokeData stroke_data; + SVGLayoutSupport::ApplyStrokeStyleToStrokeData(stroke_data, StyleRef(), + *this, DashScaleFactor()); + + if (HasNonScalingStroke()) { + // The reason is similar to the above code about HasPath(). + if (!rare_data_) + UpdateNonScalingStrokeData(); + + // Un-scale to get back to the root-transform (cheaper than re-computing + // the root transform from scratch). + AffineTransform root_transform; + root_transform.Scale(StyleRef().EffectiveZoom()) + .Multiply(NonScalingStrokeTransform()); + + stroke_path_cache_ = std::make_unique<Path>( + NonScalingStrokePath().StrokePath(stroke_data, root_transform)); + } else { + stroke_path_cache_ = std::make_unique<Path>( + path_->StrokePath(stroke_data, ComputeRootTransform())); + } } - return path_->StrokeContains(location.TransformedPoint(), stroke_data); + + DCHECK(stroke_path_cache_); + auto point = location.TransformedPoint(); + if (HasNonScalingStroke()) + point = NonScalingStrokeTransform().MapPoint(point); + return stroke_path_cache_->Contains(point); } bool LayoutSVGShape::ShapeDependentFillContains( @@ -217,6 +256,10 @@ void LayoutSVGShape::UpdateLayout() { if (EverHadLayout() && SelfNeedsLayout()) SVGResourcesCache::ClientLayoutChanged(*this); + // The cached stroke may be affected by the ancestor transform, and so needs + // to be cleared regardless of whether the shape or bounds have changed. + stroke_path_cache_.reset(); + bool update_parent_boundaries = false; bool bbox_changed = false; // UpdateShapeFromElement() also updates the object & stroke bounds - which @@ -264,19 +307,23 @@ void LayoutSVGShape::UpdateLayout() { ClearNeedsLayout(); } +AffineTransform LayoutSVGShape::ComputeRootTransform() const { + const LayoutObject* root = this; + while (root && !root->IsSVGRoot()) + root = root->Parent(); + return LocalToAncestorTransform(ToLayoutSVGRoot(root)).ToAffineTransform(); +} + AffineTransform LayoutSVGShape::ComputeNonScalingStrokeTransform() const { // Compute the CTM to the SVG root. This should probably be the CTM all the // way to the "canvas" of the page ("host" coordinate system), but with our // current approach of applying/painting non-scaling-stroke, that can break in // unpleasant ways (see crbug.com/747708 for an example.) Maybe it would be // better to apply this effect during rasterization? - const LayoutObject* root = this; - while (root && !root->IsSVGRoot()) - root = root->Parent(); AffineTransform host_transform; host_transform.Scale(1 / StyleRef().EffectiveZoom()) - .Multiply( - LocalToAncestorTransform(ToLayoutSVGRoot(root)).ToAffineTransform()); + .Multiply(ComputeRootTransform()); + // Width of non-scaling stroke is independent of translation, so zero it out // here. host_transform.SetE(0); |