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#include "RenderSVGResourceMarker.h"
24
25#include "GraphicsContext.h"
26#include "RenderSVGRoot.h"
27#include <wtf/IsoMallocInlines.h>
28#include <wtf/StackStats.h>
29
30namespace WebCore {
31
32WTF_MAKE_ISO_ALLOCATED_IMPL(RenderSVGResourceMarker);
33
34RenderSVGResourceMarker::RenderSVGResourceMarker(SVGMarkerElement& element, RenderStyle&& style)
35 : RenderSVGResourceContainer(element, WTFMove(style))
36{
37}
38
39RenderSVGResourceMarker::~RenderSVGResourceMarker() = default;
40
41void RenderSVGResourceMarker::layout()
42{
43 StackStats::LayoutCheckPoint layoutCheckPoint;
44 // Invalidate all resources if our layout changed.
45 if (everHadLayout() && selfNeedsLayout())
46 RenderSVGRoot::addResourceForClientInvalidation(this);
47
48 // RenderSVGHiddenContainer overwrites layout(). We need the
49 // layouting of RenderSVGContainer for calculating local
50 // transformations and repaint.
51 RenderSVGContainer::layout();
52}
53
54void RenderSVGResourceMarker::removeAllClientsFromCache(bool markForInvalidation)
55{
56 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
57}
58
59void RenderSVGResourceMarker::removeClientFromCache(RenderElement& client, bool markForInvalidation)
60{
61 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
62}
63
64void RenderSVGResourceMarker::applyViewportClip(PaintInfo& paintInfo)
65{
66 if (SVGRenderSupport::isOverflowHidden(*this))
67 paintInfo.context().clip(m_viewport);
68}
69
70FloatRect RenderSVGResourceMarker::markerBoundaries(const AffineTransform& markerTransformation) const
71{
72 FloatRect coordinates = RenderSVGContainer::repaintRectInLocalCoordinates();
73
74 // Map repaint rect into parent coordinate space, in which the marker boundaries have to be evaluated
75 coordinates = localToParentTransform().mapRect(coordinates);
76
77 return markerTransformation.mapRect(coordinates);
78}
79
80const AffineTransform& RenderSVGResourceMarker::localToParentTransform() const
81{
82 m_localToParentTransform = AffineTransform::translation(m_viewport.x(), m_viewport.y()) * viewportTransform();
83 return m_localToParentTransform;
84 // If this class were ever given a localTransform(), then the above would read:
85 // return viewportTranslation * localTransform() * viewportTransform();
86}
87
88FloatPoint RenderSVGResourceMarker::referencePoint() const
89{
90 SVGLengthContext lengthContext(&markerElement());
91 return FloatPoint(markerElement().refX().value(lengthContext), markerElement().refY().value(lengthContext));
92}
93
94float RenderSVGResourceMarker::angle() const
95{
96 float angle = -1;
97 if (markerElement().orientType() == SVGMarkerOrientAngle)
98 angle = markerElement().orientAngle().value();
99
100 return angle;
101}
102
103AffineTransform RenderSVGResourceMarker::markerTransformation(const FloatPoint& origin, float autoAngle, float strokeWidth) const
104{
105 float markerAngle = angle();
106 bool useStrokeWidth = markerElement().markerUnits() == SVGMarkerUnitsStrokeWidth;
107
108 AffineTransform transform;
109 transform.translate(origin);
110 transform.rotate(markerAngle == -1 ? autoAngle : markerAngle);
111 transform = markerContentTransformation(transform, referencePoint(), useStrokeWidth ? strokeWidth : -1);
112 return transform;
113}
114
115void RenderSVGResourceMarker::draw(PaintInfo& paintInfo, const AffineTransform& transform)
116{
117 // An empty viewBox disables rendering.
118 if (markerElement().hasAttribute(SVGNames::viewBoxAttr) && markerElement().hasEmptyViewBox())
119 return;
120
121 PaintInfo info(paintInfo);
122 GraphicsContextStateSaver stateSaver(info.context());
123 info.applyTransform(transform);
124 RenderSVGContainer::paint(info, IntPoint());
125}
126
127AffineTransform RenderSVGResourceMarker::markerContentTransformation(const AffineTransform& contentTransformation, const FloatPoint& origin, float strokeWidth) const
128{
129 // The 'origin' coordinate maps to SVGs refX/refY, given in coordinates relative to the viewport established by the marker
130 FloatPoint mappedOrigin = viewportTransform().mapPoint(origin);
131
132 AffineTransform transformation = contentTransformation;
133 if (strokeWidth != -1)
134 transformation.scaleNonUniform(strokeWidth, strokeWidth);
135
136 transformation.translate(-mappedOrigin);
137 return transformation;
138}
139
140AffineTransform RenderSVGResourceMarker::viewportTransform() const
141{
142 return markerElement().viewBoxToViewTransform(m_viewport.width(), m_viewport.height());
143}
144
145void RenderSVGResourceMarker::calcViewport()
146{
147 if (!selfNeedsLayout())
148 return;
149
150 SVGLengthContext lengthContext(&markerElement());
151 float w = markerElement().markerWidth().value(lengthContext);
152 float h = markerElement().markerHeight().value(lengthContext);
153 m_viewport = FloatRect(0, 0, w, h);
154}
155
156}
157