1/*
2 * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB.  If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21#include "config.h"
22#include "SVGGraphicsElement.h"
23
24#include "AffineTransform.h"
25#include "Attribute.h"
26#include "RenderSVGPath.h"
27#include "RenderSVGResource.h"
28#include "SVGElementInstance.h"
29#include "SVGNames.h"
30#include "SVGPathData.h"
31#include <wtf/NeverDestroyed.h>
32
33namespace WebCore {
34
35// Animated property definitions
36DEFINE_ANIMATED_TRANSFORM_LIST(SVGGraphicsElement, SVGNames::transformAttr, Transform, transform)
37
38BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGGraphicsElement)
39    REGISTER_LOCAL_ANIMATED_PROPERTY(transform)
40    REGISTER_PARENT_ANIMATED_PROPERTIES(SVGElement)
41    REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTests)
42END_REGISTER_ANIMATED_PROPERTIES
43
44SVGGraphicsElement::SVGGraphicsElement(const QualifiedName& tagName, Document& document)
45    : SVGElement(tagName, document)
46    , m_shouldIsolateBlending(false)
47{
48    registerAnimatedPropertiesForSVGGraphicsElement();
49}
50
51SVGGraphicsElement::~SVGGraphicsElement()
52{
53}
54
55AffineTransform SVGGraphicsElement::getCTM(StyleUpdateStrategy styleUpdateStrategy)
56{
57    return SVGLocatable::computeCTM(this, SVGLocatable::NearestViewportScope, styleUpdateStrategy);
58}
59
60AffineTransform SVGGraphicsElement::getScreenCTM(StyleUpdateStrategy styleUpdateStrategy)
61{
62    return SVGLocatable::computeCTM(this, SVGLocatable::ScreenScope, styleUpdateStrategy);
63}
64
65AffineTransform SVGGraphicsElement::animatedLocalTransform() const
66{
67    AffineTransform matrix;
68    RenderStyle* style = renderer() ? &renderer()->style() : nullptr;
69
70    // If CSS property was set, use that, otherwise fallback to attribute (if set).
71    if (style && style->hasTransform()) {
72        // Note: objectBoundingBox is an emptyRect for elements like pattern or clipPath.
73        // See the "Object bounding box units" section of http://dev.w3.org/csswg/css3-transforms/
74        TransformationMatrix transform;
75        style->applyTransform(transform, renderer()->objectBoundingBox());
76
77        // Flatten any 3D transform.
78        matrix = transform.toAffineTransform();
79        // CSS bakes the zoom factor into lengths, including translation components.
80        // In order to align CSS & SVG transforms, we need to invert this operation.
81        float zoom = style->effectiveZoom();
82        if (zoom != 1) {
83            matrix.setE(matrix.e() / zoom);
84            matrix.setF(matrix.f() / zoom);
85        }
86
87    } else
88        transform().concatenate(matrix);
89
90    if (m_supplementalTransform)
91        return *m_supplementalTransform * matrix;
92    return matrix;
93}
94
95AffineTransform* SVGGraphicsElement::supplementalTransform()
96{
97    if (!m_supplementalTransform)
98        m_supplementalTransform = std::make_unique<AffineTransform>();
99    return m_supplementalTransform.get();
100}
101
102bool SVGGraphicsElement::isSupportedAttribute(const QualifiedName& attrName)
103{
104    static NeverDestroyed<HashSet<QualifiedName>> supportedAttributes;
105    if (supportedAttributes.get().isEmpty()) {
106        SVGTests::addSupportedAttributes(supportedAttributes);
107        supportedAttributes.get().add(SVGNames::transformAttr);
108    }
109    return supportedAttributes.get().contains<SVGAttributeHashTranslator>(attrName);
110}
111
112void SVGGraphicsElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
113{
114    if (!isSupportedAttribute(name)) {
115        SVGElement::parseAttribute(name, value);
116        return;
117    }
118
119    if (name == SVGNames::transformAttr) {
120        SVGTransformList newList;
121        newList.parse(value);
122        detachAnimatedTransformListWrappers(newList.size());
123        setTransformBaseValue(newList);
124        return;
125    }
126
127    if (SVGTests::parseAttribute(name, value))
128        return;
129
130    ASSERT_NOT_REACHED();
131}
132
133void SVGGraphicsElement::svgAttributeChanged(const QualifiedName& attrName)
134{
135    if (!isSupportedAttribute(attrName)) {
136        SVGElement::svgAttributeChanged(attrName);
137        return;
138    }
139
140    SVGElementInstance::InvalidationGuard invalidationGuard(this);
141
142    if (SVGTests::handleAttributeChange(this, attrName))
143        return;
144
145    auto renderer = this->renderer();
146    if (!renderer)
147        return;
148
149    if (attrName == SVGNames::transformAttr) {
150        renderer->setNeedsTransformUpdate();
151        RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
152        return;
153    }
154
155    ASSERT_NOT_REACHED();
156}
157
158SVGElement* SVGGraphicsElement::nearestViewportElement() const
159{
160    return SVGTransformable::nearestViewportElement(this);
161}
162
163SVGElement* SVGGraphicsElement::farthestViewportElement() const
164{
165    return SVGTransformable::farthestViewportElement(this);
166}
167
168FloatRect SVGGraphicsElement::getBBox(StyleUpdateStrategy styleUpdateStrategy)
169{
170    return SVGTransformable::getBBox(this, styleUpdateStrategy);
171}
172
173RenderPtr<RenderElement> SVGGraphicsElement::createElementRenderer(PassRef<RenderStyle> style)
174{
175    // By default, any subclass is expected to do path-based drawing
176    return createRenderer<RenderSVGPath>(*this, WTF::move(style));
177}
178
179void SVGGraphicsElement::toClipPath(Path& path)
180{
181    updatePathFromGraphicsElement(this, path);
182    // FIXME: How do we know the element has done a layout?
183    path.transform(animatedLocalTransform());
184}
185
186}
187