1/*
2 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB.  If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#ifndef SVGMarkerData_h
21#define SVGMarkerData_h
22
23#if ENABLE(SVG)
24#include "FloatConversion.h"
25#include "Path.h"
26#include <wtf/MathExtras.h>
27
28namespace WebCore {
29
30class RenderSVGResourceMarker;
31
32enum SVGMarkerType {
33    StartMarker,
34    MidMarker,
35    EndMarker
36};
37
38struct MarkerPosition {
39    MarkerPosition(SVGMarkerType useType, const FloatPoint& useOrigin, float useAngle)
40        : type(useType)
41        , origin(useOrigin)
42        , angle(useAngle)
43    {
44    }
45
46    SVGMarkerType type;
47    FloatPoint origin;
48    float angle;
49};
50
51class SVGMarkerData {
52public:
53    SVGMarkerData(Vector<MarkerPosition>& positions)
54        : m_positions(positions)
55        , m_elementIndex(0)
56    {
57    }
58
59    static void updateFromPathElement(void* info, const PathElement* element)
60    {
61        SVGMarkerData* markerData = static_cast<SVGMarkerData*>(info);
62
63        // First update the outslope for the previous element.
64        markerData->updateOutslope(element->points[0]);
65
66        // Record the marker for the previous element.
67        if (markerData->m_elementIndex > 0) {
68            SVGMarkerType markerType = markerData->m_elementIndex == 1 ? StartMarker : MidMarker;
69            markerData->m_positions.append(MarkerPosition(markerType, markerData->m_origin, markerData->currentAngle(markerType)));
70        }
71
72        // Update our marker data for this element.
73        markerData->updateMarkerDataForPathElement(element);
74        ++markerData->m_elementIndex;
75    }
76
77    void pathIsDone()
78    {
79        m_positions.append(MarkerPosition(EndMarker, m_origin, currentAngle(EndMarker)));
80    }
81
82private:
83    float currentAngle(SVGMarkerType type) const
84    {
85        // For details of this calculation, see: http://www.w3.org/TR/SVG/single-page.html#painting-MarkerElement
86        FloatPoint inSlope(m_inslopePoints[1] - m_inslopePoints[0]);
87        FloatPoint outSlope(m_outslopePoints[1] - m_outslopePoints[0]);
88
89        double inAngle = rad2deg(inSlope.slopeAngleRadians());
90        double outAngle = rad2deg(outSlope.slopeAngleRadians());
91
92        switch (type) {
93        case StartMarker:
94            return narrowPrecisionToFloat(outAngle);
95        case MidMarker:
96            // WK193015: Prevent bugs due to angles being non-continuous.
97            if (fabs(inAngle - outAngle) > 180)
98                inAngle += 360;
99            return narrowPrecisionToFloat((inAngle + outAngle) / 2);
100        case EndMarker:
101            return narrowPrecisionToFloat(inAngle);
102        }
103
104        ASSERT_NOT_REACHED();
105        return 0;
106    }
107
108    void updateOutslope(const FloatPoint& point)
109    {
110        m_outslopePoints[0] = m_origin;
111        m_outslopePoints[1] = point;
112    }
113
114    void updateMarkerDataForPathElement(const PathElement* element)
115    {
116        FloatPoint* points = element->points;
117
118        switch (element->type) {
119        case PathElementAddQuadCurveToPoint:
120            // FIXME: https://bugs.webkit.org/show_bug.cgi?id=33115 (PathElementAddQuadCurveToPoint not handled for <marker>)
121            m_origin = points[1];
122            break;
123        case PathElementAddCurveToPoint:
124            m_inslopePoints[0] = points[1];
125            m_inslopePoints[1] = points[2];
126            m_origin = points[2];
127            break;
128        case PathElementMoveToPoint:
129            m_subpathStart = points[0];
130        case PathElementAddLineToPoint:
131            updateInslope(points[0]);
132            m_origin = points[0];
133            break;
134        case PathElementCloseSubpath:
135            updateInslope(points[0]);
136            m_origin = m_subpathStart;
137            m_subpathStart = FloatPoint();
138        }
139    }
140
141    void updateInslope(const FloatPoint& point)
142    {
143        m_inslopePoints[0] = m_origin;
144        m_inslopePoints[1] = point;
145    }
146
147    Vector<MarkerPosition>& m_positions;
148    unsigned m_elementIndex;
149    FloatPoint m_origin;
150    FloatPoint m_subpathStart;
151    FloatPoint m_inslopePoints[2];
152    FloatPoint m_outslopePoints[2];
153};
154
155}
156
157#endif // ENABLE(SVG)
158#endif // SVGMarkerData_h
159