| 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 |