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/PaintInfo.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 blink {
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    TemporaryChange<bool> inLayoutChange(m_isInLayout, true);
53
54    // RenderSVGHiddenContainer overwrites layout(). We need the
55    // layouting of RenderSVGContainer for calculating  local
56    // transformations and paint invalidation.
57    RenderSVGContainer::layout();
58
59    clearInvalidationMask();
60}
61
62void RenderSVGResourceMarker::removeAllClientsFromCache(bool markForInvalidation)
63{
64    markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
65}
66
67void RenderSVGResourceMarker::removeClientFromCache(RenderObject* client, bool markForInvalidation)
68{
69    ASSERT(client);
70    markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
71}
72
73void RenderSVGResourceMarker::applyViewportClip(PaintInfo& paintInfo)
74{
75    if (SVGRenderSupport::isOverflowHidden(this))
76        paintInfo.context->clip(m_viewport);
77}
78
79FloatRect RenderSVGResourceMarker::markerBoundaries(const AffineTransform& markerTransformation) const
80{
81    FloatRect coordinates = RenderSVGContainer::paintInvalidationRectInLocalCoordinates();
82
83    // Map paint invalidation rect into parent coordinate space, in which the marker boundaries have to be evaluated
84    coordinates = localToParentTransform().mapRect(coordinates);
85
86    return markerTransformation.mapRect(coordinates);
87}
88
89const AffineTransform& RenderSVGResourceMarker::localToParentTransform() const
90{
91    m_localToParentTransform = AffineTransform::translation(m_viewport.x(), m_viewport.y()) * viewportTransform();
92    return m_localToParentTransform;
93    // If this class were ever given a localTransform(), then the above would read:
94    // return viewportTranslation * localTransform() * viewportTransform();
95}
96
97FloatPoint RenderSVGResourceMarker::referencePoint() const
98{
99    SVGMarkerElement* marker = toSVGMarkerElement(element());
100    ASSERT(marker);
101
102    SVGLengthContext lengthContext(marker);
103    return FloatPoint(marker->refX()->currentValue()->value(lengthContext), marker->refY()->currentValue()->value(lengthContext));
104}
105
106float RenderSVGResourceMarker::angle() const
107{
108    SVGMarkerElement* marker = toSVGMarkerElement(element());
109    ASSERT(marker);
110
111    float angle = -1;
112    if (marker->orientType()->currentValue()->enumValue() == SVGMarkerOrientAngle)
113        angle = marker->orientAngle()->currentValue()->value();
114
115    return angle;
116}
117
118AffineTransform RenderSVGResourceMarker::markerTransformation(const FloatPoint& origin, float autoAngle, float strokeWidth) const
119{
120    SVGMarkerElement* marker = toSVGMarkerElement(element());
121    ASSERT(marker);
122
123    float markerAngle = angle();
124    bool useStrokeWidth = marker->markerUnits()->currentValue()->enumValue() == SVGMarkerUnitsStrokeWidth;
125
126    AffineTransform transform;
127    transform.translate(origin.x(), origin.y());
128    transform.rotate(markerAngle == -1 ? autoAngle : markerAngle);
129    transform = markerContentTransformation(transform, referencePoint(), useStrokeWidth ? strokeWidth : -1);
130    return transform;
131}
132
133void RenderSVGResourceMarker::draw(PaintInfo& paintInfo, const AffineTransform& transform)
134{
135    clearInvalidationMask();
136
137    // An empty viewBox disables rendering.
138    SVGMarkerElement* marker = toSVGMarkerElement(element());
139    ASSERT(marker);
140    if (marker->hasAttribute(SVGNames::viewBoxAttr) && marker->viewBox()->currentValue()->isValid() && marker->viewBox()->currentValue()->value().isEmpty())
141        return;
142
143    PaintInfo info(paintInfo);
144    GraphicsContextStateSaver stateSaver(*info.context, false);
145    info.applyTransform(transform, &stateSaver);
146    RenderSVGContainer::paint(info, IntPoint());
147}
148
149AffineTransform RenderSVGResourceMarker::markerContentTransformation(const AffineTransform& contentTransformation, const FloatPoint& origin, float strokeWidth) const
150{
151    // The 'origin' coordinate maps to SVGs refX/refY, given in coordinates relative to the viewport established by the marker
152    FloatPoint mappedOrigin = viewportTransform().mapPoint(origin);
153
154    AffineTransform transformation = contentTransformation;
155    if (strokeWidth != -1)
156        transformation.scaleNonUniform(strokeWidth, strokeWidth);
157
158    transformation.translate(-mappedOrigin.x(), -mappedOrigin.y());
159    return transformation;
160}
161
162AffineTransform RenderSVGResourceMarker::viewportTransform() const
163{
164    SVGMarkerElement* marker = toSVGMarkerElement(element());
165    ASSERT(marker);
166
167    return marker->viewBoxToViewTransform(m_viewport.width(), m_viewport.height());
168}
169
170void RenderSVGResourceMarker::calcViewport()
171{
172    if (!selfNeedsLayout())
173        return;
174
175    SVGMarkerElement* marker = toSVGMarkerElement(element());
176    ASSERT(marker);
177
178    SVGLengthContext lengthContext(marker);
179    float w = marker->markerWidth()->currentValue()->value(lengthContext);
180    float h = marker->markerHeight()->currentValue()->value(lengthContext);
181    m_viewport = FloatRect(0, 0, w, h);
182}
183
184}
185