Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / rendering / svg / RenderSVGShape.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2008 Rob Buis <buis@kde.org>
4  * Copyright (C) 2005, 2007 Eric Seidel <eric@webkit.org>
5  * Copyright (C) 2009 Google, Inc.
6  * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
7  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
8  * Copyright (C) 2009 Jeff Schiller <codedread@gmail.com>
9  * Copyright (C) 2011 Renata Hodovan <reni@webkit.org>
10  * Copyright (C) 2011 University of Szeged
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Library General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public License
23  * along with this library; see the file COPYING.LIB.  If not, write to
24  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25  * Boston, MA 02110-1301, USA.
26  */
27
28 #include "config.h"
29 #include "core/rendering/svg/RenderSVGShape.h"
30
31 #include "core/rendering/GraphicsContextAnnotator.h"
32 #include "core/rendering/HitTestRequest.h"
33 #include "core/rendering/PointerEventsHitRules.h"
34 #include "core/rendering/svg/RenderSVGResourceMarker.h"
35 #include "core/rendering/svg/RenderSVGResourceSolidColor.h"
36 #include "core/rendering/svg/SVGPathData.h"
37 #include "core/rendering/svg/SVGRenderSupport.h"
38 #include "core/rendering/svg/SVGRenderingContext.h"
39 #include "core/rendering/svg/SVGResources.h"
40 #include "core/rendering/svg/SVGResourcesCache.h"
41 #include "core/svg/SVGGraphicsElement.h"
42 #include "platform/geometry/FloatPoint.h"
43 #include "platform/graphics/GraphicsContextStateSaver.h"
44 #include "wtf/MathExtras.h"
45
46 namespace blink {
47
48 RenderSVGShape::RenderSVGShape(SVGGraphicsElement* node)
49     : RenderSVGModelObject(node)
50     , m_needsBoundariesUpdate(false) // Default is false, the cached rects are empty from the beginning.
51     , m_needsShapeUpdate(true) // Default is true, so we grab a Path object once from SVGGraphicsElement.
52     , m_needsTransformUpdate(true) // Default is true, so we grab a AffineTransform object once from SVGGraphicsElement.
53 {
54 }
55
56 RenderSVGShape::~RenderSVGShape()
57 {
58 }
59
60 void RenderSVGShape::updateShapeFromElement()
61 {
62     m_path.clear();
63     m_path = adoptPtr(new Path);
64     ASSERT(RenderSVGShape::isShapeEmpty());
65
66     updatePathFromGraphicsElement(toSVGGraphicsElement(element()), path());
67     processMarkerPositions();
68
69     m_fillBoundingBox = calculateObjectBoundingBox();
70     m_strokeBoundingBox = calculateStrokeBoundingBox();
71 }
72
73 void RenderSVGShape::fillShape(GraphicsContext* context) const
74 {
75     context->fillPath(path());
76 }
77
78 void RenderSVGShape::strokeShape(GraphicsContext* context) const
79 {
80     ASSERT(m_path);
81     Path* usePath = m_path.get();
82
83     if (hasNonScalingStroke())
84         usePath = nonScalingStrokePath(usePath, nonScalingStrokeTransform());
85
86     context->strokePath(*usePath);
87 }
88
89 bool RenderSVGShape::shapeDependentStrokeContains(const FloatPoint& point)
90 {
91     ASSERT(m_path);
92     StrokeData strokeData;
93     SVGRenderSupport::applyStrokeStyleToStrokeData(&strokeData, style(), this);
94
95     if (hasNonScalingStroke()) {
96         AffineTransform nonScalingTransform = nonScalingStrokeTransform();
97         Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
98
99         return usePath->strokeContains(nonScalingTransform.mapPoint(point), strokeData);
100     }
101
102     return m_path->strokeContains(point, strokeData);
103 }
104
105 bool RenderSVGShape::shapeDependentFillContains(const FloatPoint& point, const WindRule fillRule) const
106 {
107     return path().contains(point, fillRule);
108 }
109
110 bool RenderSVGShape::fillContains(const FloatPoint& point, bool requiresFill, const WindRule fillRule)
111 {
112     if (!m_fillBoundingBox.contains(point))
113         return false;
114
115     bool hasFallback;
116     if (requiresFill && !RenderSVGResource::requestPaintingResource(ApplyToFillMode, this, style(), hasFallback))
117         return false;
118
119     return shapeDependentFillContains(point, fillRule);
120 }
121
122 bool RenderSVGShape::strokeContains(const FloatPoint& point, bool requiresStroke)
123 {
124     if (!strokeBoundingBox().contains(point))
125         return false;
126
127     bool hasFallback;
128     if (requiresStroke && !RenderSVGResource::requestPaintingResource(ApplyToStrokeMode, this, style(), hasFallback))
129         return false;
130
131     return shapeDependentStrokeContains(point);
132 }
133
134 void RenderSVGShape::layout()
135 {
136     bool updateCachedBoundariesInParents = false;
137
138     if (m_needsShapeUpdate || m_needsBoundariesUpdate) {
139         updateShapeFromElement();
140         m_needsShapeUpdate = false;
141         updatePaintInvalidationBoundingBox();
142         m_needsBoundariesUpdate = false;
143         updateCachedBoundariesInParents = true;
144     }
145
146     if (m_needsTransformUpdate) {
147         m_localTransform =  toSVGGraphicsElement(element())->animatedLocalTransform();
148         m_needsTransformUpdate = false;
149         updateCachedBoundariesInParents = true;
150     }
151
152     // Invalidate all resources of this client if our layout changed.
153     if (everHadLayout() && selfNeedsLayout())
154         SVGResourcesCache::clientLayoutChanged(this);
155
156     // If our bounds changed, notify the parents.
157     if (updateCachedBoundariesInParents)
158         RenderSVGModelObject::setNeedsBoundariesUpdate();
159
160     clearNeedsLayout();
161 }
162
163 Path* RenderSVGShape::nonScalingStrokePath(const Path* path, const AffineTransform& strokeTransform) const
164 {
165     DEFINE_STATIC_LOCAL(Path, tempPath, ());
166
167     tempPath = *path;
168     tempPath.transform(strokeTransform);
169
170     return &tempPath;
171 }
172
173 bool RenderSVGShape::setupNonScalingStrokeContext(AffineTransform& strokeTransform, GraphicsContextStateSaver& stateSaver)
174 {
175     if (!strokeTransform.isInvertible())
176         return false;
177
178     stateSaver.save();
179     stateSaver.context()->concatCTM(strokeTransform.inverse());
180     return true;
181 }
182
183 AffineTransform RenderSVGShape::nonScalingStrokeTransform() const
184 {
185     return toSVGGraphicsElement(element())->getScreenCTM(SVGGraphicsElement::DisallowStyleUpdate);
186 }
187
188 bool RenderSVGShape::shouldGenerateMarkerPositions() const
189 {
190     if (!style()->svgStyle().hasMarkers())
191         return false;
192
193     if (!SVGResources::supportsMarkers(*toSVGGraphicsElement(element())))
194         return false;
195
196     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
197     if (!resources)
198         return false;
199
200     return resources->markerStart() || resources->markerMid() || resources->markerEnd();
201 }
202
203 void RenderSVGShape::fillShape(RenderStyle* style, GraphicsContext* context)
204 {
205     bool hasFallback;
206     if (RenderSVGResource* fillPaintingResource = RenderSVGResource::requestPaintingResource(ApplyToFillMode, this, style, hasFallback)) {
207         if (fillPaintingResource->applyResource(this, style, context, ApplyToFillMode)) {
208             fillShape(context);
209             fillPaintingResource->postApplyResource(this, context);
210         } else if (hasFallback) {
211             RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
212             if (fallbackResource->applyResource(this, style, context, ApplyToFillMode)) {
213                 fillShape(context);
214                 fallbackResource->postApplyResource(this, context);
215             }
216         }
217     }
218 }
219
220 void RenderSVGShape::strokeShape(RenderStyle* style, GraphicsContext* context)
221 {
222     bool hasFallback;
223     if (RenderSVGResource* strokePaintingResource = RenderSVGResource::requestPaintingResource(ApplyToStrokeMode, this, style, hasFallback)) {
224         if (strokePaintingResource->applyResource(this, style, context, ApplyToStrokeMode)) {
225             strokeShape(context);
226             strokePaintingResource->postApplyResource(this, context);
227         } else if (hasFallback) {
228             RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
229             if (fallbackResource->applyResource(this, style, context, ApplyToStrokeMode)) {
230                 strokeShape(context);
231                 fallbackResource->postApplyResource(this, context);
232             }
233         }
234     }
235 }
236
237 void RenderSVGShape::paint(PaintInfo& paintInfo, const LayoutPoint&)
238 {
239     ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
240     if (paintInfo.phase != PaintPhaseForeground
241         || style()->visibility() == HIDDEN
242         || isShapeEmpty())
243         return;
244
245     FloatRect boundingBox = paintInvalidationRectInLocalCoordinates();
246     if (!SVGRenderSupport::paintInfoIntersectsPaintInvalidationRect(boundingBox, m_localTransform, paintInfo))
247         return;
248
249     PaintInfo childPaintInfo(paintInfo);
250
251     GraphicsContextStateSaver stateSaver(*childPaintInfo.context);
252     childPaintInfo.applyTransform(m_localTransform);
253
254     SVGRenderingContext renderingContext(this, childPaintInfo);
255
256     if (renderingContext.isRenderingPrepared()) {
257         const SVGRenderStyle& svgStyle = style()->svgStyle();
258         if (svgStyle.shapeRendering() == SR_CRISPEDGES)
259             childPaintInfo.context->setShouldAntialias(false);
260
261         for (int i = 0; i < 3; i++) {
262             switch (svgStyle.paintOrderType(i)) {
263             case PT_FILL:
264                 fillShape(this->style(), childPaintInfo.context);
265                 break;
266             case PT_STROKE:
267                 if (svgStyle.hasVisibleStroke()) {
268                     GraphicsContextStateSaver stateSaver(*childPaintInfo.context, false);
269                     AffineTransform nonScalingTransform;
270
271                     if (hasNonScalingStroke()) {
272                         AffineTransform nonScalingTransform = nonScalingStrokeTransform();
273                         if (!setupNonScalingStrokeContext(nonScalingTransform, stateSaver))
274                             return;
275                     }
276
277                     strokeShape(this->style(), childPaintInfo.context);
278                 }
279                 break;
280             case PT_MARKERS:
281                 if (!m_markerPositions.isEmpty())
282                     drawMarkers(childPaintInfo);
283                 break;
284             default:
285                 ASSERT_NOT_REACHED();
286                 break;
287             }
288         }
289     }
290
291     if (style()->outlineWidth())
292         paintOutline(childPaintInfo, IntRect(boundingBox));
293 }
294
295 // This method is called from inside paintOutline() since we call paintOutline()
296 // while transformed to our coord system, return local coords
297 void RenderSVGShape::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint&, const RenderLayerModelObject*) const
298 {
299     LayoutRect rect = LayoutRect(paintInvalidationRectInLocalCoordinates());
300     if (!rect.isEmpty())
301         rects.append(rect);
302 }
303
304 bool RenderSVGShape::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
305 {
306     // We only draw in the foreground phase, so we only hit-test then.
307     if (hitTestAction != HitTestForeground)
308         return false;
309
310     FloatPoint localPoint;
311     if (!SVGRenderSupport::transformToUserSpaceAndCheckClipping(this, m_localTransform, pointInParent, localPoint))
312         return false;
313
314     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_GEOMETRY_HITTESTING, request, style()->pointerEvents());
315     if (nodeAtFloatPointInternal(request, localPoint, hitRules)) {
316         updateHitTestResult(result, roundedLayoutPoint(localPoint));
317         return true;
318     }
319
320     return false;
321 }
322
323 bool RenderSVGShape::nodeAtFloatPointInternal(const HitTestRequest& request, const FloatPoint& localPoint, PointerEventsHitRules hitRules)
324 {
325     bool isVisible = (style()->visibility() == VISIBLE);
326     if (isVisible || !hitRules.requireVisible) {
327         const SVGRenderStyle& svgStyle = style()->svgStyle();
328         WindRule fillRule = svgStyle.fillRule();
329         if (request.svgClipContent())
330             fillRule = svgStyle.clipRule();
331         if ((hitRules.canHitBoundingBox && objectBoundingBox().contains(localPoint))
332             || (hitRules.canHitStroke && (svgStyle.hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
333             || (hitRules.canHitFill && (svgStyle.hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill, fillRule)))
334             return true;
335     }
336     return false;
337 }
338
339 static inline RenderSVGResourceMarker* markerForType(SVGMarkerType type, RenderSVGResourceMarker* markerStart, RenderSVGResourceMarker* markerMid, RenderSVGResourceMarker* markerEnd)
340 {
341     switch (type) {
342     case StartMarker:
343         return markerStart;
344     case MidMarker:
345         return markerMid;
346     case EndMarker:
347         return markerEnd;
348     }
349
350     ASSERT_NOT_REACHED();
351     return 0;
352 }
353
354 FloatRect RenderSVGShape::markerRect(float strokeWidth) const
355 {
356     ASSERT(!m_markerPositions.isEmpty());
357
358     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
359     ASSERT(resources);
360
361     RenderSVGResourceMarker* markerStart = resources->markerStart();
362     RenderSVGResourceMarker* markerMid = resources->markerMid();
363     RenderSVGResourceMarker* markerEnd = resources->markerEnd();
364     ASSERT(markerStart || markerMid || markerEnd);
365
366     FloatRect boundaries;
367     unsigned size = m_markerPositions.size();
368     for (unsigned i = 0; i < size; ++i) {
369         if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd))
370             boundaries.unite(marker->markerBoundaries(marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth)));
371     }
372     return boundaries;
373 }
374
375 FloatRect RenderSVGShape::calculateObjectBoundingBox() const
376 {
377     return path().boundingRect();
378 }
379
380 FloatRect RenderSVGShape::calculateStrokeBoundingBox() const
381 {
382     ASSERT(m_path);
383     FloatRect strokeBoundingBox = m_fillBoundingBox;
384
385     if (style()->svgStyle().hasStroke()) {
386         StrokeData strokeData;
387         SVGRenderSupport::applyStrokeStyleToStrokeData(&strokeData, style(), this);
388         if (hasNonScalingStroke()) {
389             AffineTransform nonScalingTransform = nonScalingStrokeTransform();
390             if (nonScalingTransform.isInvertible()) {
391                 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
392                 FloatRect strokeBoundingRect = usePath->strokeBoundingRect(strokeData);
393                 strokeBoundingRect = nonScalingTransform.inverse().mapRect(strokeBoundingRect);
394                 strokeBoundingBox.unite(strokeBoundingRect);
395             }
396         } else {
397             strokeBoundingBox.unite(path().strokeBoundingRect(strokeData));
398         }
399     }
400
401     if (!m_markerPositions.isEmpty())
402         strokeBoundingBox.unite(markerRect(strokeWidth()));
403
404     return strokeBoundingBox;
405 }
406
407 void RenderSVGShape::updatePaintInvalidationBoundingBox()
408 {
409     m_paintInvalidationBoundingBox = strokeBoundingBox();
410     if (strokeWidth() < 1.0f && !m_paintInvalidationBoundingBox.isEmpty())
411         m_paintInvalidationBoundingBox.inflate(1);
412     SVGRenderSupport::intersectPaintInvalidationRectWithResources(this, m_paintInvalidationBoundingBox);
413 }
414
415 float RenderSVGShape::strokeWidth() const
416 {
417     SVGLengthContext lengthContext(element());
418     return style()->svgStyle().strokeWidth()->value(lengthContext);
419 }
420
421 bool RenderSVGShape::hasSmoothStroke() const
422 {
423     const SVGRenderStyle& svgStyle = style()->svgStyle();
424     return svgStyle.strokeDashArray()->isEmpty()
425         && svgStyle.strokeMiterLimit() == SVGRenderStyle::initialStrokeMiterLimit()
426         && svgStyle.joinStyle() == SVGRenderStyle::initialJoinStyle()
427         && svgStyle.capStyle() == SVGRenderStyle::initialCapStyle();
428 }
429
430 void RenderSVGShape::drawMarkers(PaintInfo& paintInfo)
431 {
432     ASSERT(!m_markerPositions.isEmpty());
433
434     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
435     if (!resources)
436         return;
437
438     RenderSVGResourceMarker* markerStart = resources->markerStart();
439     RenderSVGResourceMarker* markerMid = resources->markerMid();
440     RenderSVGResourceMarker* markerEnd = resources->markerEnd();
441     if (!markerStart && !markerMid && !markerEnd)
442         return;
443
444     float strokeWidth = this->strokeWidth();
445     unsigned size = m_markerPositions.size();
446     for (unsigned i = 0; i < size; ++i) {
447         if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd))
448             marker->draw(paintInfo, marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth));
449     }
450 }
451
452 void RenderSVGShape::processMarkerPositions()
453 {
454     m_markerPositions.clear();
455
456     if (!shouldGenerateMarkerPositions())
457         return;
458
459     ASSERT(m_path);
460
461     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
462     ASSERT(resources);
463
464     RenderSVGResourceMarker* markerStart = resources->markerStart();
465
466     SVGMarkerData markerData(m_markerPositions, markerStart ? markerStart->orientType() == SVGMarkerOrientAutoStartReverse : false);
467     m_path->apply(&markerData, SVGMarkerData::updateFromPathElement);
468     markerData.pathIsDone();
469 }
470
471 }