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

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

Issue 908243002: Move rendering/svg/RenderSVGResource* to layout/svg. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 5 years, 10 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
(Empty)
1 /*
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>
4 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
5 * Copyright (C) 2011 Dirk Schulze <krit@webkit.org>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 #include "config.h"
24 #include "core/rendering/svg/RenderSVGResourceClipper.h"
25
26 #include "core/SVGNames.h"
27 #include "core/dom/ElementTraversal.h"
28 #include "core/layout/HitTestResult.h"
29 #include "core/layout/PaintInfo.h"
30 #include "core/layout/svg/SVGLayoutSupport.h"
31 #include "core/layout/svg/SVGResources.h"
32 #include "core/layout/svg/SVGResourcesCache.h"
33 #include "core/svg/SVGUseElement.h"
34 #include "platform/RuntimeEnabledFeatures.h"
35 #include "platform/graphics/GraphicsContextStateSaver.h"
36 #include "platform/graphics/paint/ClipPathDisplayItem.h"
37 #include "platform/graphics/paint/DisplayItemList.h"
38 #include "third_party/skia/include/core/SkPicture.h"
39 #include "wtf/TemporaryChange.h"
40
41 namespace blink {
42
43 RenderSVGResourceClipper::RenderSVGResourceClipper(SVGClipPathElement* node)
44 : RenderSVGResourceContainer(node)
45 , m_inClipExpansion(false)
46 {
47 }
48
49 RenderSVGResourceClipper::~RenderSVGResourceClipper()
50 {
51 }
52
53 void RenderSVGResourceClipper::removeAllClientsFromCache(bool markForInvalidatio n)
54 {
55 m_clipContentPicture.clear();
56 m_clipBoundaries = FloatRect();
57 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInval idation : ParentOnlyInvalidation);
58 }
59
60 void RenderSVGResourceClipper::removeClientFromCache(LayoutObject* client, bool markForInvalidation)
61 {
62 ASSERT(client);
63 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidati on : ParentOnlyInvalidation);
64 }
65
66 bool RenderSVGResourceClipper::applyStatefulResource(LayoutObject* object, Graph icsContext*& context, ClipperState& clipperState)
67 {
68 ASSERT(object);
69 ASSERT(context);
70
71 clearInvalidationMask();
72
73 return applyClippingToContext(object, object->objectBoundingBox(), object->p aintInvalidationRectInLocalCoordinates(), context, clipperState);
74 }
75
76 bool RenderSVGResourceClipper::tryPathOnlyClipping(DisplayItemClient client, Gra phicsContext* context,
77 const AffineTransform& animatedLocalTransform, const FloatRect& objectBoundi ngBox) {
78 // If the current clip-path gets clipped itself, we have to fallback to mask ing.
79 if (!style()->svgStyle().clipperResource().isEmpty())
80 return false;
81 WindRule clipRule = RULE_NONZERO;
82 Path clipPath = Path();
83
84 for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element() ); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement )) {
85 LayoutObject* renderer = childElement->renderer();
86 if (!renderer)
87 continue;
88 // Only shapes or paths are supported for direct clipping. We need to fa llback to masking for texts.
89 if (renderer->isSVGText())
90 return false;
91 if (!childElement->isSVGGraphicsElement())
92 continue;
93 SVGGraphicsElement* styled = toSVGGraphicsElement(childElement);
94 const LayoutStyle* style = renderer->style();
95 if (!style || style->display() == NONE || style->visibility() != VISIBLE )
96 continue;
97 const SVGLayoutStyle& svgStyle = style->svgStyle();
98 // Current shape in clip-path gets clipped too. Fallback to masking.
99 if (!svgStyle.clipperResource().isEmpty())
100 return false;
101
102 if (clipPath.isEmpty()) {
103 // First clip shape.
104 styled->toClipPath(clipPath);
105 clipRule = svgStyle.clipRule();
106 clipPath.setWindRule(clipRule);
107 continue;
108 }
109
110 if (RuntimeEnabledFeatures::pathOpsSVGClippingEnabled()) {
111 // Attempt to generate a combined clip path, fall back to masking if not possible.
112 Path subPath;
113 styled->toClipPath(subPath);
114 subPath.setWindRule(svgStyle.clipRule());
115 if (!clipPath.unionPath(subPath))
116 return false;
117 } else {
118 return false;
119 }
120 }
121 // Only one visible shape/path was found. Directly continue clipping and tra nsform the content to userspace if necessary.
122 if (clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
123 AffineTransform transform;
124 transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
125 transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.h eight());
126 clipPath.transform(transform);
127 }
128
129 // Transform path by animatedLocalTransform.
130 clipPath.transform(animatedLocalTransform);
131
132 // The SVG specification wants us to clip everything, if clip-path doesn't h ave a child.
133 if (clipPath.isEmpty())
134 clipPath.addRect(FloatRect());
135
136 if (RuntimeEnabledFeatures::slimmingPaintEnabled()) {
137 context->displayItemList()->add(BeginClipPathDisplayItem::create(client, clipPath, clipRule));
138 } else {
139 BeginClipPathDisplayItem clipPathDisplayItem(client, clipPath, clipRule) ;
140 clipPathDisplayItem.replay(context);
141 }
142
143 return true;
144 }
145
146 bool RenderSVGResourceClipper::applyClippingToContext(LayoutObject* target, cons t FloatRect& targetBoundingBox,
147 const FloatRect& paintInvalidationRect, GraphicsContext* context, ClipperSta te& clipperState)
148 {
149 ASSERT(target);
150 ASSERT(context);
151 ASSERT(clipperState == ClipperNotApplied);
152 ASSERT_WITH_SECURITY_IMPLICATION(!needsLayout());
153
154 if (paintInvalidationRect.isEmpty() || m_inClipExpansion)
155 return false;
156 TemporaryChange<bool> inClipExpansionChange(m_inClipExpansion, true);
157
158 AffineTransform animatedLocalTransform = toSVGClipPathElement(element())->ca lculateAnimatedLocalTransform();
159 // When drawing a clip for non-SVG elements, the CTM does not include the zo om factor.
160 // In this case, we need to apply the zoom scale explicitly - but only for c lips with
161 // userSpaceOnUse units (the zoom is accounted for objectBoundingBox-resolve d lengths).
162 if (!target->isSVG() && clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_USERS PACEONUSE) {
163 ASSERT(style());
164 animatedLocalTransform.scale(style()->effectiveZoom());
165 }
166
167 // First, try to apply the clip as a clipPath.
168 if (tryPathOnlyClipping(target->displayItemClient(), context, animatedLocalT ransform, targetBoundingBox)) {
169 clipperState = ClipperAppliedPath;
170 return true;
171 }
172
173 // Fall back to masking.
174 clipperState = ClipperAppliedMask;
175
176 // Mask layer start
177 context->beginTransparencyLayer(1, &paintInvalidationRect);
178 {
179 GraphicsContextStateSaver maskContentSaver(*context);
180 context->concatCTM(animatedLocalTransform);
181
182 // clipPath can also be clipped by another clipPath.
183 SVGResources* resources = SVGResourcesCache::cachedResourcesForLayoutObj ect(this);
184 RenderSVGResourceClipper* clipPathClipper = resources ? resources->clipp er() : 0;
185 ClipperState clipPathClipperState = ClipperNotApplied;
186 if (clipPathClipper && !clipPathClipper->applyClippingToContext(this, ta rgetBoundingBox, paintInvalidationRect, context, clipPathClipperState)) {
187 // FIXME: Awkward state micro-management. Ideally, GraphicsContextSt ateSaver should
188 // a) pop saveLayers also
189 // b) pop multiple states if needed (similarly to SkCanvas::restor eToCount())
190 // Then we should be able to replace this mess with a single, top-le vel GCSS.
191 maskContentSaver.restore();
192 context->endLayer();
193 return false;
194 }
195
196 drawClipMaskContent(context, targetBoundingBox);
197
198 if (clipPathClipper)
199 clipPathClipper->postApplyStatefulResource(this, context, clipPathCl ipperState);
200 }
201
202 // Masked content layer start.
203 context->beginLayer(1, SkXfermode::kSrcIn_Mode, &paintInvalidationRect);
204
205 return true;
206 }
207
208 void RenderSVGResourceClipper::postApplyStatefulResource(LayoutObject* target, G raphicsContext*& context, ClipperState& clipperState)
209 {
210 switch (clipperState) {
211 case ClipperAppliedPath:
212 // Path-only clipping, no layers to restore but we need to emit an end t o the clip path display item.
213 {
214 if (RuntimeEnabledFeatures::slimmingPaintEnabled()) {
215 context->displayItemList()->add(EndClipPathDisplayItem::create(t arget->displayItemClient()));
216 } else {
217 EndClipPathDisplayItem endClipPathDisplayItem(target->displayIte mClient());
218 endClipPathDisplayItem.replay(context);
219 }
220 }
221 break;
222 case ClipperAppliedMask:
223 // Transfer content layer -> mask layer (SrcIn)
224 context->endLayer();
225 // Transfer mask layer -> bg layer (SrcOver)
226 context->endLayer();
227 break;
228 default:
229 ASSERT_NOT_REACHED();
230 }
231 }
232
233 void RenderSVGResourceClipper::drawClipMaskContent(GraphicsContext* context, con st FloatRect& targetBoundingBox)
234 {
235 ASSERT(context);
236
237 AffineTransform contentTransformation;
238 if (clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
239 contentTransformation.translate(targetBoundingBox.x(), targetBoundingBox .y());
240 contentTransformation.scaleNonUniform(targetBoundingBox.width(), targetB oundingBox.height());
241 context->concatCTM(contentTransformation);
242 }
243
244 if (!m_clipContentPicture) {
245 SubtreeContentTransformScope contentTransformScope(contentTransformation );
246 createPicture(context);
247 }
248
249 context->drawPicture(m_clipContentPicture.get());
250 }
251
252 void RenderSVGResourceClipper::createPicture(GraphicsContext* context)
253 {
254 ASSERT(context);
255 ASSERT(frame());
256
257 // Using strokeBoundingBox (instead of paintInvalidationRectInLocalCoordinat es) to avoid the intersection
258 // with local clips/mask, which may yield incorrect results when mixing obje ctBoundingBox and
259 // userSpaceOnUse units (http://crbug.com/294900).
260 FloatRect bounds = strokeBoundingBox();
261 context->beginRecording(bounds);
262
263 for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element() ); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement )) {
264 LayoutObject* renderer = childElement->renderer();
265 if (!renderer)
266 continue;
267
268 const LayoutStyle* style = renderer->style();
269 if (!style || style->display() == NONE || style->visibility() != VISIBLE )
270 continue;
271
272 WindRule newClipRule = style->svgStyle().clipRule();
273 bool isUseElement = isSVGUseElement(*childElement);
274 if (isUseElement) {
275 SVGUseElement& useElement = toSVGUseElement(*childElement);
276 renderer = useElement.rendererClipChild();
277 if (!renderer)
278 continue;
279 if (!useElement.hasAttribute(SVGNames::clip_ruleAttr))
280 newClipRule = renderer->style()->svgStyle().clipRule();
281 }
282
283 // Only shapes, paths and texts are allowed for clipping.
284 if (!renderer->isSVGShape() && !renderer->isSVGText())
285 continue;
286
287 context->setFillRule(newClipRule);
288
289 if (isUseElement)
290 renderer = childElement->renderer();
291
292 // Switch to a paint behavior where all children of this <clipPath> will be rendered using special constraints:
293 // - fill-opacity/stroke-opacity/opacity set to 1
294 // - masker/filter not applied when rendering the children
295 // - fill is set to the initial fill paint server (solid, black)
296 // - stroke is set to the initial stroke paint server (none)
297 PaintInfo info(context, LayoutRect::infiniteIntRect(), PaintPhaseForegro und, PaintBehaviorRenderingClipPathAsMask);
298 renderer->paint(info, IntPoint());
299 }
300
301 m_clipContentPicture = context->endRecording();
302 }
303
304 void RenderSVGResourceClipper::calculateClipContentPaintInvalidationRect()
305 {
306 // This is a rough heuristic to appraise the clip size and doesn't consider clip on clip.
307 for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element() ); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement )) {
308 LayoutObject* renderer = childElement->renderer();
309 if (!renderer)
310 continue;
311 if (!renderer->isSVGShape() && !renderer->isSVGText() && !isSVGUseElemen t(*childElement))
312 continue;
313 const LayoutStyle* style = renderer->style();
314 if (!style || style->display() == NONE || style->visibility() != VISIBLE )
315 continue;
316 m_clipBoundaries.unite(renderer->localToParentTransform().mapRect(render er->paintInvalidationRectInLocalCoordinates()));
317 }
318 m_clipBoundaries = toSVGClipPathElement(element())->calculateAnimatedLocalTr ansform().mapRect(m_clipBoundaries);
319 }
320
321 bool RenderSVGResourceClipper::hitTestClipContent(const FloatRect& objectBoundin gBox, const FloatPoint& nodeAtPoint)
322 {
323 FloatPoint point = nodeAtPoint;
324 if (!SVGLayoutSupport::pointInClippingArea(this, point))
325 return false;
326
327 if (clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
328 AffineTransform transform;
329 transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
330 transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.h eight());
331 point = transform.inverse().mapPoint(point);
332 }
333
334 AffineTransform animatedLocalTransform = toSVGClipPathElement(element())->ca lculateAnimatedLocalTransform();
335 if (!animatedLocalTransform.isInvertible())
336 return false;
337
338 point = animatedLocalTransform.inverse().mapPoint(point);
339
340 for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element() ); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement )) {
341 LayoutObject* renderer = childElement->renderer();
342 if (!renderer)
343 continue;
344 if (!renderer->isSVGShape() && !renderer->isSVGText() && !isSVGUseElemen t(*childElement))
345 continue;
346 IntPoint hitPoint;
347 HitTestResult result(hitPoint);
348 if (renderer->nodeAtFloatPoint(HitTestRequest(HitTestRequest::SVGClipCon tent), result, point, HitTestForeground))
349 return true;
350 }
351
352 return false;
353 }
354
355 FloatRect RenderSVGResourceClipper::resourceBoundingBox(const LayoutObject* obje ct)
356 {
357 // Resource was not layouted yet. Give back the boundingBox of the object.
358 if (selfNeedsLayout())
359 return object->objectBoundingBox();
360
361 if (m_clipBoundaries.isEmpty())
362 calculateClipContentPaintInvalidationRect();
363
364 if (clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
365 FloatRect objectBoundingBox = object->objectBoundingBox();
366 AffineTransform transform;
367 transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
368 transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.h eight());
369 return transform.mapRect(m_clipBoundaries);
370 }
371
372 return m_clipBoundaries;
373 }
374
375 }
OLDNEW
« no previous file with comments | « Source/core/rendering/svg/RenderSVGResourceClipper.h ('k') | Source/core/rendering/svg/RenderSVGResourceContainer.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698