Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
      3  *
      4  * This library is free software; you can redistribute it and/or
      5  * modify it under the terms of the GNU Library General Public
      6  * License as published by the Free Software Foundation; either
      7  * version 2 of the License, or (at your option) any later version.
      8  *
      9  * This library is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  * Library General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU Library General Public License
     15  * along with this library; see the file COPYING.LIB.  If not, write to
     16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     17  * Boston, MA 02110-1301, USA.
     18  */
     19 
     20 #include "config.h"
     21 
     22 #include "core/rendering/svg/RenderSVGResourceContainer.h"
     23 
     24 #include "core/rendering/RenderLayer.h"
     25 #include "core/rendering/RenderView.h"
     26 #include "core/rendering/svg/SVGRenderingContext.h"
     27 #include "core/rendering/svg/SVGResourcesCache.h"
     28 #include "core/svg/SVGGraphicsElement.h"
     29 
     30 #include "wtf/TemporaryChange.h"
     31 
     32 namespace WebCore {
     33 
     34 static inline SVGDocumentExtensions& svgExtensionsFromElement(SVGElement* element)
     35 {
     36     ASSERT(element);
     37     return element->document().accessSVGExtensions();
     38 }
     39 
     40 RenderSVGResourceContainer::RenderSVGResourceContainer(SVGElement* node)
     41     : RenderSVGHiddenContainer(node)
     42     , m_isInLayout(false)
     43     , m_id(node->getIdAttribute())
     44     , m_invalidationMask(0)
     45     , m_registered(false)
     46     , m_isInvalidating(false)
     47 {
     48 }
     49 
     50 RenderSVGResourceContainer::~RenderSVGResourceContainer()
     51 {
     52     if (m_registered)
     53         svgExtensionsFromElement(element()).removeResource(m_id);
     54 }
     55 
     56 void RenderSVGResourceContainer::layout()
     57 {
     58     // FIXME: Investigate a way to detect and break resource layout dependency cycles early.
     59     // Then we can remove this method altogether, and fall back onto RenderSVGHiddenContainer::layout().
     60     ASSERT(needsLayout());
     61     if (m_isInLayout)
     62         return;
     63 
     64     TemporaryChange<bool> inLayoutChange(m_isInLayout, true);
     65 
     66     RenderSVGHiddenContainer::layout();
     67 
     68     clearInvalidationMask();
     69 }
     70 
     71 void RenderSVGResourceContainer::willBeDestroyed()
     72 {
     73     SVGResourcesCache::resourceDestroyed(this);
     74     RenderSVGHiddenContainer::willBeDestroyed();
     75 }
     76 
     77 void RenderSVGResourceContainer::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
     78 {
     79     RenderSVGHiddenContainer::styleDidChange(diff, oldStyle);
     80 
     81     if (!m_registered) {
     82         m_registered = true;
     83         registerResource();
     84     }
     85 }
     86 
     87 void RenderSVGResourceContainer::idChanged()
     88 {
     89     // Invalidate all our current clients.
     90     removeAllClientsFromCache();
     91 
     92     // Remove old id, that is guaranteed to be present in cache.
     93     SVGDocumentExtensions& extensions = svgExtensionsFromElement(element());
     94     extensions.removeResource(m_id);
     95     m_id = element()->getIdAttribute();
     96 
     97     registerResource();
     98 }
     99 
    100 void RenderSVGResourceContainer::markAllClientsForInvalidation(InvalidationMode mode)
    101 {
    102     if ((m_clients.isEmpty() && m_clientLayers.isEmpty()) || m_isInvalidating)
    103         return;
    104 
    105     if (m_invalidationMask & mode)
    106         return;
    107 
    108     m_invalidationMask |= mode;
    109     m_isInvalidating = true;
    110     bool needsLayout = mode == LayoutAndBoundariesInvalidation;
    111     bool markForInvalidation = mode != ParentOnlyInvalidation;
    112 
    113     HashSet<RenderObject*>::iterator end = m_clients.end();
    114     for (HashSet<RenderObject*>::iterator it = m_clients.begin(); it != end; ++it) {
    115         RenderObject* client = *it;
    116         if (client->isSVGResourceContainer()) {
    117             toRenderSVGResourceContainer(client)->removeAllClientsFromCache(markForInvalidation);
    118             continue;
    119         }
    120 
    121         if (markForInvalidation)
    122             markClientForInvalidation(client, mode);
    123 
    124         RenderSVGResource::markForLayoutAndParentResourceInvalidation(client, needsLayout);
    125     }
    126 
    127     markAllClientLayersForInvalidation();
    128 
    129     m_isInvalidating = false;
    130 }
    131 
    132 void RenderSVGResourceContainer::markAllClientLayersForInvalidation()
    133 {
    134     HashSet<RenderLayer*>::iterator layerEnd = m_clientLayers.end();
    135     for (HashSet<RenderLayer*>::iterator it = m_clientLayers.begin(); it != layerEnd; ++it)
    136         (*it)->filterNeedsRepaint();
    137 }
    138 
    139 void RenderSVGResourceContainer::markClientForInvalidation(RenderObject* client, InvalidationMode mode)
    140 {
    141     ASSERT(client);
    142     ASSERT(!m_clients.isEmpty());
    143 
    144     switch (mode) {
    145     case LayoutAndBoundariesInvalidation:
    146     case BoundariesInvalidation:
    147         client->setNeedsBoundariesUpdate();
    148         break;
    149     case RepaintInvalidation:
    150         if (client->view()) {
    151             if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled() && frameView()->isInPerformLayout())
    152                 client->setShouldDoFullPaintInvalidationAfterLayout(true);
    153             else
    154                 client->paintInvalidationForWholeRenderer();
    155         }
    156         break;
    157     case ParentOnlyInvalidation:
    158         break;
    159     }
    160 }
    161 
    162 void RenderSVGResourceContainer::addClient(RenderObject* client)
    163 {
    164     ASSERT(client);
    165     m_clients.add(client);
    166     clearInvalidationMask();
    167 }
    168 
    169 void RenderSVGResourceContainer::removeClient(RenderObject* client)
    170 {
    171     ASSERT(client);
    172     removeClientFromCache(client, false);
    173     m_clients.remove(client);
    174 }
    175 
    176 void RenderSVGResourceContainer::addClientRenderLayer(Node* node)
    177 {
    178     ASSERT(node);
    179     if (!node->renderer() || !node->renderer()->hasLayer())
    180         return;
    181     m_clientLayers.add(toRenderLayerModelObject(node->renderer())->layer());
    182     clearInvalidationMask();
    183 }
    184 
    185 void RenderSVGResourceContainer::addClientRenderLayer(RenderLayer* client)
    186 {
    187     ASSERT(client);
    188     m_clientLayers.add(client);
    189     clearInvalidationMask();
    190 }
    191 
    192 void RenderSVGResourceContainer::removeClientRenderLayer(RenderLayer* client)
    193 {
    194     ASSERT(client);
    195     m_clientLayers.remove(client);
    196 }
    197 
    198 void RenderSVGResourceContainer::invalidateCacheAndMarkForLayout(SubtreeLayoutScope* layoutScope)
    199 {
    200     if (selfNeedsLayout())
    201         return;
    202 
    203     setNeedsLayoutAndFullPaintInvalidation(MarkContainingBlockChain, layoutScope);
    204 
    205     if (everHadLayout())
    206         removeAllClientsFromCache();
    207 }
    208 
    209 void RenderSVGResourceContainer::registerResource()
    210 {
    211     SVGDocumentExtensions& extensions = svgExtensionsFromElement(element());
    212     if (!extensions.hasPendingResource(m_id)) {
    213         extensions.addResource(m_id, this);
    214         return;
    215     }
    216 
    217     OwnPtr<SVGDocumentExtensions::SVGPendingElements> clients(extensions.removePendingResource(m_id));
    218 
    219     // Cache us with the new id.
    220     extensions.addResource(m_id, this);
    221 
    222     // Update cached resources of pending clients.
    223     const SVGDocumentExtensions::SVGPendingElements::const_iterator end = clients->end();
    224     for (SVGDocumentExtensions::SVGPendingElements::const_iterator it = clients->begin(); it != end; ++it) {
    225         ASSERT((*it)->hasPendingResources());
    226         extensions.clearHasPendingResourcesIfPossible(*it);
    227         RenderObject* renderer = (*it)->renderer();
    228         if (!renderer)
    229             continue;
    230 
    231         StyleDifference diff;
    232         diff.setNeedsFullLayout();
    233         SVGResourcesCache::clientStyleChanged(renderer, diff, renderer->style());
    234         renderer->setNeedsLayoutAndFullPaintInvalidation();
    235     }
    236 }
    237 
    238 static bool shouldTransformOnTextPainting(RenderObject* object, AffineTransform& resourceTransform)
    239 {
    240     ASSERT(object);
    241 
    242     // This method should only be called for RenderObjects that deal with text rendering. Cmp. RenderObject.h's is*() methods.
    243     ASSERT(object->isSVGText() || object->isSVGTextPath() || object->isSVGInline());
    244 
    245     // In text drawing, the scaling part of the graphics context CTM is removed, compare SVGInlineTextBox::paintTextWithShadows.
    246     // So, we use that scaling factor here, too, and then push it down to pattern or gradient space
    247     // in order to keep the pattern or gradient correctly scaled.
    248     float scalingFactor = SVGRenderingContext::calculateScreenFontSizeScalingFactor(object);
    249     if (scalingFactor == 1)
    250         return false;
    251     resourceTransform.scale(scalingFactor);
    252     return true;
    253 }
    254 
    255 AffineTransform RenderSVGResourceContainer::computeResourceSpaceTransform(RenderObject* object, const AffineTransform& baseTransform, const SVGRenderStyle* svgStyle, unsigned short resourceMode)
    256 {
    257     AffineTransform computedSpaceTransform = baseTransform;
    258     if (resourceMode & ApplyToTextMode) {
    259         // Depending on the font scaling factor, we may need to apply an
    260         // additional transform (scale-factor) the paintserver, since text
    261         // painting removes the scale factor from the context. (See
    262         // SVGInlineTextBox::paintTextWithShadows.)
    263         AffineTransform additionalTextTransformation;
    264         if (shouldTransformOnTextPainting(object, additionalTextTransformation))
    265             computedSpaceTransform = additionalTextTransformation * computedSpaceTransform;
    266     }
    267     if (resourceMode & ApplyToStrokeMode) {
    268         // Non-scaling stroke needs to reset the transform back to the host transform.
    269         if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE)
    270             computedSpaceTransform = transformOnNonScalingStroke(object, computedSpaceTransform);
    271     }
    272     return computedSpaceTransform;
    273 }
    274 
    275 // FIXME: This does not belong here.
    276 AffineTransform RenderSVGResourceContainer::transformOnNonScalingStroke(RenderObject* object, const AffineTransform& resourceTransform)
    277 {
    278     if (!object->isSVGShape())
    279         return resourceTransform;
    280 
    281     SVGGraphicsElement* element = toSVGGraphicsElement(object->node());
    282     AffineTransform transform = element->getScreenCTM(SVGGraphicsElement::DisallowStyleUpdate);
    283     transform *= resourceTransform;
    284     return transform;
    285 }
    286 
    287 }
    288