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

Side by Side Diff: third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceClipper.cpp

Issue 2560513002: Unify predicates for elements "contributing" to a <clipPath> (Closed)
Patch Set: Created 4 years 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
« no previous file with comments | « no previous file | third_party/WebKit/Source/core/svg/SVGUseElement.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details. 15 * Library General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Library General Public License 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 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, 19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA. 20 * Boston, MA 02110-1301, USA.
21 */ 21 */
22 22
23 #include "core/layout/svg/LayoutSVGResourceClipper.h" 23 #include "core/layout/svg/LayoutSVGResourceClipper.h"
24 24
25 #include "core/SVGNames.h"
26 #include "core/dom/ElementTraversal.h" 25 #include "core/dom/ElementTraversal.h"
27 #include "core/layout/HitTestResult.h" 26 #include "core/layout/HitTestResult.h"
28 #include "core/layout/svg/SVGLayoutSupport.h" 27 #include "core/layout/svg/SVGLayoutSupport.h"
29 #include "core/paint/PaintInfo.h" 28 #include "core/paint/PaintInfo.h"
30 #include "core/svg/SVGGeometryElement.h" 29 #include "core/svg/SVGGeometryElement.h"
31 #include "core/svg/SVGUseElement.h" 30 #include "core/svg/SVGUseElement.h"
32 #include "platform/RuntimeEnabledFeatures.h"
33 #include "platform/graphics/paint/SkPictureBuilder.h" 31 #include "platform/graphics/paint/SkPictureBuilder.h"
34 #include "third_party/skia/include/core/SkPicture.h" 32 #include "third_party/skia/include/core/SkPicture.h"
35 #include "third_party/skia/include/pathops/SkPathOps.h" 33 #include "third_party/skia/include/pathops/SkPathOps.h"
36 34
37 namespace blink { 35 namespace blink {
38 36
37 namespace {
38
39 bool requiresMask(const SVGElement& childElement) {
40 // TODO(fs): This needs to special-case <use> in a way similar to
41 // contributesToClip. (crbug.com/604677)
42 LayoutObject* layoutObject = childElement.layoutObject();
43 DCHECK(layoutObject);
44 // Only basic shapes or paths are supported for direct clipping. We need to
45 // fallback to masking for texts.
46 if (layoutObject->isSVGText())
47 return true;
48 // Current shape in clip-path gets clipped too. Fallback to masking.
49 return layoutObject->styleRef().clipPath();
50 }
51
52 bool contributesToClip(const SVGGraphicsElement& element) {
53 const LayoutObject* layoutObject = element.layoutObject();
54 if (!layoutObject)
55 return false;
56 const ComputedStyle& style = layoutObject->styleRef();
57 if (style.display() == EDisplay::None ||
58 style.visibility() != EVisibility::Visible)
59 return false;
60 // Only shapes, paths and texts are allowed for clipping.
61 return layoutObject->isSVGShape() || layoutObject->isSVGText();
62 }
63
64 bool contributesToClip(const SVGElement& element) {
65 // <use> within <clipPath> have a restricted content model.
66 // (https://drafts.fxtf.org/css-masking-1/#ClipPathElement)
67 if (isSVGUseElement(element)) {
68 const LayoutObject* useLayoutObject = element.layoutObject();
69 if (!useLayoutObject ||
70 useLayoutObject->styleRef().display() == EDisplay::None)
71 return false;
72 const SVGGraphicsElement* clippingElement =
73 toSVGUseElement(element).visibleTargetGraphicsElementForClipping();
74 if (!clippingElement)
75 return false;
76 return contributesToClip(*clippingElement);
77 }
78 if (!element.isSVGGraphicsElement())
79 return false;
80 return contributesToClip(toSVGGraphicsElement(element));
81 }
82
83 void pathFromElement(const SVGElement& element, Path& clipPath) {
84 if (isSVGGeometryElement(element))
85 toSVGGeometryElement(element).toClipPath(clipPath);
86 else if (isSVGUseElement(element))
87 toSVGUseElement(element).toClipPath(clipPath);
88 }
89
90 } // namespace
91
39 LayoutSVGResourceClipper::LayoutSVGResourceClipper(SVGClipPathElement* node) 92 LayoutSVGResourceClipper::LayoutSVGResourceClipper(SVGClipPathElement* node)
40 : LayoutSVGResourceContainer(node), m_inClipExpansion(false) {} 93 : LayoutSVGResourceContainer(node), m_inClipExpansion(false) {}
41 94
42 LayoutSVGResourceClipper::~LayoutSVGResourceClipper() {} 95 LayoutSVGResourceClipper::~LayoutSVGResourceClipper() {}
43 96
44 void LayoutSVGResourceClipper::removeAllClientsFromCache( 97 void LayoutSVGResourceClipper::removeAllClientsFromCache(
45 bool markForInvalidation) { 98 bool markForInvalidation) {
46 m_clipContentPath.clear(); 99 m_clipContentPath.clear();
47 m_clipContentPicture.reset(); 100 m_clipContentPicture.reset();
48 m_localClipBounds = FloatRect(); 101 m_localClipBounds = FloatRect();
(...skipping 16 matching lines...) Expand all
65 118
66 // If the current clip-path gets clipped itself, we have to fallback to 119 // If the current clip-path gets clipped itself, we have to fallback to
67 // masking. 120 // masking.
68 if (styleRef().clipPath()) 121 if (styleRef().clipPath())
69 return false; 122 return false;
70 123
71 unsigned opCount = 0; 124 unsigned opCount = 0;
72 bool usingBuilder = false; 125 bool usingBuilder = false;
73 SkOpBuilder clipPathBuilder; 126 SkOpBuilder clipPathBuilder;
74 127
75 for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); 128 for (const SVGElement& childElement :
76 childElement; 129 Traversal<SVGElement>::childrenOf(*element())) {
77 childElement = Traversal<SVGElement>::nextSibling(*childElement)) { 130 if (!contributesToClip(childElement))
78 LayoutObject* childLayoutObject = childElement->layoutObject();
79 if (!childLayoutObject)
80 continue;
81 // Only shapes or paths are supported for direct clipping. We need to
82 // fallback to masking for texts.
83 if (childLayoutObject->isSVGText()) {
84 m_clipContentPath.clear();
85 return false;
86 }
87 if (!childElement->isSVGGraphicsElement())
88 continue; 131 continue;
89 132
90 const ComputedStyle* style = childLayoutObject->style(); 133 if (requiresMask(childElement)) {
91 if (!style || style->display() == EDisplay::None ||
92 (style->visibility() != EVisibility::Visible &&
93 !isSVGUseElement(*childElement)))
94 continue;
95
96 // Current shape in clip-path gets clipped too. Fallback to masking.
97 if (style->clipPath()) {
98 m_clipContentPath.clear(); 134 m_clipContentPath.clear();
99 return false; 135 return false;
100 } 136 }
101 137
102 // First clip shape. 138 // First clip shape.
103 if (m_clipContentPath.isEmpty()) { 139 if (m_clipContentPath.isEmpty()) {
104 if (isSVGGeometryElement(childElement)) 140 pathFromElement(childElement, m_clipContentPath);
105 toSVGGeometryElement(childElement)->toClipPath(m_clipContentPath);
106 else if (isSVGUseElement(childElement))
107 toSVGUseElement(childElement)->toClipPath(m_clipContentPath);
108
109 continue; 141 continue;
110 } 142 }
111 143
112 // Multiple shapes require PathOps. In some degenerate cases PathOps can 144 // Multiple shapes require PathOps. In some degenerate cases PathOps can
113 // exhibit quadratic behavior, so we cap the number of ops to a reasonable 145 // exhibit quadratic behavior, so we cap the number of ops to a reasonable
114 // count. 146 // count.
115 const unsigned kMaxOps = 42; 147 const unsigned kMaxOps = 42;
116 if (++opCount > kMaxOps) { 148 if (++opCount > kMaxOps) {
117 m_clipContentPath.clear(); 149 m_clipContentPath.clear();
118 return false; 150 return false;
119 } 151 }
120 152
121 // Second clip shape => start using the builder. 153 // Second clip shape => start using the builder.
122 if (!usingBuilder) { 154 if (!usingBuilder) {
123 clipPathBuilder.add(m_clipContentPath.getSkPath(), kUnion_SkPathOp); 155 clipPathBuilder.add(m_clipContentPath.getSkPath(), kUnion_SkPathOp);
124 usingBuilder = true; 156 usingBuilder = true;
125 } 157 }
126 158
127 Path subPath; 159 Path subPath;
128 if (isSVGGeometryElement(childElement)) 160 pathFromElement(childElement, subPath);
129 toSVGGeometryElement(childElement)->toClipPath(subPath);
130 else if (isSVGUseElement(childElement))
131 toSVGUseElement(childElement)->toClipPath(subPath);
132 161
133 clipPathBuilder.add(subPath.getSkPath(), kUnion_SkPathOp); 162 clipPathBuilder.add(subPath.getSkPath(), kUnion_SkPathOp);
134 } 163 }
135 164
136 if (usingBuilder) { 165 if (usingBuilder) {
137 SkPath resolvedPath; 166 SkPath resolvedPath;
138 clipPathBuilder.resolve(&resolvedPath); 167 clipPathBuilder.resolve(&resolvedPath);
139 m_clipContentPath = resolvedPath; 168 m_clipContentPath = resolvedPath;
140 } 169 }
141 170
(...skipping 28 matching lines...) Expand all
170 if (m_clipContentPicture) 199 if (m_clipContentPicture)
171 return m_clipContentPicture; 200 return m_clipContentPicture;
172 201
173 // Using strokeBoundingBox (instead of visualRectInLocalSVGCoordinates) to 202 // Using strokeBoundingBox (instead of visualRectInLocalSVGCoordinates) to
174 // avoid the intersection with local clips/mask, which may yield incorrect 203 // avoid the intersection with local clips/mask, which may yield incorrect
175 // results when mixing objectBoundingBox and userSpaceOnUse units 204 // results when mixing objectBoundingBox and userSpaceOnUse units
176 // (http://crbug.com/294900). 205 // (http://crbug.com/294900).
177 FloatRect bounds = strokeBoundingBox(); 206 FloatRect bounds = strokeBoundingBox();
178 207
179 SkPictureBuilder pictureBuilder(bounds, nullptr, nullptr); 208 SkPictureBuilder pictureBuilder(bounds, nullptr, nullptr);
209 // Switch to a paint behavior where all children of this <clipPath> will be
210 // laid out using special constraints:
211 // - fill-opacity/stroke-opacity/opacity set to 1
212 // - masker/filter not applied when laying out the children
213 // - fill is set to the initial fill paint server (solid, black)
214 // - stroke is set to the initial stroke paint server (none)
215 PaintInfo info(pictureBuilder.context(), LayoutRect::infiniteIntRect(),
216 PaintPhaseForeground, GlobalPaintNormalPhase,
217 PaintLayerPaintingRenderingClipPathAsMask);
180 218
181 for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); 219 for (const SVGElement& childElement :
182 childElement; 220 Traversal<SVGElement>::childrenOf(*element())) {
183 childElement = Traversal<SVGElement>::nextSibling(*childElement)) { 221 if (!contributesToClip(childElement))
184 LayoutObject* layoutObject = childElement->layoutObject();
185 if (!layoutObject)
186 continue; 222 continue;
187 223 // Use the LayoutObject of the direct child even if it is a <use>. In that
188 const ComputedStyle* style = layoutObject->style(); 224 // case, we will paint the targeted element indirectly.
189 if (!style || style->display() == EDisplay::None || 225 const LayoutObject* layoutObject = childElement.layoutObject();
190 (style->visibility() != EVisibility::Visible &&
191 !isSVGUseElement(*childElement)))
192 continue;
193
194 bool isUseElement = isSVGUseElement(*childElement);
195 if (isUseElement) {
196 const SVGGraphicsElement* clippingElement =
197 toSVGUseElement(*childElement)
198 .visibleTargetGraphicsElementForClipping();
199 if (!clippingElement)
200 continue;
201
202 layoutObject = clippingElement->layoutObject();
203 if (!layoutObject)
204 continue;
205 }
206
207 // Only shapes, paths and texts are allowed for clipping.
208 if (!layoutObject->isSVGShape() && !layoutObject->isSVGText())
209 continue;
210
211 if (isUseElement)
212 layoutObject = childElement->layoutObject();
213
214 // Switch to a paint behavior where all children of this <clipPath> will be
215 // laid out using special constraints:
216 // - fill-opacity/stroke-opacity/opacity set to 1
217 // - masker/filter not applied when laying out the children
218 // - fill is set to the initial fill paint server (solid, black)
219 // - stroke is set to the initial stroke paint server (none)
220 PaintInfo info(pictureBuilder.context(), LayoutRect::infiniteIntRect(),
221 PaintPhaseForeground, GlobalPaintNormalPhase,
222 PaintLayerPaintingRenderingClipPathAsMask);
223 layoutObject->paint(info, IntPoint()); 226 layoutObject->paint(info, IntPoint());
224 } 227 }
225 228
226 m_clipContentPicture = pictureBuilder.endRecording(); 229 m_clipContentPicture = pictureBuilder.endRecording();
227 return m_clipContentPicture; 230 return m_clipContentPicture;
228 } 231 }
229 232
230 void LayoutSVGResourceClipper::calculateLocalClipBounds() { 233 void LayoutSVGResourceClipper::calculateLocalClipBounds() {
231 // This is a rough heuristic to appraise the clip size and doesn't consider 234 // This is a rough heuristic to appraise the clip size and doesn't consider
232 // clip on clip. 235 // clip on clip.
233 for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); 236 for (const SVGElement& childElement :
234 childElement; 237 Traversal<SVGElement>::childrenOf(*element())) {
235 childElement = Traversal<SVGElement>::nextSibling(*childElement)) { 238 if (!contributesToClip(childElement))
236 LayoutObject* layoutObject = childElement->layoutObject();
237 if (!layoutObject)
238 continue; 239 continue;
239 if (!layoutObject->isSVGShape() && !layoutObject->isSVGText() && 240 const LayoutObject* layoutObject = childElement.layoutObject();
240 !isSVGUseElement(*childElement))
241 continue;
242 const ComputedStyle* style = layoutObject->style();
243 if (!style || style->display() == EDisplay::None ||
244 (style->visibility() != EVisibility::Visible &&
245 !isSVGUseElement(*childElement)))
246 continue;
247 if (isSVGUseElement(*childElement) &&
248 !toSVGUseElement(*childElement)
249 .visibleTargetGraphicsElementForClipping())
250 continue;
251
252 m_localClipBounds.unite(layoutObject->localToSVGParentTransform().mapRect( 241 m_localClipBounds.unite(layoutObject->localToSVGParentTransform().mapRect(
253 layoutObject->visualRectInLocalSVGCoordinates())); 242 layoutObject->visualRectInLocalSVGCoordinates()));
254 } 243 }
255 } 244 }
256 245
257 bool LayoutSVGResourceClipper::hitTestClipContent( 246 bool LayoutSVGResourceClipper::hitTestClipContent(
258 const FloatRect& objectBoundingBox, 247 const FloatRect& objectBoundingBox,
259 const FloatPoint& nodeAtPoint) { 248 const FloatPoint& nodeAtPoint) {
260 FloatPoint point = nodeAtPoint; 249 FloatPoint point = nodeAtPoint;
261 if (!SVGLayoutSupport::pointInClippingArea(*this, point)) 250 if (!SVGLayoutSupport::pointInClippingArea(*this, point))
262 return false; 251 return false;
263 252
264 if (clipPathUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox) { 253 if (clipPathUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox) {
265 AffineTransform transform; 254 AffineTransform transform;
266 transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); 255 transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
267 transform.scaleNonUniform(objectBoundingBox.width(), 256 transform.scaleNonUniform(objectBoundingBox.width(),
268 objectBoundingBox.height()); 257 objectBoundingBox.height());
269 point = transform.inverse().mapPoint(point); 258 point = transform.inverse().mapPoint(point);
270 } 259 }
271 260
272 AffineTransform animatedLocalTransform = 261 AffineTransform animatedLocalTransform =
273 toSVGClipPathElement(element())->calculateTransform( 262 toSVGClipPathElement(element())->calculateTransform(
274 SVGElement::IncludeMotionTransform); 263 SVGElement::IncludeMotionTransform);
275 if (!animatedLocalTransform.isInvertible()) 264 if (!animatedLocalTransform.isInvertible())
276 return false; 265 return false;
277 266
278 point = animatedLocalTransform.inverse().mapPoint(point); 267 point = animatedLocalTransform.inverse().mapPoint(point);
279 268
280 for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); 269 for (const SVGElement& childElement :
281 childElement; 270 Traversal<SVGElement>::childrenOf(*element())) {
282 childElement = Traversal<SVGElement>::nextSibling(*childElement)) { 271 if (!contributesToClip(childElement))
283 LayoutObject* layoutObject = childElement->layoutObject();
284 if (!layoutObject)
285 continue;
286 if (!layoutObject->isSVGShape() && !layoutObject->isSVGText() &&
287 !isSVGUseElement(*childElement))
288 continue; 272 continue;
289 IntPoint hitPoint; 273 IntPoint hitPoint;
290 HitTestResult result(HitTestRequest::SVGClipContent, hitPoint); 274 HitTestResult result(HitTestRequest::SVGClipContent, hitPoint);
275 LayoutObject* layoutObject = childElement.layoutObject();
291 if (layoutObject->nodeAtFloatPoint(result, point, HitTestForeground)) 276 if (layoutObject->nodeAtFloatPoint(result, point, HitTestForeground))
292 return true; 277 return true;
293 } 278 }
294
295 return false; 279 return false;
296 } 280 }
297 281
298 FloatRect LayoutSVGResourceClipper::resourceBoundingBox( 282 FloatRect LayoutSVGResourceClipper::resourceBoundingBox(
299 const FloatRect& referenceBox) { 283 const FloatRect& referenceBox) {
300 // The resource has not been layouted yet. Return the reference box. 284 // The resource has not been layouted yet. Return the reference box.
301 if (selfNeedsLayout()) 285 if (selfNeedsLayout())
302 return referenceBox; 286 return referenceBox;
303 287
304 if (m_localClipBounds.isEmpty()) 288 if (m_localClipBounds.isEmpty())
305 calculateLocalClipBounds(); 289 calculateLocalClipBounds();
306 290
307 AffineTransform transform = 291 AffineTransform transform =
308 toSVGClipPathElement(element())->calculateTransform( 292 toSVGClipPathElement(element())->calculateTransform(
309 SVGElement::IncludeMotionTransform); 293 SVGElement::IncludeMotionTransform);
310 if (clipPathUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox) { 294 if (clipPathUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox) {
311 transform.translate(referenceBox.x(), referenceBox.y()); 295 transform.translate(referenceBox.x(), referenceBox.y());
312 transform.scaleNonUniform(referenceBox.width(), referenceBox.height()); 296 transform.scaleNonUniform(referenceBox.width(), referenceBox.height());
313 } 297 }
314 return transform.mapRect(m_localClipBounds); 298 return transform.mapRect(m_localClipBounds);
315 } 299 }
316 300
317 } // namespace blink 301 } // namespace blink
OLDNEW
« no previous file with comments | « no previous file | third_party/WebKit/Source/core/svg/SVGUseElement.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698