| /* |
| * Copyright (C) 2004, 2005, 2006, 2008 Nikolas Zimmermann <zimmermann@kde.org> |
| * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "third_party/blink/renderer/core/svg/svg_path_element.h" |
| |
| #include "third_party/blink/renderer/core/dom/node_computed_style.h" |
| #include "third_party/blink/renderer/core/layout/layout_object.h" |
| #include "third_party/blink/renderer/core/svg/svg_animated_path.h" |
| #include "third_party/blink/renderer/core/svg/svg_mpath_element.h" |
| #include "third_party/blink/renderer/core/svg/svg_path_query.h" |
| #include "third_party/blink/renderer/core/svg/svg_path_utilities.h" |
| #include "third_party/blink/renderer/core/svg/svg_point_tear_off.h" |
| #include "third_party/blink/renderer/platform/heap/heap.h" |
| |
| namespace blink { |
| |
| SVGPathElement::SVGPathElement(Document& document) |
| : SVGGeometryElement(svg_names::kPathTag, document), |
| path_(MakeGarbageCollected<SVGAnimatedPath>(this, |
| svg_names::kDAttr, |
| CSSPropertyID::kD)) { |
| AddToPropertyMap(path_); |
| } |
| |
| void SVGPathElement::Trace(Visitor* visitor) const { |
| visitor->Trace(path_); |
| SVGGeometryElement::Trace(visitor); |
| } |
| |
| Path SVGPathElement::AttributePath() const { |
| return path_->CurrentValue()->GetStylePath()->GetPath(); |
| } |
| |
| const StylePath* SVGPathElement::GetStylePath() const { |
| if (const ComputedStyle* style = GetComputedStyle()) { |
| if (const StylePath* style_path = style->D()) |
| return style_path; |
| return StylePath::EmptyPath(); |
| } |
| return path_->CurrentValue()->GetStylePath(); |
| } |
| |
| float SVGPathElement::ComputePathLength() const { |
| return GetStylePath()->length(); |
| } |
| |
| const SVGPathByteStream& SVGPathElement::PathByteStream() const { |
| return GetStylePath()->ByteStream(); |
| } |
| |
| Path SVGPathElement::AsPath() const { |
| return GetStylePath()->GetPath(); |
| } |
| |
| float SVGPathElement::getTotalLength(ExceptionState& exception_state) { |
| GetDocument().UpdateStyleAndLayoutForNode(this, |
| DocumentUpdateReason::kJavaScript); |
| return SVGPathQuery(PathByteStream()).GetTotalLength(); |
| } |
| |
| SVGPointTearOff* SVGPathElement::getPointAtLength( |
| float length, |
| ExceptionState& exception_state) { |
| GetDocument().UpdateStyleAndLayoutForNode(this, |
| DocumentUpdateReason::kJavaScript); |
| |
| const SVGPathByteStream& byte_stream = PathByteStream(); |
| if (byte_stream.IsEmpty()) { |
| exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, |
| "The element's path is empty."); |
| return nullptr; |
| } |
| |
| SVGPathQuery path_query(byte_stream); |
| if (length < 0) { |
| length = 0; |
| } else { |
| float computed_length = path_query.GetTotalLength(); |
| if (length > computed_length) |
| length = computed_length; |
| } |
| FloatPoint point = path_query.GetPointAtLength(length); |
| return SVGPointTearOff::CreateDetached(point); |
| } |
| |
| void SVGPathElement::SvgAttributeChanged( |
| const SvgAttributeChangedParams& params) { |
| const QualifiedName& attr_name = params.name; |
| if (attr_name == svg_names::kDAttr) { |
| InvalidateMPathDependencies(); |
| GeometryPresentationAttributeChanged(attr_name); |
| return; |
| } |
| |
| SVGGeometryElement::SvgAttributeChanged(params); |
| } |
| |
| void SVGPathElement::CollectStyleForPresentationAttribute( |
| const QualifiedName& name, |
| const AtomicString& value, |
| MutableCSSPropertyValueSet* style) { |
| SVGAnimatedPropertyBase* property = PropertyFromAttribute(name); |
| if (property == path_) { |
| SVGAnimatedPath* path = GetPath(); |
| // If this is a <use> instance, return the referenced path to maximize |
| // geometry sharing. |
| if (const SVGElement* element = CorrespondingElement()) |
| path = To<SVGPathElement>(element)->GetPath(); |
| AddPropertyToPresentationAttributeStyle(style, property->CssPropertyId(), |
| path->CssValue()); |
| return; |
| } |
| SVGGeometryElement::CollectStyleForPresentationAttribute(name, value, style); |
| } |
| |
| void SVGPathElement::InvalidateMPathDependencies() { |
| // <mpath> can only reference <path> but this dependency is not handled in |
| // markForLayoutAndParentResourceInvalidation so we update any mpath |
| // dependencies manually. |
| if (SVGElementSet* dependencies = SetOfIncomingReferences()) { |
| for (SVGElement* element : *dependencies) { |
| if (auto* mpath = DynamicTo<SVGMPathElement>(*element)) |
| mpath->TargetPathChanged(); |
| } |
| } |
| } |
| |
| Node::InsertionNotificationRequest SVGPathElement::InsertedInto( |
| ContainerNode& root_parent) { |
| SVGGeometryElement::InsertedInto(root_parent); |
| InvalidateMPathDependencies(); |
| return kInsertionDone; |
| } |
| |
| void SVGPathElement::RemovedFrom(ContainerNode& root_parent) { |
| SVGGeometryElement::RemovedFrom(root_parent); |
| InvalidateMPathDependencies(); |
| } |
| |
| FloatRect SVGPathElement::GetBBox() { |
| // We want the exact bounds. |
| return SVGPathElement::AsPath().TightBoundingRect(); |
| } |
| |
| } // namespace blink |