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
30#if ENABLE(SVG)
31#include "RenderSVGShape.h"
32
33#include "FloatPoint.h"
34#include "FloatQuad.h"
35#include "GraphicsContext.h"
36#include "HitTestRequest.h"
37#include "LayoutRepainter.h"
38#include "PointerEventsHitRules.h"
39#include "RenderSVGContainer.h"
40#include "RenderSVGResourceMarker.h"
41#include "RenderSVGResourceSolidColor.h"
42#include "SVGPathData.h"
43#include "SVGRenderingContext.h"
44#include "SVGResources.h"
45#include "SVGResourcesCache.h"
46#include "SVGStyledTransformableElement.h"
47#include "SVGTransformList.h"
48#include "SVGURIReference.h"
49#include "StrokeStyleApplier.h"
50#include <wtf/MathExtras.h>
51#include <wtf/StackStats.h>
52
53namespace WebCore {
54
55RenderSVGShape::RenderSVGShape(SVGStyledTransformableElement* node)
56    : RenderSVGModelObject(node)
57    , m_needsBoundariesUpdate(false) // Default is false, the cached rects are empty from the beginning.
58    , m_needsShapeUpdate(true) // Default is true, so we grab a Path object once from SVGStyledTransformableElement.
59    , m_needsTransformUpdate(true) // Default is true, so we grab a AffineTransform object once from SVGStyledTransformableElement.
60{
61}
62
63RenderSVGShape::~RenderSVGShape()
64{
65}
66
67void RenderSVGShape::updateShapeFromElement()
68{
69    m_path.clear();
70    m_path = adoptPtr(new Path);
71    ASSERT(RenderSVGShape::isEmpty());
72
73    SVGStyledTransformableElement* element = toSVGStyledTransformableElement(node());
74    updatePathFromGraphicsElement(element, path());
75    processMarkerPositions();
76
77    m_fillBoundingBox = calculateObjectBoundingBox();
78    m_strokeBoundingBox = calculateStrokeBoundingBox();
79}
80
81bool RenderSVGShape::isEmpty() const
82{
83    return path().isEmpty();
84}
85
86void RenderSVGShape::fillShape(GraphicsContext* context) const
87{
88    context->fillPath(path());
89}
90
91void RenderSVGShape::strokeShape(GraphicsContext* context) const
92{
93    ASSERT(m_path);
94    Path* usePath = m_path.get();
95
96    if (hasNonScalingStroke())
97        usePath = nonScalingStrokePath(usePath, nonScalingStrokeTransform());
98
99    context->strokePath(*usePath);
100}
101
102bool RenderSVGShape::shapeDependentStrokeContains(const FloatPoint& point)
103{
104    ASSERT(m_path);
105    BoundingRectStrokeStyleApplier applier(this, style());
106
107    if (hasNonScalingStroke()) {
108        AffineTransform nonScalingTransform = nonScalingStrokeTransform();
109        Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
110
111        return usePath->strokeContains(&applier, nonScalingTransform.mapPoint(point));
112    }
113
114    return m_path->strokeContains(&applier, point);
115}
116
117bool RenderSVGShape::shapeDependentFillContains(const FloatPoint& point, const WindRule fillRule) const
118{
119    return path().contains(point, fillRule);
120}
121
122bool RenderSVGShape::fillContains(const FloatPoint& point, bool requiresFill, const WindRule fillRule)
123{
124    if (!m_fillBoundingBox.contains(point))
125        return false;
126
127    Color fallbackColor;
128    if (requiresFill && !RenderSVGResource::fillPaintingResource(this, style(), fallbackColor))
129        return false;
130
131    return shapeDependentFillContains(point, fillRule);
132}
133
134bool RenderSVGShape::strokeContains(const FloatPoint& point, bool requiresStroke)
135{
136    if (!strokeBoundingBox().contains(point))
137        return false;
138
139    Color fallbackColor;
140    if (requiresStroke && !RenderSVGResource::strokePaintingResource(this, style(), fallbackColor))
141        return false;
142
143    return shapeDependentStrokeContains(point);
144}
145
146void RenderSVGShape::layout()
147{
148    StackStats::LayoutCheckPoint layoutCheckPoint;
149    LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(this) && selfNeedsLayout());
150    SVGStyledTransformableElement* element = toSVGStyledTransformableElement(node());
151
152    bool updateCachedBoundariesInParents = false;
153
154    if (m_needsShapeUpdate || m_needsBoundariesUpdate) {
155        updateShapeFromElement();
156        m_needsShapeUpdate = false;
157        updateRepaintBoundingBox();
158        m_needsBoundariesUpdate = false;
159        updateCachedBoundariesInParents = true;
160    }
161
162    if (m_needsTransformUpdate) {
163        m_localTransform = element->animatedLocalTransform();
164        m_needsTransformUpdate = false;
165        updateCachedBoundariesInParents = true;
166    }
167
168    // Invalidate all resources of this client if our layout changed.
169    if (everHadLayout() && selfNeedsLayout())
170        SVGResourcesCache::clientLayoutChanged(this);
171
172    // If our bounds changed, notify the parents.
173    if (updateCachedBoundariesInParents)
174        RenderSVGModelObject::setNeedsBoundariesUpdate();
175
176    repainter.repaintAfterLayout();
177    setNeedsLayout(false);
178}
179
180Path* RenderSVGShape::nonScalingStrokePath(const Path* path, const AffineTransform& strokeTransform) const
181{
182    DEFINE_STATIC_LOCAL(Path, tempPath, ());
183
184    tempPath = *path;
185    tempPath.transform(strokeTransform);
186
187    return &tempPath;
188}
189
190bool RenderSVGShape::setupNonScalingStrokeContext(AffineTransform& strokeTransform, GraphicsContextStateSaver& stateSaver)
191{
192    if (!strokeTransform.isInvertible())
193        return false;
194
195    stateSaver.save();
196    stateSaver.context()->concatCTM(strokeTransform.inverse());
197    return true;
198}
199
200AffineTransform RenderSVGShape::nonScalingStrokeTransform() const
201{
202    SVGStyledTransformableElement* element = toSVGStyledTransformableElement(node());
203    return element->getScreenCTM(SVGLocatable::DisallowStyleUpdate);
204}
205
206bool RenderSVGShape::shouldGenerateMarkerPositions() const
207{
208    if (!style()->svgStyle()->hasMarkers())
209        return false;
210
211    SVGStyledTransformableElement* element = toSVGStyledTransformableElement(node());
212    if (!element->supportsMarkers())
213        return false;
214
215    SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
216    if (!resources)
217        return false;
218
219    return resources->markerStart() || resources->markerMid() || resources->markerEnd();
220}
221
222void RenderSVGShape::fillShape(RenderStyle* style, GraphicsContext* context)
223{
224    Color fallbackColor;
225    if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(this, style, fallbackColor)) {
226        if (fillPaintingResource->applyResource(this, style, context, ApplyToFillMode))
227            fillPaintingResource->postApplyResource(this, context, ApplyToFillMode, 0, this);
228        else if (fallbackColor.isValid()) {
229            RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
230            fallbackResource->setColor(fallbackColor);
231            if (fallbackResource->applyResource(this, style, context, ApplyToFillMode))
232                fallbackResource->postApplyResource(this, context, ApplyToFillMode, 0, this);
233        }
234    }
235}
236
237void RenderSVGShape::strokeShape(RenderStyle* style, GraphicsContext* context)
238{
239    Color fallbackColor;
240    if (RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(this, style, fallbackColor)) {
241        if (strokePaintingResource->applyResource(this, style, context, ApplyToStrokeMode))
242            strokePaintingResource->postApplyResource(this, context, ApplyToStrokeMode, 0, this);
243        else if (fallbackColor.isValid()) {
244            RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
245            fallbackResource->setColor(fallbackColor);
246            if (fallbackResource->applyResource(this, style, context, ApplyToStrokeMode))
247                fallbackResource->postApplyResource(this, context, ApplyToStrokeMode, 0, this);
248        }
249    }
250}
251
252void RenderSVGShape::fillAndStrokeShape(GraphicsContext* context)
253{
254    RenderStyle* style = this->style();
255
256    fillShape(style, context);
257
258    if (!style->svgStyle()->hasVisibleStroke())
259        return;
260
261    GraphicsContextStateSaver stateSaver(*context, false);
262
263    if (hasNonScalingStroke()) {
264        AffineTransform nonScalingTransform = nonScalingStrokeTransform();
265        if (!setupNonScalingStrokeContext(nonScalingTransform, stateSaver))
266            return;
267    }
268
269    strokeShape(style, context);
270}
271
272void RenderSVGShape::paint(PaintInfo& paintInfo, const LayoutPoint&)
273{
274    if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || isEmpty())
275        return;
276    FloatRect boundingBox = repaintRectInLocalCoordinates();
277    if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
278        return;
279
280    PaintInfo childPaintInfo(paintInfo);
281    bool drawsOutline = style()->outlineWidth() && (childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline);
282    if (drawsOutline || childPaintInfo.phase == PaintPhaseForeground) {
283        GraphicsContextStateSaver stateSaver(*childPaintInfo.context);
284        childPaintInfo.applyTransform(m_localTransform);
285
286        if (childPaintInfo.phase == PaintPhaseForeground) {
287            SVGRenderingContext renderingContext(this, childPaintInfo);
288
289            if (renderingContext.isRenderingPrepared()) {
290                const SVGRenderStyle* svgStyle = style()->svgStyle();
291                if (svgStyle->shapeRendering() == SR_CRISPEDGES)
292                    childPaintInfo.context->setShouldAntialias(false);
293
294                fillAndStrokeShape(childPaintInfo.context);
295                if (!m_markerPositions.isEmpty())
296                    drawMarkers(childPaintInfo);
297            }
298        }
299
300        if (drawsOutline)
301            paintOutline(childPaintInfo.context, IntRect(boundingBox));
302    }
303}
304
305// This method is called from inside paintOutline() since we call paintOutline()
306// while transformed to our coord system, return local coords
307void RenderSVGShape::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&)
308{
309    IntRect rect = enclosingIntRect(repaintRectInLocalCoordinates());
310    if (!rect.isEmpty())
311        rects.append(rect);
312}
313
314bool RenderSVGShape::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
315{
316    // We only draw in the forground phase, so we only hit-test then.
317    if (hitTestAction != HitTestForeground)
318        return false;
319
320    FloatPoint localPoint = m_localTransform.inverse().mapPoint(pointInParent);
321
322    if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
323        return false;
324
325    PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, request, style()->pointerEvents());
326    bool isVisible = (style()->visibility() == VISIBLE);
327    if (isVisible || !hitRules.requireVisible) {
328        const SVGRenderStyle* svgStyle = style()->svgStyle();
329        WindRule fillRule = svgStyle->fillRule();
330        if (request.svgClipContent())
331            fillRule = svgStyle->clipRule();
332        if ((hitRules.canHitStroke && (svgStyle->hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
333            || (hitRules.canHitFill && (svgStyle->hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill, fillRule))) {
334            updateHitTestResult(result, roundedLayoutPoint(localPoint));
335            return true;
336        }
337    }
338    return false;
339}
340
341static inline RenderSVGResourceMarker* markerForType(SVGMarkerType type, RenderSVGResourceMarker* markerStart, RenderSVGResourceMarker* markerMid, RenderSVGResourceMarker* markerEnd)
342{
343    switch (type) {
344    case StartMarker:
345        return markerStart;
346    case MidMarker:
347        return markerMid;
348    case EndMarker:
349        return markerEnd;
350    }
351
352    ASSERT_NOT_REACHED();
353    return 0;
354}
355
356FloatRect RenderSVGShape::markerRect(float strokeWidth) const
357{
358    ASSERT(!m_markerPositions.isEmpty());
359
360    SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
361    ASSERT(resources);
362
363    RenderSVGResourceMarker* markerStart = resources->markerStart();
364    RenderSVGResourceMarker* markerMid = resources->markerMid();
365    RenderSVGResourceMarker* markerEnd = resources->markerEnd();
366    ASSERT(markerStart || markerMid || markerEnd);
367
368    FloatRect boundaries;
369    unsigned size = m_markerPositions.size();
370    for (unsigned i = 0; i < size; ++i) {
371        if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd))
372            boundaries.unite(marker->markerBoundaries(marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth)));
373    }
374    return boundaries;
375}
376
377FloatRect RenderSVGShape::calculateObjectBoundingBox() const
378{
379    return path().fastBoundingRect();
380}
381
382FloatRect RenderSVGShape::calculateStrokeBoundingBox() const
383{
384    ASSERT(m_path);
385    FloatRect strokeBoundingBox = m_fillBoundingBox;
386
387    const SVGRenderStyle* svgStyle = style()->svgStyle();
388    if (svgStyle->hasStroke()) {
389        BoundingRectStrokeStyleApplier strokeStyle(this, style());
390        if (hasNonScalingStroke()) {
391            AffineTransform nonScalingTransform = nonScalingStrokeTransform();
392            if (nonScalingTransform.isInvertible()) {
393                Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
394                FloatRect strokeBoundingRect = usePath->strokeBoundingRect(&strokeStyle);
395                strokeBoundingRect = nonScalingTransform.inverse().mapRect(strokeBoundingRect);
396                strokeBoundingBox.unite(strokeBoundingRect);
397            }
398        } else
399            strokeBoundingBox.unite(path().strokeBoundingRect(&strokeStyle));
400    }
401
402    if (!m_markerPositions.isEmpty())
403        strokeBoundingBox.unite(markerRect(strokeWidth()));
404
405    return strokeBoundingBox;
406}
407
408void RenderSVGShape::updateRepaintBoundingBox()
409{
410    m_repaintBoundingBoxExcludingShadow = strokeBoundingBox();
411    SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBoxExcludingShadow);
412
413    m_repaintBoundingBox = m_repaintBoundingBoxExcludingShadow;
414    SVGRenderSupport::intersectRepaintRectWithShadows(this, m_repaintBoundingBox);
415}
416
417float RenderSVGShape::strokeWidth() const
418{
419    SVGElement* svgElement = toSVGElement(node());
420    SVGLengthContext lengthContext(svgElement);
421    return style()->svgStyle()->strokeWidth().value(lengthContext);
422}
423
424bool RenderSVGShape::hasSmoothStroke() const
425{
426    const SVGRenderStyle* svgStyle = style()->svgStyle();
427    return svgStyle->strokeDashArray().isEmpty()
428        && svgStyle->strokeMiterLimit() == svgStyle->initialStrokeMiterLimit()
429        && svgStyle->joinStyle() == svgStyle->initialJoinStyle()
430        && svgStyle->capStyle() == svgStyle->initialCapStyle();
431}
432
433void RenderSVGShape::drawMarkers(PaintInfo& paintInfo)
434{
435    ASSERT(!m_markerPositions.isEmpty());
436
437    SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
438    if (!resources)
439        return;
440
441    RenderSVGResourceMarker* markerStart = resources->markerStart();
442    RenderSVGResourceMarker* markerMid = resources->markerMid();
443    RenderSVGResourceMarker* markerEnd = resources->markerEnd();
444    if (!markerStart && !markerMid && !markerEnd)
445        return;
446
447    float strokeWidth = this->strokeWidth();
448    unsigned size = m_markerPositions.size();
449    for (unsigned i = 0; i < size; ++i) {
450        if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd))
451            marker->draw(paintInfo, marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth));
452    }
453}
454
455void RenderSVGShape::processMarkerPositions()
456{
457    m_markerPositions.clear();
458
459    if (!shouldGenerateMarkerPositions())
460        return;
461
462    ASSERT(m_path);
463
464    SVGMarkerData markerData(m_markerPositions);
465    m_path->apply(&markerData, SVGMarkerData::updateFromPathElement);
466    markerData.pathIsDone();
467}
468
469}
470
471#endif // ENABLE(SVG)
472