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 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB.  If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22#include "config.h"
23
24#if ENABLE(SVG)
25#include "RenderSVGResourceMarker.h"
26
27#include "GraphicsContext.h"
28#include "RenderSVGContainer.h"
29#include "RenderSVGRoot.h"
30#include "SVGElement.h"
31#include "SVGMarkerElement.h"
32#include "SVGRenderSupport.h"
33#include "SVGStyledElement.h"
34#include "SVGStyledTransformableElement.h"
35#include <wtf/StackStats.h>
36
37namespace WebCore {
38
39RenderSVGResourceType RenderSVGResourceMarker::s_resourceType = MarkerResourceType;
40
41RenderSVGResourceMarker::RenderSVGResourceMarker(SVGMarkerElement* node)
42    : RenderSVGResourceContainer(node)
43{
44}
45
46RenderSVGResourceMarker::~RenderSVGResourceMarker()
47{
48}
49
50void RenderSVGResourceMarker::layout()
51{
52    StackStats::LayoutCheckPoint layoutCheckPoint;
53    // Invalidate all resources if our layout changed.
54    if (everHadLayout() && selfNeedsLayout())
55        RenderSVGRoot::addResourceForClientInvalidation(this);
56
57    // RenderSVGHiddenContainer overwrites layout(). We need the
58    // layouting of RenderSVGContainer for calculating  local
59    // transformations and repaint.
60    RenderSVGContainer::layout();
61}
62
63void RenderSVGResourceMarker::removeAllClientsFromCache(bool markForInvalidation)
64{
65    markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
66}
67
68void RenderSVGResourceMarker::removeClientFromCache(RenderObject* client, bool markForInvalidation)
69{
70    ASSERT(client);
71    markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
72}
73
74void RenderSVGResourceMarker::applyViewportClip(PaintInfo& paintInfo)
75{
76    if (SVGRenderSupport::isOverflowHidden(this))
77        paintInfo.context->clip(m_viewport);
78}
79
80FloatRect RenderSVGResourceMarker::markerBoundaries(const AffineTransform& markerTransformation) const
81{
82    FloatRect coordinates = RenderSVGContainer::repaintRectInLocalCoordinates();
83
84    // Map repaint rect into parent coordinate space, in which the marker boundaries have to be evaluated
85    coordinates = localToParentTransform().mapRect(coordinates);
86
87    return markerTransformation.mapRect(coordinates);
88}
89
90const AffineTransform& RenderSVGResourceMarker::localToParentTransform() const
91{
92    m_localToParentTransform = AffineTransform::translation(m_viewport.x(), m_viewport.y()) * viewportTransform();
93    return m_localToParentTransform;
94    // If this class were ever given a localTransform(), then the above would read:
95    // return viewportTranslation * localTransform() * viewportTransform();
96}
97
98FloatPoint RenderSVGResourceMarker::referencePoint() const
99{
100    SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(node());
101    ASSERT(marker);
102
103    SVGLengthContext lengthContext(marker);
104    return FloatPoint(marker->refX().value(lengthContext), marker->refY().value(lengthContext));
105}
106
107float RenderSVGResourceMarker::angle() const
108{
109    SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(node());
110    ASSERT(marker);
111
112    float angle = -1;
113    if (marker->orientType() == SVGMarkerOrientAngle)
114        angle = marker->orientAngle().value();
115
116    return angle;
117}
118
119AffineTransform RenderSVGResourceMarker::markerTransformation(const FloatPoint& origin, float autoAngle, float strokeWidth) const
120{
121    SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(node());
122    ASSERT(marker);
123
124    float markerAngle = angle();
125    bool useStrokeWidth = marker->markerUnits() == SVGMarkerUnitsStrokeWidth;
126
127    AffineTransform transform;
128    transform.translate(origin.x(), origin.y());
129    transform.rotate(markerAngle == -1 ? autoAngle : markerAngle);
130    transform = markerContentTransformation(transform, referencePoint(), useStrokeWidth ? strokeWidth : -1);
131    return transform;
132}
133
134void RenderSVGResourceMarker::draw(PaintInfo& paintInfo, const AffineTransform& transform)
135{
136    // An empty viewBox disables rendering.
137    SVGMarkerElement* marker = toSVGMarkerElement(toSVGElement(node()));
138    ASSERT(marker);
139    if (marker->hasAttribute(SVGNames::viewBoxAttr) && marker->viewBoxIsValid() && marker->viewBox().isEmpty())
140        return;
141
142    PaintInfo info(paintInfo);
143    GraphicsContextStateSaver stateSaver(*info.context);
144    info.applyTransform(transform);
145    RenderSVGContainer::paint(info, IntPoint());
146}
147
148AffineTransform RenderSVGResourceMarker::markerContentTransformation(const AffineTransform& contentTransformation, const FloatPoint& origin, float strokeWidth) const
149{
150    // The 'origin' coordinate maps to SVGs refX/refY, given in coordinates relative to the viewport established by the marker
151    FloatPoint mappedOrigin = viewportTransform().mapPoint(origin);
152
153    AffineTransform transformation = contentTransformation;
154    if (strokeWidth != -1)
155        transformation.scaleNonUniform(strokeWidth, strokeWidth);
156
157    transformation.translate(-mappedOrigin.x(), -mappedOrigin.y());
158    return transformation;
159}
160
161AffineTransform RenderSVGResourceMarker::viewportTransform() const
162{
163    SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(node());
164    ASSERT(marker);
165
166    return marker->viewBoxToViewTransform(m_viewport.width(), m_viewport.height());
167}
168
169void RenderSVGResourceMarker::calcViewport()
170{
171    if (!selfNeedsLayout())
172        return;
173
174    SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(node());
175    ASSERT(marker);
176
177    SVGLengthContext lengthContext(marker);
178    float w = marker->markerWidth().value(lengthContext);
179    float h = marker->markerHeight().value(lengthContext);
180    m_viewport = FloatRect(0, 0, w, h);
181}
182
183}
184
185#endif // ENABLE(SVG)
186