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#include "platform/FloatConversion.h"
24#include "platform/graphics/Path.h"
25#include "wtf/MathExtras.h"
26
27namespace blink {
28
29enum SVGMarkerType {
30    StartMarker,
31    MidMarker,
32    EndMarker
33};
34
35struct MarkerPosition {
36    MarkerPosition(SVGMarkerType useType, const FloatPoint& useOrigin, float useAngle)
37        : type(useType)
38        , origin(useOrigin)
39        , angle(useAngle)
40    {
41    }
42
43    SVGMarkerType type;
44    FloatPoint origin;
45    float angle;
46};
47
48class SVGMarkerData {
49public:
50    SVGMarkerData(Vector<MarkerPosition>& positions, bool autoStartReverse)
51        : m_positions(positions)
52        , m_elementIndex(0)
53        , m_autoStartReverse(autoStartReverse)
54    {
55    }
56
57    static void updateFromPathElement(void* info, const PathElement* element)
58    {
59        SVGMarkerData* markerData = static_cast<SVGMarkerData*>(info);
60
61        // First update the outslope for the previous element.
62        markerData->updateOutslope(element->points[0]);
63
64        // Record the marker for the previous element.
65        if (markerData->m_elementIndex > 0) {
66            SVGMarkerType markerType = markerData->m_elementIndex == 1 ? StartMarker : MidMarker;
67            markerData->m_positions.append(MarkerPosition(markerType, markerData->m_origin, markerData->currentAngle(markerType)));
68        }
69
70        // Update our marker data for this element.
71        markerData->updateMarkerDataForPathElement(element);
72        ++markerData->m_elementIndex;
73    }
74
75    void pathIsDone()
76    {
77        m_positions.append(MarkerPosition(EndMarker, m_origin, currentAngle(EndMarker)));
78    }
79
80private:
81    float currentAngle(SVGMarkerType type) const
82    {
83        // For details of this calculation, see: http://www.w3.org/TR/SVG/single-page.html#painting-MarkerElement
84        FloatPoint inSlope(m_inslopePoints[1] - m_inslopePoints[0]);
85        FloatPoint outSlope(m_outslopePoints[1] - m_outslopePoints[0]);
86
87        double inAngle = rad2deg(inSlope.slopeAngleRadians());
88        double outAngle = rad2deg(outSlope.slopeAngleRadians());
89
90        switch (type) {
91        case StartMarker:
92            if (m_autoStartReverse)
93                outAngle += 180;
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    bool m_autoStartReverse;
154};
155
156}
157
158#endif // SVGMarkerData_h
159