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