Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(176)

Side by Side Diff: Source/core/rendering/svg/RenderSVGResourceClipper.cpp

Issue 23643003: ImageBuffer-less SVG masking and clipping. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Fix the Win build. Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2004, 2005, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org> 2 * Copyright (C) 2004, 2005, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Rob Buis <buis@kde.org> 3 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Rob Buis <buis@kde.org>
4 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. 4 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
5 * Copyright (C) 2011 Dirk Schulze <krit@webkit.org> 5 * Copyright (C) 2011 Dirk Schulze <krit@webkit.org>
6 * 6 *
7 * This library is free software; you can redistribute it and/or 7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public 8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version. 10 * version 2 of the License, or (at your option) any later version.
(...skipping 15 matching lines...) Expand all
26 26
27 #include "RuntimeEnabledFeatures.h" 27 #include "RuntimeEnabledFeatures.h"
28 #include "SVGNames.h" 28 #include "SVGNames.h"
29 #include "core/page/FrameView.h" 29 #include "core/page/FrameView.h"
30 #include "core/platform/graphics/GraphicsContextStateSaver.h" 30 #include "core/platform/graphics/GraphicsContextStateSaver.h"
31 #include "core/rendering/HitTestResult.h" 31 #include "core/rendering/HitTestResult.h"
32 #include "core/rendering/svg/SVGRenderingContext.h" 32 #include "core/rendering/svg/SVGRenderingContext.h"
33 #include "core/rendering/svg/SVGResources.h" 33 #include "core/rendering/svg/SVGResources.h"
34 #include "core/rendering/svg/SVGResourcesCache.h" 34 #include "core/rendering/svg/SVGResourcesCache.h"
35 #include "core/svg/SVGUseElement.h" 35 #include "core/svg/SVGUseElement.h"
36 #include "wtf/TemporaryChange.h"
36 37
37 namespace WebCore { 38 namespace WebCore {
38 39
39 RenderSVGResourceType RenderSVGResourceClipper::s_resourceType = ClipperResource Type; 40 RenderSVGResourceType RenderSVGResourceClipper::s_resourceType = ClipperResource Type;
40 41
41 RenderSVGResourceClipper::RenderSVGResourceClipper(SVGClipPathElement* node) 42 RenderSVGResourceClipper::RenderSVGResourceClipper(SVGClipPathElement* node)
42 : RenderSVGResourceContainer(node) 43 : RenderSVGResourceContainer(node)
44 , m_inClipExpansion(false)
43 { 45 {
44 } 46 }
45 47
46 RenderSVGResourceClipper::~RenderSVGResourceClipper() 48 RenderSVGResourceClipper::~RenderSVGResourceClipper()
47 { 49 {
48 if (m_clipper.isEmpty())
49 return;
50
51 deleteAllValues(m_clipper);
52 m_clipper.clear();
53 } 50 }
54 51
55 void RenderSVGResourceClipper::removeAllClientsFromCache(bool markForInvalidatio n) 52 void RenderSVGResourceClipper::removeAllClientsFromCache(bool markForInvalidatio n)
56 { 53 {
57 m_clipBoundaries = FloatRect(); 54 m_clipBoundaries = FloatRect();
58 if (!m_clipper.isEmpty()) { 55 m_rendererToClipperMap.clear();
59 deleteAllValues(m_clipper);
60 m_clipper.clear();
61 }
62
63 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInval idation : ParentOnlyInvalidation); 56 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInval idation : ParentOnlyInvalidation);
64 } 57 }
65 58
66 void RenderSVGResourceClipper::removeClientFromCache(RenderObject* client, bool markForInvalidation) 59 void RenderSVGResourceClipper::removeClientFromCache(RenderObject* client, bool markForInvalidation)
67 { 60 {
68 ASSERT(client); 61 ASSERT(client);
69 if (m_clipper.contains(client)) 62 m_rendererToClipperMap.remove(client);
70 delete m_clipper.take(client);
71
72 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidati on : ParentOnlyInvalidation); 63 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidati on : ParentOnlyInvalidation);
73 } 64 }
74 65
75 bool RenderSVGResourceClipper::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode) 66 bool RenderSVGResourceClipper::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
76 { 67 {
77 ASSERT(object); 68 ASSERT(object);
78 ASSERT(context); 69 ASSERT(context);
79 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); 70 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
80 71
81 return applyClippingToContext(object, object->objectBoundingBox(), object->r epaintRectInLocalCoordinates(), context); 72 return applyClippingToContext(object, object->objectBoundingBox(), object->r epaintRectInLocalCoordinates(), context);
82 } 73 }
83 74
84 bool RenderSVGResourceClipper::pathOnlyClipping(GraphicsContext* context, const AffineTransform& animatedLocalTransform, const FloatRect& objectBoundingBox) 75 bool RenderSVGResourceClipper::tryPathOnlyClipping(GraphicsContext* context,
85 { 76 const AffineTransform& animatedLocalTransform, const FloatRect& objectBoundi ngBox) {
86 // If the current clip-path gets clipped itself, we have to fallback to mask ing. 77 // If the current clip-path gets clipped itself, we have to fallback to mask ing.
87 if (!style()->svgStyle()->clipperResource().isEmpty()) 78 if (!style()->svgStyle()->clipperResource().isEmpty())
88 return false; 79 return false;
89 WindRule clipRule = RULE_NONZERO; 80 WindRule clipRule = RULE_NONZERO;
90 Path clipPath = Path(); 81 Path clipPath = Path();
91 82
92 for (Node* childNode = node()->firstChild(); childNode; childNode = childNod e->nextSibling()) { 83 for (Node* childNode = node()->firstChild(); childNode; childNode = childNod e->nextSibling()) {
93 RenderObject* renderer = childNode->renderer(); 84 RenderObject* renderer = childNode->renderer();
94 if (!renderer) 85 if (!renderer)
95 continue; 86 continue;
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
137 // Transform path by animatedLocalTransform. 128 // Transform path by animatedLocalTransform.
138 clipPath.transform(animatedLocalTransform); 129 clipPath.transform(animatedLocalTransform);
139 130
140 // The SVG specification wants us to clip everything, if clip-path doesn't h ave a child. 131 // The SVG specification wants us to clip everything, if clip-path doesn't h ave a child.
141 if (clipPath.isEmpty()) 132 if (clipPath.isEmpty())
142 clipPath.addRect(FloatRect()); 133 clipPath.addRect(FloatRect());
143 context->clipPath(clipPath, clipRule); 134 context->clipPath(clipPath, clipRule);
144 return true; 135 return true;
145 } 136 }
146 137
147 bool RenderSVGResourceClipper::applyClippingToContext(RenderObject* object, cons t FloatRect& objectBoundingBox, 138 bool RenderSVGResourceClipper::applyClippingToContext(RenderObject* target, cons t FloatRect& targetBoundingBox,
148 const FloatRect& repaintRe ct, GraphicsContext* context) 139 const FloatRect& repaintRe ct, GraphicsContext* context)
149 { 140 {
150 bool missingClipperData = !m_clipper.contains(object); 141 ASSERT(target);
151 if (missingClipperData) 142 ASSERT(context);
152 m_clipper.set(object, new ClipperData); 143 ASSERT_WITH_SECURITY_IMPLICATION(!needsLayout());
153 144
154 bool shouldCreateClipData = false; 145 if (repaintRect.isEmpty() || m_inClipExpansion)
146 return false;
147 TemporaryChange<bool> inClipExpansionChange(m_inClipExpansion, true);
148
149 // add(obj, 0) idiom -> lookup & add if not found.
150 OwnPtr<ClipperData>& clipperData = m_rendererToClipperMap.add(target, nullpt r).iterator->value;
151 if (!clipperData)
152 clipperData = adoptPtr(new ClipperData);
153
154 // First, try to apply the clip as a clipPath.
155 AffineTransform animatedLocalTransform = toSVGClipPathElement(node())->anima tedLocalTransform(); 155 AffineTransform animatedLocalTransform = toSVGClipPathElement(node())->anima tedLocalTransform();
156 ClipperData* clipperData = m_clipper.get(object); 156 if (tryPathOnlyClipping(context, animatedLocalTransform, targetBoundingBox)) {
157 if (!clipperData->clipMaskImage) { 157 clipperData->clipMode = ClipperData::PathOnlyClipMode;
158 if (pathOnlyClipping(context, animatedLocalTransform, objectBoundingBox) ) 158 return true;
159 return true;
160 shouldCreateClipData = true;
161 } 159 }
162 160
163 AffineTransform absoluteTransform; 161 // Fall back to masking.
164 SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(obje ct, absoluteTransform); 162 clipperData->clipMode = ClipperData::MaskClipMode;
165 163
166 if (shouldCreateClipData && !repaintRect.isEmpty()) { 164 // Mask layer start
167 if (!SVGRenderingContext::createImageBuffer(repaintRect, absoluteTransfo rm, clipperData->clipMaskImage, Unaccelerated)) 165 context->beginTransparencyLayer(1, &repaintRect);
168 return false; 166 {
169 167 GraphicsContextStateSaver maskContentSaver(*context);
170 GraphicsContext* maskContext = clipperData->clipMaskImage->context(); 168 context->concatCTM(animatedLocalTransform);
171 ASSERT(maskContext);
172
173 maskContext->concatCTM(animatedLocalTransform);
174 169
175 // clipPath can also be clipped by another clipPath. 170 // clipPath can also be clipped by another clipPath.
176 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObj ect(this); 171 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObj ect(this);
177 RenderSVGResourceClipper* clipper; 172 RenderSVGResourceClipper* clipPathClipper = 0;
178 bool succeeded; 173 if (resources && (clipPathClipper = resources->clipper())) {
179 if (resources && (clipper = resources->clipper())) { 174 if (!clipPathClipper->applyClippingToContext(this, targetBoundingBox , repaintRect, context)) {
180 GraphicsContextStateSaver stateSaver(*maskContext); 175 // FIXME: Awkward state micro-management. Ideally, GraphicsConte xtStateSaver should
176 // a) pop saveLayers also
177 // b) pop multiple states if needed (similarly to SkCanvas::re storeToCount())
178 // Then we should be able to replace this mess with a single, to p-level GCSS.
179 maskContentSaver.restore();
180 context->restoreLayer();
181 return false;
182 }
183 }
181 184
182 if (!clipper->applyClippingToContext(this, objectBoundingBox, repain tRect, maskContext)) 185 drawMaskContent(context, targetBoundingBox);
183 return false;
184 186
185 succeeded = drawContentIntoMaskImage(clipperData, objectBoundingBox) ; 187 if (clipPathClipper)
186 // The context restore applies the clipping on non-CG platforms. 188 clipPathClipper->postApplyResource(this, context, ApplyToDefaultMode , 0, 0);
187 } else
188 succeeded = drawContentIntoMaskImage(clipperData, objectBoundingBox) ;
189
190 if (!succeeded)
191 clipperData->clipMaskImage.clear();
192 } 189 }
193 190
194 if (!clipperData->clipMaskImage) 191 // Masked content layer start.
195 return false; 192 context->beginMaskedLayer(repaintRect);
196 193
197 SVGRenderingContext::clipToImageBuffer(context, absoluteTransform, repaintRe ct, clipperData->clipMaskImage, missingClipperData);
198 return true; 194 return true;
199 } 195 }
200 196
201 bool RenderSVGResourceClipper::drawContentIntoMaskImage(ClipperData* clipperData , const FloatRect& objectBoundingBox) 197 void RenderSVGResourceClipper::postApplyResource(RenderObject* object, GraphicsC ontext*& context,
198 unsigned short resourceMode, const Path*, const RenderSVGShape*) {
199 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
200
201 ClipperData* clipperData = m_rendererToClipperMap.get(object);
202 ASSERT(clipperData);
203
204 // Path-only clipping, no layers to restore.
205 if (clipperData->clipMode == ClipperData::PathOnlyClipMode)
206 return;
207
208 // Transfer content layer -> mask layer (SrcIn)
209 context->endLayer();
210 // Transfer mask layer -> bg layer (SrcOver)
211 context->endLayer();
212 }
213
214 void RenderSVGResourceClipper::drawMaskContent(GraphicsContext* context, const F loatRect& targetBoundingBox)
202 { 215 {
203 ASSERT(frame()); 216 ASSERT(frame());
204 ASSERT(clipperData); 217 ASSERT(context);
205 ASSERT(clipperData->clipMaskImage);
206 218
207 GraphicsContext* maskContext = clipperData->clipMaskImage->context(); 219 // Adjust the mask image context according to the target objectBoundingBox.
208 ASSERT(maskContext);
209
210 AffineTransform maskContentTransformation; 220 AffineTransform maskContentTransformation;
211 if (toSVGClipPathElement(node())->clipPathUnitsCurrentValue() == SVGUnitType s::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { 221 if (toSVGClipPathElement(node())->clipPathUnitsCurrentValue() == SVGUnitType s::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
212 maskContentTransformation.translate(objectBoundingBox.x(), objectBoundin gBox.y()); 222 maskContentTransformation.translate(targetBoundingBox.x(), targetBoundin gBox.y());
213 maskContentTransformation.scaleNonUniform(objectBoundingBox.width(), obj ectBoundingBox.height()); 223 maskContentTransformation.scaleNonUniform(targetBoundingBox.width(), tar getBoundingBox.height());
214 maskContext->concatCTM(maskContentTransformation); 224 context->concatCTM(maskContentTransformation);
215 } 225 }
216 226
217 // Switch to a paint behavior where all children of this <clipPath> will be rendered using special constraints: 227 // Switch to a paint behavior where all children of this <clipPath> will be rendered using special constraints:
218 // - fill-opacity/stroke-opacity/opacity set to 1 228 // - fill-opacity/stroke-opacity/opacity set to 1
219 // - masker/filter not applied when rendering the children 229 // - masker/filter not applied when rendering the children
220 // - fill is set to the initial fill paint server (solid, black) 230 // - fill is set to the initial fill paint server (solid, black)
221 // - stroke is set to the initial stroke paint server (none) 231 // - stroke is set to the initial stroke paint server (none)
222 PaintBehavior oldBehavior = frame()->view()->paintBehavior(); 232 PaintBehavior oldBehavior = frame()->view()->paintBehavior();
223 frame()->view()->setPaintBehavior(oldBehavior | PaintBehaviorRenderingSVGMas k); 233 frame()->view()->setPaintBehavior(oldBehavior | PaintBehaviorRenderingSVGMas k);
224 234
225 // Draw all clipPath children into a global mask.
226 for (Node* childNode = node()->firstChild(); childNode; childNode = childNod e->nextSibling()) { 235 for (Node* childNode = node()->firstChild(); childNode; childNode = childNod e->nextSibling()) {
227 RenderObject* renderer = childNode->renderer(); 236 RenderObject* renderer = childNode->renderer();
228 if (!childNode->isSVGElement() || !renderer) 237 if (!childNode->isSVGElement() || !renderer)
229 continue; 238 continue;
230 if (renderer->needsLayout()) { 239
231 frame()->view()->setPaintBehavior(oldBehavior);
232 return false;
233 }
234 RenderStyle* style = renderer->style(); 240 RenderStyle* style = renderer->style();
235 if (!style || style->display() == NONE || style->visibility() != VISIBLE ) 241 if (!style || style->display() == NONE || style->visibility() != VISIBLE )
236 continue; 242 continue;
237 243
238 WindRule newClipRule = style->svgStyle()->clipRule(); 244 WindRule newClipRule = style->svgStyle()->clipRule();
239 bool isUseElement = childNode->hasTagName(SVGNames::useTag); 245 bool isUseElement = childNode->hasTagName(SVGNames::useTag);
240 if (isUseElement) { 246 if (isUseElement) {
241 SVGUseElement* useElement = toSVGUseElement(childNode); 247 SVGUseElement* useElement = toSVGUseElement(childNode);
242 renderer = useElement->rendererClipChild(); 248 renderer = useElement->rendererClipChild();
243 if (!renderer) 249 if (!renderer)
244 continue; 250 continue;
245 if (!useElement->hasAttribute(SVGNames::clip_ruleAttr)) 251 if (!useElement->hasAttribute(SVGNames::clip_ruleAttr))
246 newClipRule = renderer->style()->svgStyle()->clipRule(); 252 newClipRule = renderer->style()->svgStyle()->clipRule();
247 } 253 }
248 254
249 // Only shapes, paths and texts are allowed for clipping. 255 // Only shapes, paths and texts are allowed for clipping.
250 if (!renderer->isSVGShape() && !renderer->isSVGText()) 256 if (!renderer->isSVGShape() && !renderer->isSVGText())
251 continue; 257 continue;
252 258
253 maskContext->setFillRule(newClipRule); 259 context->setFillRule(newClipRule);
254 260
255 // In the case of a <use> element, we obtained its renderere above, to r etrieve its clipRule. 261 if (isUseElement)
256 // We have to pass the <use> renderer itself to renderSubtreeToImageBuff er() to apply it's x/y/transform/etc. values when rendering. 262 renderer = childNode->renderer();
257 // So if isUseElement is true, refetch the childNode->renderer(), as ren derer got overriden above. 263
258 SVGRenderingContext::renderSubtreeToImageBuffer(clipperData->clipMaskIma ge.get(), isUseElement ? childNode->renderer() : renderer, maskContentTransforma tion); 264 SVGRenderingContext::renderSubtree(context, renderer, maskContentTransfo rmation);
259 } 265 }
260 266
261 frame()->view()->setPaintBehavior(oldBehavior); 267 frame()->view()->setPaintBehavior(oldBehavior);
262 return true;
263 } 268 }
264 269
265 void RenderSVGResourceClipper::calculateClipContentRepaintRect() 270 void RenderSVGResourceClipper::calculateClipContentRepaintRect()
266 { 271 {
267 // This is a rough heuristic to appraise the clip size and doesn't consider clip on clip. 272 // This is a rough heuristic to appraise the clip size and doesn't consider clip on clip.
268 for (Node* childNode = node()->firstChild(); childNode; childNode = childNod e->nextSibling()) { 273 for (Node* childNode = node()->firstChild(); childNode; childNode = childNod e->nextSibling()) {
269 RenderObject* renderer = childNode->renderer(); 274 RenderObject* renderer = childNode->renderer();
270 if (!childNode->isSVGElement() || !renderer) 275 if (!childNode->isSVGElement() || !renderer)
271 continue; 276 continue;
272 if (!renderer->isSVGShape() && !renderer->isSVGText() && !childNode->has TagName(SVGNames::useTag)) 277 if (!renderer->isSVGShape() && !renderer->isSVGText() && !childNode->has TagName(SVGNames::useTag))
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
324 AffineTransform transform; 329 AffineTransform transform;
325 transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); 330 transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
326 transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.h eight()); 331 transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.h eight());
327 return transform.mapRect(m_clipBoundaries); 332 return transform.mapRect(m_clipBoundaries);
328 } 333 }
329 334
330 return m_clipBoundaries; 335 return m_clipBoundaries;
331 } 336 }
332 337
333 } 338 }
OLDNEW
« no previous file with comments | « Source/core/rendering/svg/RenderSVGResourceClipper.h ('k') | Source/core/rendering/svg/RenderSVGResourceMasker.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698