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#include "core/rendering/svg/RenderSVGResourceMarker.h"
25
26#include "core/rendering/LayoutRectRecorder.h"
27#include "core/rendering/svg/RenderSVGContainer.h"
28#include "core/rendering/svg/SVGRenderSupport.h"
29#include "platform/graphics/GraphicsContextStateSaver.h"
30
31#include "wtf/TemporaryChange.h"
32
33namespace WebCore {
34
35const RenderSVGResourceType RenderSVGResourceMarker::s_resourceType = MarkerResourceType;
36
37RenderSVGResourceMarker::RenderSVGResourceMarker(SVGMarkerElement* node)
38    : RenderSVGResourceContainer(node)
39{
40}
41
42RenderSVGResourceMarker::~RenderSVGResourceMarker()
43{
44}
45
46void RenderSVGResourceMarker::layout()
47{
48    ASSERT(needsLayout());
49    if (m_isInLayout)
50        return;
51
52    LayoutRectRecorder recorder(*this);
53    TemporaryChange<bool> inLayoutChange(m_isInLayout, true);
54
55    // Invalidate all resources if our layout changed.
56    if (everHadLayout() && selfNeedsLayout())
57        removeAllClientsFromCache();
58
59    // RenderSVGHiddenContainer overwrites layout(). We need the
60    // layouting of RenderSVGContainer for calculating  local
61    // transformations and repaint.
62    RenderSVGContainer::layout();
63
64    clearInvalidationMask();
65}
66
67void RenderSVGResourceMarker::removeAllClientsFromCache(bool markForInvalidation)
68{
69    markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
70}
71
72void RenderSVGResourceMarker::removeClientFromCache(RenderObject* client, bool markForInvalidation)
73{
74    ASSERT(client);
75    markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
76}
77
78void RenderSVGResourceMarker::applyViewportClip(PaintInfo& paintInfo)
79{
80    if (SVGRenderSupport::isOverflowHidden(this))
81        paintInfo.context->clip(m_viewport);
82}
83
84FloatRect RenderSVGResourceMarker::markerBoundaries(const AffineTransform& markerTransformation) const
85{
86    FloatRect coordinates = RenderSVGContainer::repaintRectInLocalCoordinates();
87
88    // Map repaint rect into parent coordinate space, in which the marker boundaries have to be evaluated
89    coordinates = localToParentTransform().mapRect(coordinates);
90
91    return markerTransformation.mapRect(coordinates);
92}
93
94const AffineTransform& RenderSVGResourceMarker::localToParentTransform() const
95{
96    m_localToParentTransform = AffineTransform::translation(m_viewport.x(), m_viewport.y()) * viewportTransform();
97    return m_localToParentTransform;
98    // If this class were ever given a localTransform(), then the above would read:
99    // return viewportTranslation * localTransform() * viewportTransform();
100}
101
102FloatPoint RenderSVGResourceMarker::referencePoint() const
103{
104    SVGMarkerElement* marker = toSVGMarkerElement(element());
105    ASSERT(marker);
106
107    SVGLengthContext lengthContext(marker);
108    return FloatPoint(marker->refXCurrentValue().value(lengthContext), marker->refYCurrentValue().value(lengthContext));
109}
110
111float RenderSVGResourceMarker::angle() const
112{
113    SVGMarkerElement* marker = toSVGMarkerElement(element());
114    ASSERT(marker);
115
116    float angle = -1;
117    if (marker->orientTypeCurrentValue() == SVGMarkerOrientAngle)
118        angle = marker->orientAngleCurrentValue().value();
119
120    return angle;
121}
122
123AffineTransform RenderSVGResourceMarker::markerTransformation(const FloatPoint& origin, float autoAngle, float strokeWidth) const
124{
125    SVGMarkerElement* marker = toSVGMarkerElement(element());
126    ASSERT(marker);
127
128    float markerAngle = angle();
129    bool useStrokeWidth = marker->markerUnitsCurrentValue() == SVGMarkerUnitsStrokeWidth;
130
131    AffineTransform transform;
132    transform.translate(origin.x(), origin.y());
133    transform.rotate(markerAngle == -1 ? autoAngle : markerAngle);
134    transform = markerContentTransformation(transform, referencePoint(), useStrokeWidth ? strokeWidth : -1);
135    return transform;
136}
137
138void RenderSVGResourceMarker::draw(PaintInfo& paintInfo, const AffineTransform& transform)
139{
140    clearInvalidationMask();
141
142    // An empty viewBox disables rendering.
143    SVGMarkerElement* marker = toSVGMarkerElement(element());
144    ASSERT(marker);
145    if (marker->hasAttribute(SVGNames::viewBoxAttr) && marker->viewBoxCurrentValue().isValid() && marker->viewBoxCurrentValue().isEmpty())
146        return;
147
148    PaintInfo info(paintInfo);
149    GraphicsContextStateSaver stateSaver(*info.context);
150    info.applyTransform(transform);
151    RenderSVGContainer::paint(info, IntPoint());
152}
153
154AffineTransform RenderSVGResourceMarker::markerContentTransformation(const AffineTransform& contentTransformation, const FloatPoint& origin, float strokeWidth) const
155{
156    // The 'origin' coordinate maps to SVGs refX/refY, given in coordinates relative to the viewport established by the marker
157    FloatPoint mappedOrigin = viewportTransform().mapPoint(origin);
158
159    AffineTransform transformation = contentTransformation;
160    if (strokeWidth != -1)
161        transformation.scaleNonUniform(strokeWidth, strokeWidth);
162
163    transformation.translate(-mappedOrigin.x(), -mappedOrigin.y());
164    return transformation;
165}
166
167AffineTransform RenderSVGResourceMarker::viewportTransform() const
168{
169    SVGMarkerElement* marker = toSVGMarkerElement(element());
170    ASSERT(marker);
171
172    return marker->viewBoxToViewTransform(m_viewport.width(), m_viewport.height());
173}
174
175void RenderSVGResourceMarker::calcViewport()
176{
177    if (!selfNeedsLayout())
178        return;
179
180    SVGMarkerElement* marker = toSVGMarkerElement(element());
181    ASSERT(marker);
182
183    SVGLengthContext lengthContext(marker);
184    float w = marker->markerWidthCurrentValue().value(lengthContext);
185    float h = marker->markerHeightCurrentValue().value(lengthContext);
186    m_viewport = FloatRect(0, 0, w, h);
187}
188
189}
190