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 23#include "core/svg/SVGGraphicsElement.h" 24 25#include "SVGNames.h" 26#include "core/rendering/svg/RenderSVGPath.h" 27#include "core/rendering/svg/RenderSVGResource.h" 28#include "core/rendering/svg/SVGPathData.h" 29#include "core/svg/SVGElementInstance.h" 30#include "platform/transforms/AffineTransform.h" 31 32namespace WebCore { 33 34// Animated property definitions 35DEFINE_ANIMATED_TRANSFORM_LIST(SVGGraphicsElement, SVGNames::transformAttr, Transform, transform) 36 37BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGGraphicsElement) 38 REGISTER_LOCAL_ANIMATED_PROPERTY(transform) 39 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGElement) 40 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTests) 41END_REGISTER_ANIMATED_PROPERTIES 42 43SVGGraphicsElement::SVGGraphicsElement(const QualifiedName& tagName, Document& document, ConstructionType constructionType) 44 : SVGElement(tagName, document, constructionType) 45{ 46 registerAnimatedPropertiesForSVGGraphicsElement(); 47} 48 49SVGGraphicsElement::~SVGGraphicsElement() 50{ 51} 52 53AffineTransform SVGGraphicsElement::getTransformToElement(SVGElement* target, ExceptionState& exceptionState) 54{ 55 AffineTransform ctm = getCTM(AllowStyleUpdate); 56 57 if (target && target->isSVGGraphicsElement()) { 58 AffineTransform targetCTM = toSVGGraphicsElement(target)->getCTM(AllowStyleUpdate); 59 if (!targetCTM.isInvertible()) { 60 exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError); 61 return ctm; 62 } 63 ctm = targetCTM.inverse() * ctm; 64 } 65 66 return ctm; 67} 68 69static AffineTransform computeCTM(SVGGraphicsElement* element, SVGElement::CTMScope mode, SVGGraphicsElement::StyleUpdateStrategy styleUpdateStrategy) 70{ 71 ASSERT(element); 72 if (styleUpdateStrategy == SVGGraphicsElement::AllowStyleUpdate) 73 element->document().updateLayoutIgnorePendingStylesheets(); 74 75 AffineTransform ctm; 76 77 SVGElement* stopAtElement = mode == SVGGraphicsElement::NearestViewportScope ? element->nearestViewportElement() : 0; 78 for (Element* currentElement = element; currentElement; currentElement = currentElement->parentOrShadowHostElement()) { 79 if (!currentElement->isSVGElement()) 80 break; 81 82 ctm = toSVGElement(currentElement)->localCoordinateSpaceTransform(mode).multiply(ctm); 83 84 // For getCTM() computation, stop at the nearest viewport element 85 if (currentElement == stopAtElement) 86 break; 87 } 88 89 return ctm; 90} 91 92AffineTransform SVGGraphicsElement::getCTM(StyleUpdateStrategy styleUpdateStrategy) 93{ 94 return computeCTM(this, NearestViewportScope, styleUpdateStrategy); 95} 96 97AffineTransform SVGGraphicsElement::getScreenCTM(StyleUpdateStrategy styleUpdateStrategy) 98{ 99 return computeCTM(this, ScreenScope, styleUpdateStrategy); 100} 101 102AffineTransform SVGGraphicsElement::animatedLocalTransform() const 103{ 104 AffineTransform matrix; 105 RenderStyle* style = renderer() ? renderer()->style() : 0; 106 107 // If CSS property was set, use that, otherwise fallback to attribute (if set). 108 if (style && style->hasTransform()) { 109 // Note: objectBoundingBox is an emptyRect for elements like pattern or clipPath. 110 // See the "Object bounding box units" section of http://dev.w3.org/csswg/css3-transforms/ 111 TransformationMatrix transform; 112 style->applyTransform(transform, renderer()->objectBoundingBox()); 113 114 // Flatten any 3D transform. 115 matrix = transform.toAffineTransform(); 116 117 // CSS bakes the zoom factor into lengths, including translation components. 118 // In order to align CSS & SVG transforms, we need to invert this operation. 119 float zoom = style->effectiveZoom(); 120 if (zoom != 1) { 121 matrix.setE(matrix.e() / zoom); 122 matrix.setF(matrix.f() / zoom); 123 } 124 } else { 125 transformCurrentValue().concatenate(matrix); 126 } 127 128 if (m_supplementalTransform) 129 return *m_supplementalTransform * matrix; 130 return matrix; 131} 132 133AffineTransform* SVGGraphicsElement::supplementalTransform() 134{ 135 if (!m_supplementalTransform) 136 m_supplementalTransform = adoptPtr(new AffineTransform); 137 return m_supplementalTransform.get(); 138} 139 140bool SVGGraphicsElement::isSupportedAttribute(const QualifiedName& attrName) 141{ 142 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ()); 143 if (supportedAttributes.isEmpty()) { 144 SVGTests::addSupportedAttributes(supportedAttributes); 145 supportedAttributes.add(SVGNames::transformAttr); 146 } 147 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName); 148} 149 150void SVGGraphicsElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 151{ 152 if (!isSupportedAttribute(name)) { 153 SVGElement::parseAttribute(name, value); 154 return; 155 } 156 157 if (name == SVGNames::transformAttr) { 158 SVGTransformList newList; 159 newList.parse(value); 160 detachAnimatedTransformListWrappers(newList.size()); 161 setTransformBaseValue(newList); 162 return; 163 } else if (SVGTests::parseAttribute(name, value)) { 164 return; 165 } 166 167 ASSERT_NOT_REACHED(); 168} 169 170void SVGGraphicsElement::svgAttributeChanged(const QualifiedName& attrName) 171{ 172 if (!isSupportedAttribute(attrName)) { 173 SVGElement::svgAttributeChanged(attrName); 174 return; 175 } 176 177 SVGElementInstance::InvalidationGuard invalidationGuard(this); 178 179 // Reattach so the isValid() check will be run again during renderer creation. 180 if (SVGTests::isKnownAttribute(attrName)) { 181 lazyReattachIfAttached(); 182 return; 183 } 184 185 RenderObject* object = renderer(); 186 if (!object) 187 return; 188 189 if (attrName == SVGNames::transformAttr) { 190 object->setNeedsTransformUpdate(); 191 RenderSVGResource::markForLayoutAndParentResourceInvalidation(object); 192 return; 193 } 194 195 ASSERT_NOT_REACHED(); 196} 197 198static bool isViewportElement(Node* node) 199{ 200 return (node->hasTagName(SVGNames::svgTag) 201 || node->hasTagName(SVGNames::symbolTag) 202 || node->hasTagName(SVGNames::foreignObjectTag) 203 || node->hasTagName(SVGNames::imageTag)); 204} 205 206SVGElement* SVGGraphicsElement::nearestViewportElement() const 207{ 208 for (Element* current = parentOrShadowHostElement(); current; current = current->parentOrShadowHostElement()) { 209 if (isViewportElement(current)) 210 return toSVGElement(current); 211 } 212 213 return 0; 214} 215 216SVGElement* SVGGraphicsElement::farthestViewportElement() const 217{ 218 SVGElement* farthest = 0; 219 for (Element* current = parentOrShadowHostElement(); current; current = current->parentOrShadowHostElement()) { 220 if (isViewportElement(current)) 221 farthest = toSVGElement(current); 222 } 223 return farthest; 224} 225 226SVGRect SVGGraphicsElement::getBBox() 227{ 228 document().updateLayoutIgnorePendingStylesheets(); 229 230 // FIXME: Eventually we should support getBBox for detached elements. 231 if (!renderer()) 232 return SVGRect(); 233 234 return renderer()->objectBoundingBox(); 235} 236 237SVGRect SVGGraphicsElement::getStrokeBBox() 238{ 239 document().updateLayoutIgnorePendingStylesheets(); 240 241 // FIXME: Eventually we should support getStrokeBBox for detached elements. 242 if (!renderer()) 243 return SVGRect(); 244 245 return renderer()->strokeBoundingBox(); 246} 247 248RenderObject* SVGGraphicsElement::createRenderer(RenderStyle*) 249{ 250 // By default, any subclass is expected to do path-based drawing 251 return new RenderSVGPath(this); 252} 253 254void SVGGraphicsElement::toClipPath(Path& path) 255{ 256 updatePathFromGraphicsElement(this, path); 257 // FIXME: How do we know the element has done a layout? 258 path.transform(animatedLocalTransform()); 259} 260 261} 262