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 blink { 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 } 53 54 void RenderSVGResourceContainer::layout() 55 { 56 // FIXME: Investigate a way to detect and break resource layout dependency cycles early. 57 // Then we can remove this method altogether, and fall back onto RenderSVGHiddenContainer::layout(). 58 ASSERT(needsLayout()); 59 if (m_isInLayout) 60 return; 61 62 TemporaryChange<bool> inLayoutChange(m_isInLayout, true); 63 64 RenderSVGHiddenContainer::layout(); 65 66 clearInvalidationMask(); 67 } 68 69 void RenderSVGResourceContainer::willBeDestroyed() 70 { 71 SVGResourcesCache::resourceDestroyed(this); 72 RenderSVGHiddenContainer::willBeDestroyed(); 73 if (m_registered) 74 svgExtensionsFromElement(element()).removeResource(m_id); 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)->filterNeedsPaintInvalidation(); 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 PaintInvalidation: 150 client->setShouldDoFullPaintInvalidation(true); 151 break; 152 case ParentOnlyInvalidation: 153 break; 154 } 155 } 156 157 void RenderSVGResourceContainer::addClient(RenderObject* client) 158 { 159 ASSERT(client); 160 m_clients.add(client); 161 clearInvalidationMask(); 162 } 163 164 void RenderSVGResourceContainer::removeClient(RenderObject* client) 165 { 166 ASSERT(client); 167 removeClientFromCache(client, false); 168 m_clients.remove(client); 169 } 170 171 void RenderSVGResourceContainer::addClientRenderLayer(Node* node) 172 { 173 ASSERT(node); 174 if (!node->renderer() || !node->renderer()->hasLayer()) 175 return; 176 m_clientLayers.add(toRenderLayerModelObject(node->renderer())->layer()); 177 clearInvalidationMask(); 178 } 179 180 void RenderSVGResourceContainer::addClientRenderLayer(RenderLayer* client) 181 { 182 ASSERT(client); 183 m_clientLayers.add(client); 184 clearInvalidationMask(); 185 } 186 187 void RenderSVGResourceContainer::removeClientRenderLayer(RenderLayer* client) 188 { 189 ASSERT(client); 190 m_clientLayers.remove(client); 191 } 192 193 void RenderSVGResourceContainer::invalidateCacheAndMarkForLayout(SubtreeLayoutScope* layoutScope) 194 { 195 if (selfNeedsLayout()) 196 return; 197 198 setNeedsLayoutAndFullPaintInvalidation(MarkContainingBlockChain, layoutScope); 199 200 if (everHadLayout()) 201 removeAllClientsFromCache(); 202 } 203 204 void RenderSVGResourceContainer::registerResource() 205 { 206 SVGDocumentExtensions& extensions = svgExtensionsFromElement(element()); 207 if (!extensions.hasPendingResource(m_id)) { 208 extensions.addResource(m_id, this); 209 return; 210 } 211 212 OwnPtrWillBeRawPtr<SVGDocumentExtensions::SVGPendingElements> clients(extensions.removePendingResource(m_id)); 213 214 // Cache us with the new id. 215 extensions.addResource(m_id, this); 216 217 // Update cached resources of pending clients. 218 const SVGDocumentExtensions::SVGPendingElements::const_iterator end = clients->end(); 219 for (SVGDocumentExtensions::SVGPendingElements::const_iterator it = clients->begin(); it != end; ++it) { 220 ASSERT((*it)->hasPendingResources()); 221 extensions.clearHasPendingResourcesIfPossible(*it); 222 RenderObject* renderer = (*it)->renderer(); 223 if (!renderer) 224 continue; 225 226 StyleDifference diff; 227 diff.setNeedsFullLayout(); 228 SVGResourcesCache::clientStyleChanged(renderer, diff, renderer->style()); 229 renderer->setNeedsLayoutAndFullPaintInvalidation(); 230 } 231 } 232 233 static bool shouldTransformOnTextPainting(RenderObject* object, AffineTransform& resourceTransform) 234 { 235 ASSERT(object); 236 237 // This method should only be called for RenderObjects that deal with text rendering. Cmp. RenderObject.h's is*() methods. 238 ASSERT(object->isSVGText() || object->isSVGTextPath() || object->isSVGInline()); 239 240 // In text drawing, the scaling part of the graphics context CTM is removed, compare SVGInlineTextBox::paintTextWithShadows. 241 // So, we use that scaling factor here, too, and then push it down to pattern or gradient space 242 // in order to keep the pattern or gradient correctly scaled. 243 float scalingFactor = SVGRenderingContext::calculateScreenFontSizeScalingFactor(object); 244 if (scalingFactor == 1) 245 return false; 246 resourceTransform.scale(scalingFactor); 247 return true; 248 } 249 250 AffineTransform RenderSVGResourceContainer::computeResourceSpaceTransform(RenderObject* object, const AffineTransform& baseTransform, const SVGRenderStyle& svgStyle, unsigned short resourceMode) 251 { 252 AffineTransform computedSpaceTransform = baseTransform; 253 if (resourceMode & ApplyToTextMode) { 254 // Depending on the font scaling factor, we may need to apply an 255 // additional transform (scale-factor) the paintserver, since text 256 // painting removes the scale factor from the context. (See 257 // SVGInlineTextBox::paintTextWithShadows.) 258 AffineTransform additionalTextTransformation; 259 if (shouldTransformOnTextPainting(object, additionalTextTransformation)) 260 computedSpaceTransform = additionalTextTransformation * computedSpaceTransform; 261 } 262 if (resourceMode & ApplyToStrokeMode) { 263 // Non-scaling stroke needs to reset the transform back to the host transform. 264 if (svgStyle.vectorEffect() == VE_NON_SCALING_STROKE) 265 computedSpaceTransform = transformOnNonScalingStroke(object, computedSpaceTransform); 266 } 267 return computedSpaceTransform; 268 } 269 270 // FIXME: This does not belong here. 271 AffineTransform RenderSVGResourceContainer::transformOnNonScalingStroke(RenderObject* object, const AffineTransform& resourceTransform) 272 { 273 if (!object->isSVGShape()) 274 return resourceTransform; 275 276 SVGGraphicsElement* element = toSVGGraphicsElement(object->node()); 277 AffineTransform transform = element->getScreenCTM(SVGGraphicsElement::DisallowStyleUpdate); 278 transform *= resourceTransform; 279 return transform; 280 } 281 282 } 283