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