OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2002, 2003 The Karbon Developers | 2 * Copyright (C) 2002, 2003 The Karbon Developers |
3 * Copyright (C) 2006 Alexander Kellett <lypanov@kde.org> | 3 * Copyright (C) 2006 Alexander Kellett <lypanov@kde.org> |
4 * Copyright (C) 2006, 2007 Rob Buis <buis@kde.org> | 4 * Copyright (C) 2006, 2007 Rob Buis <buis@kde.org> |
5 * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. | 5 * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. |
6 * Copyright (C) Research In Motion Limited 2010. All rights reserved. | 6 * Copyright (C) Research In Motion Limited 2010. All rights reserved. |
7 * | 7 * |
8 * This library is free software; you can redistribute it and/or | 8 * This library is free software; you can redistribute it and/or |
9 * modify it under the terms of the GNU Library General Public | 9 * modify it under the terms of the GNU Library General Public |
10 * License as published by the Free Software Foundation; either | 10 * License as published by the Free Software Foundation; either |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
53 | 53 |
54 if (!m_consumer->continueConsuming()) | 54 if (!m_consumer->continueConsuming()) |
55 return true; | 55 return true; |
56 | 56 |
57 if (m_source->hasMoreData()) | 57 if (m_source->hasMoreData()) |
58 m_consumer->incrementPathSegmentCount(); | 58 m_consumer->incrementPathSegmentCount(); |
59 } | 59 } |
60 return true; | 60 return true; |
61 } | 61 } |
62 | 62 |
63 class NormalizingConsumer { | |
64 STACK_ALLOCATED(); | |
65 public: | |
66 NormalizingConsumer(SVGPathConsumer* consumer) | |
67 : m_consumer(consumer) | |
68 , m_lastCommand(PathSegUnknown) | |
69 { | |
70 ASSERT(m_consumer); | |
71 } | |
72 | |
73 void emitSegment(PathSegmentData&); | |
74 | |
75 private: | |
76 bool decomposeArcToCubic(const FloatPoint& currentPoint, const PathSegmentDa
ta&); | |
77 | |
78 SVGPathConsumer* m_consumer; | |
79 FloatPoint m_controlPoint; | |
80 FloatPoint m_currentPoint; | |
81 FloatPoint m_subPathPoint; | |
82 SVGPathSegType m_lastCommand; | |
83 }; | |
84 | |
85 static FloatPoint reflectedPoint(const FloatPoint& reflectIn, const FloatPoint&
pointToReflect) | 63 static FloatPoint reflectedPoint(const FloatPoint& reflectIn, const FloatPoint&
pointToReflect) |
86 { | 64 { |
87 return FloatPoint(2 * reflectIn.x() - pointToReflect.x(), 2 * reflectIn.y()
- pointToReflect.y()); | 65 return FloatPoint(2 * reflectIn.x() - pointToReflect.x(), 2 * reflectIn.y()
- pointToReflect.y()); |
88 } | 66 } |
89 | 67 |
90 // Blend the points with a ratio (1/3):(2/3). | 68 // Blend the points with a ratio (1/3):(2/3). |
91 static FloatPoint blendPoints(const FloatPoint& p1, const FloatPoint& p2) | 69 static FloatPoint blendPoints(const FloatPoint& p1, const FloatPoint& p2) |
92 { | 70 { |
93 const float oneOverThree = 1 / 3.f; | 71 const float oneOverThree = 1 / 3.f; |
94 return FloatPoint((p1.x() + 2 * p2.x()) * oneOverThree, (p1.y() + 2 * p2.y()
) * oneOverThree); | 72 return FloatPoint((p1.x() + 2 * p2.x()) * oneOverThree, (p1.y() + 2 * p2.y()
) * oneOverThree); |
95 } | 73 } |
96 | 74 |
97 static inline bool isCubicCommand(SVGPathSegType command) | 75 static inline bool isCubicCommand(SVGPathSegType command) |
98 { | 76 { |
99 return command == PathSegCurveToCubicAbs | 77 return command == PathSegCurveToCubicAbs |
100 || command == PathSegCurveToCubicRel | 78 || command == PathSegCurveToCubicRel |
101 || command == PathSegCurveToCubicSmoothAbs | 79 || command == PathSegCurveToCubicSmoothAbs |
102 || command == PathSegCurveToCubicSmoothRel; | 80 || command == PathSegCurveToCubicSmoothRel; |
103 } | 81 } |
104 | 82 |
105 static inline bool isQuadraticCommand(SVGPathSegType command) | 83 static inline bool isQuadraticCommand(SVGPathSegType command) |
106 { | 84 { |
107 return command == PathSegCurveToQuadraticAbs | 85 return command == PathSegCurveToQuadraticAbs |
108 || command == PathSegCurveToQuadraticRel | 86 || command == PathSegCurveToQuadraticRel |
109 || command == PathSegCurveToQuadraticSmoothAbs | 87 || command == PathSegCurveToQuadraticSmoothAbs |
110 || command == PathSegCurveToQuadraticSmoothRel; | 88 || command == PathSegCurveToQuadraticSmoothRel; |
111 } | 89 } |
112 | 90 |
113 void NormalizingConsumer::emitSegment(PathSegmentData& segment) | 91 void SVGPathNormalizer::emitSegment(const PathSegmentData& segment) |
114 { | 92 { |
115 SVGPathSegType originalCommand = segment.command; | 93 PathSegmentData normSeg = segment; |
116 | 94 |
117 // Convert relative points to absolute points. | 95 // Convert relative points to absolute points. |
118 switch (segment.command) { | 96 switch (segment.command) { |
119 case PathSegCurveToQuadraticRel: | 97 case PathSegCurveToQuadraticRel: |
120 segment.point1 += m_currentPoint; | 98 normSeg.point1 += m_currentPoint; |
121 segment.targetPoint += m_currentPoint; | 99 normSeg.targetPoint += m_currentPoint; |
122 break; | 100 break; |
123 case PathSegCurveToCubicRel: | 101 case PathSegCurveToCubicRel: |
124 segment.point1 += m_currentPoint; | 102 normSeg.point1 += m_currentPoint; |
125 /* fall through */ | 103 /* fall through */ |
126 case PathSegCurveToCubicSmoothRel: | 104 case PathSegCurveToCubicSmoothRel: |
127 segment.point2 += m_currentPoint; | 105 normSeg.point2 += m_currentPoint; |
128 /* fall through */ | 106 /* fall through */ |
129 case PathSegMoveToRel: | 107 case PathSegMoveToRel: |
130 case PathSegLineToRel: | 108 case PathSegLineToRel: |
131 case PathSegLineToHorizontalRel: | 109 case PathSegLineToHorizontalRel: |
132 case PathSegLineToVerticalRel: | 110 case PathSegLineToVerticalRel: |
133 case PathSegCurveToQuadraticSmoothRel: | 111 case PathSegCurveToQuadraticSmoothRel: |
134 case PathSegArcRel: | 112 case PathSegArcRel: |
135 segment.targetPoint += m_currentPoint; | 113 normSeg.targetPoint += m_currentPoint; |
136 break; | 114 break; |
137 case PathSegLineToHorizontalAbs: | 115 case PathSegLineToHorizontalAbs: |
138 segment.targetPoint.setY(m_currentPoint.y()); | 116 normSeg.targetPoint.setY(m_currentPoint.y()); |
139 break; | 117 break; |
140 case PathSegLineToVerticalAbs: | 118 case PathSegLineToVerticalAbs: |
141 segment.targetPoint.setX(m_currentPoint.x()); | 119 normSeg.targetPoint.setX(m_currentPoint.x()); |
142 break; | 120 break; |
143 case PathSegClosePath: | 121 case PathSegClosePath: |
144 // Reset m_currentPoint for the next path. | 122 // Reset m_currentPoint for the next path. |
145 segment.targetPoint = m_subPathPoint; | 123 normSeg.targetPoint = m_subPathPoint; |
146 break; | 124 break; |
147 default: | 125 default: |
148 break; | 126 break; |
149 } | 127 } |
150 | 128 |
151 // Update command verb, handle smooth segments and convert quadratic curve | 129 // Update command verb, handle smooth segments and convert quadratic curve |
152 // segments to cubics. | 130 // segments to cubics. |
153 switch (segment.command) { | 131 switch (segment.command) { |
154 case PathSegMoveToRel: | 132 case PathSegMoveToRel: |
155 case PathSegMoveToAbs: | 133 case PathSegMoveToAbs: |
156 m_subPathPoint = segment.targetPoint; | 134 m_subPathPoint = normSeg.targetPoint; |
157 segment.command = PathSegMoveToAbs; | 135 normSeg.command = PathSegMoveToAbs; |
158 break; | 136 break; |
159 case PathSegLineToRel: | 137 case PathSegLineToRel: |
160 case PathSegLineToAbs: | 138 case PathSegLineToAbs: |
161 case PathSegLineToHorizontalRel: | 139 case PathSegLineToHorizontalRel: |
162 case PathSegLineToHorizontalAbs: | 140 case PathSegLineToHorizontalAbs: |
163 case PathSegLineToVerticalRel: | 141 case PathSegLineToVerticalRel: |
164 case PathSegLineToVerticalAbs: | 142 case PathSegLineToVerticalAbs: |
165 segment.command = PathSegLineToAbs; | 143 normSeg.command = PathSegLineToAbs; |
166 break; | 144 break; |
167 case PathSegClosePath: | 145 case PathSegClosePath: |
| 146 normSeg.command = PathSegClosePath; |
168 break; | 147 break; |
169 case PathSegCurveToCubicSmoothRel: | 148 case PathSegCurveToCubicSmoothRel: |
170 case PathSegCurveToCubicSmoothAbs: | 149 case PathSegCurveToCubicSmoothAbs: |
171 if (!isCubicCommand(m_lastCommand)) | 150 if (!isCubicCommand(m_lastCommand)) |
172 segment.point1 = m_currentPoint; | 151 normSeg.point1 = m_currentPoint; |
173 else | 152 else |
174 segment.point1 = reflectedPoint(m_currentPoint, m_controlPoint); | 153 normSeg.point1 = reflectedPoint(m_currentPoint, m_controlPoint); |
175 /* fall through */ | 154 /* fall through */ |
176 case PathSegCurveToCubicRel: | 155 case PathSegCurveToCubicRel: |
177 case PathSegCurveToCubicAbs: | 156 case PathSegCurveToCubicAbs: |
178 m_controlPoint = segment.point2; | 157 m_controlPoint = normSeg.point2; |
179 segment.command = PathSegCurveToCubicAbs; | 158 normSeg.command = PathSegCurveToCubicAbs; |
180 break; | 159 break; |
181 case PathSegCurveToQuadraticSmoothRel: | 160 case PathSegCurveToQuadraticSmoothRel: |
182 case PathSegCurveToQuadraticSmoothAbs: | 161 case PathSegCurveToQuadraticSmoothAbs: |
183 if (!isQuadraticCommand(m_lastCommand)) | 162 if (!isQuadraticCommand(m_lastCommand)) |
184 segment.point1 = m_currentPoint; | 163 normSeg.point1 = m_currentPoint; |
185 else | 164 else |
186 segment.point1 = reflectedPoint(m_currentPoint, m_controlPoint); | 165 normSeg.point1 = reflectedPoint(m_currentPoint, m_controlPoint); |
187 /* fall through */ | 166 /* fall through */ |
188 case PathSegCurveToQuadraticRel: | 167 case PathSegCurveToQuadraticRel: |
189 case PathSegCurveToQuadraticAbs: | 168 case PathSegCurveToQuadraticAbs: |
190 // Save the unmodified control point. | 169 // Save the unmodified control point. |
191 m_controlPoint = segment.point1; | 170 m_controlPoint = normSeg.point1; |
192 segment.point1 = blendPoints(m_currentPoint, m_controlPoint); | 171 normSeg.point1 = blendPoints(m_currentPoint, m_controlPoint); |
193 segment.point2 = blendPoints(segment.targetPoint, m_controlPoint); | 172 normSeg.point2 = blendPoints(normSeg.targetPoint, m_controlPoint); |
194 segment.command = PathSegCurveToCubicAbs; | 173 normSeg.command = PathSegCurveToCubicAbs; |
195 break; | 174 break; |
196 case PathSegArcRel: | 175 case PathSegArcRel: |
197 case PathSegArcAbs: | 176 case PathSegArcAbs: |
198 if (!decomposeArcToCubic(m_currentPoint, segment)) { | 177 if (!decomposeArcToCubic(m_currentPoint, normSeg)) { |
199 // On failure, emit a line segment to the target point. | 178 // On failure, emit a line segment to the target point. |
200 segment.command = PathSegLineToAbs; | 179 normSeg.command = PathSegLineToAbs; |
201 } else { | 180 } else { |
202 // decomposeArcToCubic() has already emitted the normalized | 181 // decomposeArcToCubic() has already emitted the normalized |
203 // segments, so set command to PathSegArcAbs, to skip any further | 182 // segments, so set command to PathSegArcAbs, to skip any further |
204 // emit. | 183 // emit. |
205 segment.command = PathSegArcAbs; | 184 normSeg.command = PathSegArcAbs; |
206 } | 185 } |
207 break; | 186 break; |
208 default: | 187 default: |
209 ASSERT_NOT_REACHED(); | 188 ASSERT_NOT_REACHED(); |
210 } | 189 } |
211 | 190 |
212 if (segment.command != PathSegArcAbs) | 191 if (normSeg.command != PathSegArcAbs) |
213 m_consumer->emitSegment(segment); | 192 m_consumer->emitSegment(normSeg); |
214 | 193 |
215 m_currentPoint = segment.targetPoint; | 194 m_currentPoint = normSeg.targetPoint; |
216 | 195 |
217 if (!isCubicCommand(originalCommand) && !isQuadraticCommand(originalCommand)
) | 196 if (!isCubicCommand(segment.command) && !isQuadraticCommand(segment.command)
) |
218 m_controlPoint = m_currentPoint; | 197 m_controlPoint = m_currentPoint; |
219 | 198 |
220 m_lastCommand = originalCommand; | 199 m_lastCommand = segment.command; |
221 } | 200 } |
222 | 201 |
223 // This works by converting the SVG arc to "simple" beziers. | 202 // This works by converting the SVG arc to "simple" beziers. |
224 // Partly adapted from Niko's code in kdelibs/kdecore/svgicons. | 203 // Partly adapted from Niko's code in kdelibs/kdecore/svgicons. |
225 // See also SVG implementation notes: http://www.w3.org/TR/SVG/implnote.html#Arc
ConversionEndpointToCenter | 204 // See also SVG implementation notes: http://www.w3.org/TR/SVG/implnote.html#Arc
ConversionEndpointToCenter |
226 bool NormalizingConsumer::decomposeArcToCubic(const FloatPoint& currentPoint, co
nst PathSegmentData& arcSegment) | 205 bool SVGPathNormalizer::decomposeArcToCubic(const FloatPoint& currentPoint, cons
t PathSegmentData& arcSegment) |
227 { | 206 { |
228 // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (
a "lineto") joining the endpoints. | 207 // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (
a "lineto") joining the endpoints. |
229 // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters | 208 // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters |
230 float rx = fabsf(arcSegment.arcRadii().x()); | 209 float rx = fabsf(arcSegment.arcRadii().x()); |
231 float ry = fabsf(arcSegment.arcRadii().y()); | 210 float ry = fabsf(arcSegment.arcRadii().y()); |
232 if (!rx || !ry) | 211 if (!rx || !ry) |
233 return false; | 212 return false; |
234 | 213 |
235 // If the current point and target point for the arc are identical, it shoul
d be treated as a zero length | 214 // If the current point and target point for the arc are identical, it shoul
d be treated as a zero length |
236 // path. This ensures continuity in animations. | 215 // path. This ensures continuity in animations. |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
320 cubicSegment.point2 = pointTransform.mapPoint(point2); | 299 cubicSegment.point2 = pointTransform.mapPoint(point2); |
321 cubicSegment.targetPoint = pointTransform.mapPoint(targetPoint); | 300 cubicSegment.targetPoint = pointTransform.mapPoint(targetPoint); |
322 | 301 |
323 m_consumer->emitSegment(cubicSegment); | 302 m_consumer->emitSegment(cubicSegment); |
324 } | 303 } |
325 return true; | 304 return true; |
326 } | 305 } |
327 | 306 |
328 bool SVGPathParser::parseAndNormalizePath() | 307 bool SVGPathParser::parseAndNormalizePath() |
329 { | 308 { |
330 NormalizingConsumer normalizer(m_consumer); | 309 SVGPathNormalizer normalizer(m_consumer); |
331 | 310 |
332 while (m_source->hasMoreData()) { | 311 while (m_source->hasMoreData()) { |
333 PathSegmentData segment = m_source->parseSegment(); | 312 PathSegmentData segment = m_source->parseSegment(); |
334 if (segment.command == PathSegUnknown) | 313 if (segment.command == PathSegUnknown) |
335 return false; | 314 return false; |
336 | 315 |
337 normalizer.emitSegment(segment); | 316 normalizer.emitSegment(segment); |
338 | 317 |
339 if (!m_consumer->continueConsuming()) | 318 if (!m_consumer->continueConsuming()) |
340 return true; | 319 return true; |
341 | 320 |
342 if (m_source->hasMoreData()) | 321 if (m_source->hasMoreData()) |
343 m_consumer->incrementPathSegmentCount(); | 322 m_consumer->incrementPathSegmentCount(); |
344 } | 323 } |
345 return true; | 324 return true; |
346 } | 325 } |
347 | 326 |
348 } | 327 } |
OLD | NEW |