Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann (at) kde.org>
      3  * Copyright (C) 2004, 2005, 2006 Rob Buis <buis (at) 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 
     32 namespace WebCore {
     33 
     34 // Animated property definitions
     35 DEFINE_ANIMATED_TRANSFORM_LIST(SVGGraphicsElement, SVGNames::transformAttr, Transform, transform)
     36 
     37 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGGraphicsElement)
     38     REGISTER_LOCAL_ANIMATED_PROPERTY(transform)
     39     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGElement)
     40     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTests)
     41 END_REGISTER_ANIMATED_PROPERTIES
     42 
     43 SVGGraphicsElement::SVGGraphicsElement(const QualifiedName& tagName, Document& document, ConstructionType constructionType)
     44     : SVGElement(tagName, document, constructionType)
     45 {
     46     registerAnimatedPropertiesForSVGGraphicsElement();
     47 }
     48 
     49 SVGGraphicsElement::~SVGGraphicsElement()
     50 {
     51 }
     52 
     53 AffineTransform 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 
     69 static 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 
     92 AffineTransform SVGGraphicsElement::getCTM(StyleUpdateStrategy styleUpdateStrategy)
     93 {
     94     return computeCTM(this, NearestViewportScope, styleUpdateStrategy);
     95 }
     96 
     97 AffineTransform SVGGraphicsElement::getScreenCTM(StyleUpdateStrategy styleUpdateStrategy)
     98 {
     99     return computeCTM(this, ScreenScope, styleUpdateStrategy);
    100 }
    101 
    102 AffineTransform 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 
    133 AffineTransform* SVGGraphicsElement::supplementalTransform()
    134 {
    135     if (!m_supplementalTransform)
    136         m_supplementalTransform = adoptPtr(new AffineTransform);
    137     return m_supplementalTransform.get();
    138 }
    139 
    140 bool 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 
    150 void 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 
    170 void 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 
    198 static 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 
    206 SVGElement* 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 
    216 SVGElement* 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 
    226 SVGRect 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 
    237 SVGRect 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 
    248 RenderObject* SVGGraphicsElement::createRenderer(RenderStyle*)
    249 {
    250     // By default, any subclass is expected to do path-based drawing
    251     return new RenderSVGPath(this);
    252 }
    253 
    254 void 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