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

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: Removed Linux rebaselines. 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 10 matching lines...) Expand all
21 */ 21 */
22 22
23 #include "config.h" 23 #include "config.h"
24 24
25 #include "core/rendering/svg/RenderSVGResourceClipper.h" 25 #include "core/rendering/svg/RenderSVGResourceClipper.h"
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/platform/graphics/skia/SkiaUtils.h"
Stephen White 2013/08/28 14:11:10 We're calling into skia-specific code from outside
f(malita) 2013/08/28 19:54:38 Fixed, see other comment.
31 #include "core/rendering/HitTestResult.h" 32 #include "core/rendering/HitTestResult.h"
32 #include "core/rendering/svg/SVGRenderingContext.h" 33 #include "core/rendering/svg/SVGRenderingContext.h"
33 #include "core/rendering/svg/SVGResources.h" 34 #include "core/rendering/svg/SVGResources.h"
34 #include "core/rendering/svg/SVGResourcesCache.h" 35 #include "core/rendering/svg/SVGResourcesCache.h"
35 #include "core/svg/SVGUseElement.h" 36 #include "core/svg/SVGUseElement.h"
37 #include "wtf/TemporaryChange.h"
36 38
37 namespace WebCore { 39 namespace WebCore {
38 40
39 RenderSVGResourceType RenderSVGResourceClipper::s_resourceType = ClipperResource Type; 41 RenderSVGResourceType RenderSVGResourceClipper::s_resourceType = ClipperResource Type;
40 42
41 RenderSVGResourceClipper::RenderSVGResourceClipper(SVGClipPathElement* node) 43 RenderSVGResourceClipper::RenderSVGResourceClipper(SVGClipPathElement* node)
42 : RenderSVGResourceContainer(node) 44 : RenderSVGResourceContainer(node)
45 , m_inClipExpansion(false)
43 { 46 {
44 } 47 }
45 48
46 RenderSVGResourceClipper::~RenderSVGResourceClipper() 49 RenderSVGResourceClipper::~RenderSVGResourceClipper()
47 { 50 {
48 if (m_clipper.isEmpty())
49 return;
50
51 deleteAllValues(m_clipper);
52 m_clipper.clear();
53 } 51 }
54 52
55 void RenderSVGResourceClipper::removeAllClientsFromCache(bool markForInvalidatio n) 53 void RenderSVGResourceClipper::removeAllClientsFromCache(bool markForInvalidatio n)
56 { 54 {
57 m_clipBoundaries = FloatRect(); 55 m_clipBoundaries = FloatRect();
58 if (!m_clipper.isEmpty()) { 56 m_clipperMap.clear();
59 deleteAllValues(m_clipper);
60 m_clipper.clear();
61 }
62
63 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInval idation : ParentOnlyInvalidation); 57 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInval idation : ParentOnlyInvalidation);
64 } 58 }
65 59
66 void RenderSVGResourceClipper::removeClientFromCache(RenderObject* client, bool markForInvalidation) 60 void RenderSVGResourceClipper::removeClientFromCache(RenderObject* client, bool markForInvalidation)
67 { 61 {
68 ASSERT(client); 62 ASSERT(client);
69 if (m_clipper.contains(client)) 63 m_clipperMap.remove(client);
70 delete m_clipper.take(client);
71
72 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidati on : ParentOnlyInvalidation); 64 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidati on : ParentOnlyInvalidation);
73 } 65 }
74 66
75 bool RenderSVGResourceClipper::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode) 67 bool RenderSVGResourceClipper::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
76 { 68 {
77 ASSERT(object); 69 ASSERT(object);
78 ASSERT(context); 70 ASSERT(context);
79 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); 71 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
pdr. 2013/08/28 03:32:36 Can you add ASSERT_WITH_SECURITY_IMPLICATION(!need
80 72
81 return applyClippingToContext(object, object->objectBoundingBox(), object->r epaintRectInLocalCoordinates(), context); 73 return applyClippingToContext(object, object->objectBoundingBox(), object->r epaintRectInLocalCoordinates(), context);
82 } 74 }
83 75
84 bool RenderSVGResourceClipper::pathOnlyClipping(GraphicsContext* context, const AffineTransform& animatedLocalTransform, const FloatRect& objectBoundingBox) 76 bool RenderSVGResourceClipper::pathOnlyClipping(GraphicsContext* context, const AffineTransform& animatedLocalTransform, const FloatRect& objectBoundingBox)
85 { 77 {
86 // If the current clip-path gets clipped itself, we have to fallback to mask ing. 78 // If the current clip-path gets clipped itself, we have to fallback to mask ing.
87 if (!style()->svgStyle()->clipperResource().isEmpty()) 79 if (!style()->svgStyle()->clipperResource().isEmpty())
88 return false; 80 return false;
89 WindRule clipRule = RULE_NONZERO; 81 WindRule clipRule = RULE_NONZERO;
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
137 // Transform path by animatedLocalTransform. 129 // Transform path by animatedLocalTransform.
138 clipPath.transform(animatedLocalTransform); 130 clipPath.transform(animatedLocalTransform);
139 131
140 // The SVG specification wants us to clip everything, if clip-path doesn't h ave a child. 132 // The SVG specification wants us to clip everything, if clip-path doesn't h ave a child.
141 if (clipPath.isEmpty()) 133 if (clipPath.isEmpty())
142 clipPath.addRect(FloatRect()); 134 clipPath.addRect(FloatRect());
143 context->clipPath(clipPath, clipRule); 135 context->clipPath(clipPath, clipRule);
144 return true; 136 return true;
145 } 137 }
146 138
147 bool RenderSVGResourceClipper::applyClippingToContext(RenderObject* object, cons t FloatRect& objectBoundingBox, 139 bool RenderSVGResourceClipper::applyClippingToContext(RenderObject* target, cons t FloatRect& targetBoundingBox,
148 const FloatRect& repaintRe ct, GraphicsContext* context) 140 const FloatRect& repaintRe ct, GraphicsContext* context)
149 { 141 {
150 bool missingClipperData = !m_clipper.contains(object); 142 ASSERT(target);
151 if (missingClipperData) 143 ASSERT(context);
152 m_clipper.set(object, new ClipperData); 144 ASSERT_WITH_SECURITY_IMPLICATION(!needsLayout());
153 145
154 bool shouldCreateClipData = false; 146 if (repaintRect.isEmpty() || m_inClipExpansion)
147 return false;
148 TemporaryChange<bool> inClipExpansionChange(m_inClipExpansion, true);
149
150 // add(obj, 0) idiom -> lookup & add if not found.
pdr. 2013/08/28 03:32:36 (not for this patch) We should add HashMap::lookup
151 OwnPtr<ClipperData>& clipperData = m_clipperMap.add(target, nullptr).iterato r->value;
152 if (!clipperData)
153 clipperData = adoptPtr(new ClipperData);
154
155 // First, try to apply the clip as a clipPath.
155 AffineTransform animatedLocalTransform = toSVGClipPathElement(node())->anima tedLocalTransform(); 156 AffineTransform animatedLocalTransform = toSVGClipPathElement(node())->anima tedLocalTransform();
156 ClipperData* clipperData = m_clipper.get(object); 157 if (pathOnlyClipping(context, animatedLocalTransform, targetBoundingBox)) {
pdr. 2013/08/28 03:32:36 Nit: could you rename this tryPathOnlyClipping?
f(malita) 2013/08/28 19:54:38 Done.
157 if (!clipperData->clipMaskImage) { 158 clipperData->pathOnly = true;
158 if (pathOnlyClipping(context, animatedLocalTransform, objectBoundingBox) ) 159 return true;
159 return true;
160 shouldCreateClipData = true;
161 } 160 }
162 161
163 AffineTransform absoluteTransform; 162 // Fall back to masking.
164 SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(obje ct, absoluteTransform); 163 clipperData->pathOnly = false;
164 SkRect layerRect = WebCoreFloatRectToSKRect(repaintRect);
165 165
166 if (shouldCreateClipData && !repaintRect.isEmpty()) { 166 // Mask layer start
167 if (!SVGRenderingContext::createImageBuffer(repaintRect, absoluteTransfo rm, clipperData->clipMaskImage, Unaccelerated)) 167 context->saveLayer(&layerRect, 0);
168 return false; 168 {
169 169 GraphicsContextStateSaver maskContentSaver(*context);
170 GraphicsContext* maskContext = clipperData->clipMaskImage->context(); 170 context->concatCTM(animatedLocalTransform);
171 ASSERT(maskContext);
172
173 maskContext->concatCTM(animatedLocalTransform);
174 171
175 // clipPath can also be clipped by another clipPath. 172 // clipPath can also be clipped by another clipPath.
176 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObj ect(this); 173 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObj ect(this);
177 RenderSVGResourceClipper* clipper; 174 RenderSVGResourceClipper* selfClipper = 0;
pdr. 2013/08/28 03:32:36 Nit: clipPathClipper? or clipOnClip? I was confuse
f(malita) 2013/08/28 19:54:38 Done.
178 bool succeeded; 175 if (resources && (selfClipper = resources->clipper())) {
179 if (resources && (clipper = resources->clipper())) { 176 if (!selfClipper->applyClippingToContext(this, targetBoundingBox, re paintRect, context)) {
180 GraphicsContextStateSaver stateSaver(*maskContext); 177 // FIXME: Awkward state micro-management. Ideally, GraphicsConte xtStateSaver should
178 // a) pop saveLayers also
179 // b) pop multiple states if needed (similarly to SkCanvas::re storeToCount())
180 // Then we should be able to replace this mess with a single, to p-level GCSS.
181 maskContentSaver.restore();
182 context->restoreLayer();
183 return false;
184 }
185 }
181 186
182 if (!clipper->applyClippingToContext(this, objectBoundingBox, repain tRect, maskContext)) 187 drawMaskContent(context, targetBoundingBox);
183 return false;
184 188
185 succeeded = drawContentIntoMaskImage(clipperData, objectBoundingBox) ; 189 if (selfClipper)
186 // The context restore applies the clipping on non-CG platforms. 190 selfClipper->postApplyResource(this, context, ApplyToDefaultMode, 0, 0);
187 } else
188 succeeded = drawContentIntoMaskImage(clipperData, objectBoundingBox) ;
189
190 if (!succeeded)
191 clipperData->clipMaskImage.clear();
192 } 191 }
193 192
194 if (!clipperData->clipMaskImage) 193 // Start the content layer in SrcIn mode. This ensures that it gets masked a gainst the current
195 return false; 194 // (mask content) layer on restore.
195 SkPaint contentPaint;
196 contentPaint.setXfermodeMode(SkXfermode::kSrcIn_Mode);
197 context->saveLayer(&layerRect, &contentPaint);
196 198
197 SVGRenderingContext::clipToImageBuffer(context, absoluteTransform, repaintRe ct, clipperData->clipMaskImage, missingClipperData);
198 return true; 199 return true;
199 } 200 }
200 201
201 bool RenderSVGResourceClipper::drawContentIntoMaskImage(ClipperData* clipperData , const FloatRect& objectBoundingBox) 202 void RenderSVGResourceClipper::postApplyResource(RenderObject* object, GraphicsC ontext*& context,
203 unsigned short resourceMode, const Path*, const RenderSVGShape*) {
204 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
205
206 ClipperData* clipperData = m_clipperMap.get(object);
207 ASSERT(clipperData);
208
209 // Path-only clipping, no layers to restore.
210 if (clipperData->pathOnly)
211 return;
212
213 // Transfer content layer -> mask layer (SrcIn)
214 context->restoreLayer();
215 // Transfer mask layer -> bg layer (SrcOver)
216 context->restoreLayer();
217 }
218
219 void RenderSVGResourceClipper::drawMaskContent(GraphicsContext* context, const F loatRect& targetBoundingBox)
202 { 220 {
203 ASSERT(frame()); 221 ASSERT(frame());
204 ASSERT(clipperData); 222 ASSERT(context);
205 ASSERT(clipperData->clipMaskImage);
206 223
207 GraphicsContext* maskContext = clipperData->clipMaskImage->context(); 224 // Adjust the mask image context according to the target objectBoundingBox.
208 ASSERT(maskContext);
209
210 AffineTransform maskContentTransformation; 225 AffineTransform maskContentTransformation;
211 if (toSVGClipPathElement(node())->clipPathUnitsCurrentValue() == SVGUnitType s::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { 226 if (toSVGClipPathElement(node())->clipPathUnitsCurrentValue() == SVGUnitType s::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
212 maskContentTransformation.translate(objectBoundingBox.x(), objectBoundin gBox.y()); 227 maskContentTransformation = makeMapBetweenRects(FloatRect(0, 0, 1, 1), t argetBoundingBox);
pdr. 2013/08/28 03:32:36 Can you switch this back to the previous method? m
f(malita) 2013/08/28 19:54:38 Done.
213 maskContentTransformation.scaleNonUniform(objectBoundingBox.width(), obj ectBoundingBox.height()); 228 context->concatCTM(maskContentTransformation);
214 maskContext->concatCTM(maskContentTransformation);
215 } 229 }
216 230
217 // Switch to a paint behavior where all children of this <clipPath> will be rendered using special constraints: 231 // 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 232 // - fill-opacity/stroke-opacity/opacity set to 1
219 // - masker/filter not applied when rendering the children 233 // - masker/filter not applied when rendering the children
220 // - fill is set to the initial fill paint server (solid, black) 234 // - fill is set to the initial fill paint server (solid, black)
221 // - stroke is set to the initial stroke paint server (none) 235 // - stroke is set to the initial stroke paint server (none)
222 PaintBehavior oldBehavior = frame()->view()->paintBehavior(); 236 PaintBehavior oldBehavior = frame()->view()->paintBehavior();
223 frame()->view()->setPaintBehavior(oldBehavior | PaintBehaviorRenderingSVGMas k); 237 frame()->view()->setPaintBehavior(oldBehavior | PaintBehaviorRenderingSVGMas k);
224 238
225 // Draw all clipPath children into a global mask.
226 for (Node* childNode = node()->firstChild(); childNode; childNode = childNod e->nextSibling()) { 239 for (Node* childNode = node()->firstChild(); childNode; childNode = childNod e->nextSibling()) {
227 RenderObject* renderer = childNode->renderer(); 240 RenderObject* renderer = childNode->renderer();
228 if (!childNode->isSVGElement() || !renderer) 241 if (!childNode->isSVGElement() || !renderer)
229 continue; 242 continue;
230 if (renderer->needsLayout()) { 243
231 frame()->view()->setPaintBehavior(oldBehavior);
232 return false;
233 }
234 RenderStyle* style = renderer->style(); 244 RenderStyle* style = renderer->style();
235 if (!style || style->display() == NONE || style->visibility() != VISIBLE ) 245 if (!style || style->display() == NONE || style->visibility() != VISIBLE )
236 continue; 246 continue;
237 247
238 WindRule newClipRule = style->svgStyle()->clipRule(); 248 WindRule newClipRule = style->svgStyle()->clipRule();
239 bool isUseElement = childNode->hasTagName(SVGNames::useTag); 249 bool isUseElement = childNode->hasTagName(SVGNames::useTag);
240 if (isUseElement) { 250 if (isUseElement) {
241 SVGUseElement* useElement = toSVGUseElement(childNode); 251 SVGUseElement* useElement = toSVGUseElement(childNode);
242 renderer = useElement->rendererClipChild(); 252 renderer = useElement->rendererClipChild();
243 if (!renderer) 253 if (!renderer)
244 continue; 254 continue;
245 if (!useElement->hasAttribute(SVGNames::clip_ruleAttr)) 255 if (!useElement->hasAttribute(SVGNames::clip_ruleAttr))
246 newClipRule = renderer->style()->svgStyle()->clipRule(); 256 newClipRule = renderer->style()->svgStyle()->clipRule();
247 } 257 }
248 258
249 // Only shapes, paths and texts are allowed for clipping. 259 // Only shapes, paths and texts are allowed for clipping.
250 if (!renderer->isSVGShape() && !renderer->isSVGText()) 260 if (!renderer->isSVGShape() && !renderer->isSVGText())
251 continue; 261 continue;
252 262
253 maskContext->setFillRule(newClipRule); 263 context->setFillRule(newClipRule);
254 264
255 // In the case of a <use> element, we obtained its renderere above, to r etrieve its clipRule. 265 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. 266 renderer = childNode->renderer();
257 // So if isUseElement is true, refetch the childNode->renderer(), as ren derer got overriden above. 267
258 SVGRenderingContext::renderSubtreeToImageBuffer(clipperData->clipMaskIma ge.get(), isUseElement ? childNode->renderer() : renderer, maskContentTransforma tion); 268 SVGRenderingContext::renderSubtree(context, renderer, maskContentTransfo rmation);
259 } 269 }
260 270
261 frame()->view()->setPaintBehavior(oldBehavior); 271 frame()->view()->setPaintBehavior(oldBehavior);
262 return true;
263 } 272 }
264 273
265 void RenderSVGResourceClipper::calculateClipContentRepaintRect() 274 void RenderSVGResourceClipper::calculateClipContentRepaintRect()
266 { 275 {
267 // This is a rough heuristic to appraise the clip size and doesn't consider clip on clip. 276 // 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()) { 277 for (Node* childNode = node()->firstChild(); childNode; childNode = childNod e->nextSibling()) {
269 RenderObject* renderer = childNode->renderer(); 278 RenderObject* renderer = childNode->renderer();
270 if (!childNode->isSVGElement() || !renderer) 279 if (!childNode->isSVGElement() || !renderer)
271 continue; 280 continue;
272 if (!renderer->isSVGShape() && !renderer->isSVGText() && !childNode->has TagName(SVGNames::useTag)) 281 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; 333 AffineTransform transform;
325 transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); 334 transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
326 transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.h eight()); 335 transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.h eight());
327 return transform.mapRect(m_clipBoundaries); 336 return transform.mapRect(m_clipBoundaries);
328 } 337 }
329 338
330 return m_clipBoundaries; 339 return m_clipBoundaries;
331 } 340 }
332 341
333 } 342 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698