LCOV - code coverage report
Current view: top level - dom/svg - nsSVGPathDataParser.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 175 212 82.5 %
Date: 2017-07-14 16:53:18 Functions: 21 22 95.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "nsSVGPathDataParser.h"
       8             : 
       9             : #include "mozilla/gfx/Point.h"
      10             : #include "nsSVGDataParser.h"
      11             : #include "SVGContentUtils.h"
      12             : #include "SVGPathData.h"
      13             : #include "SVGPathSegUtils.h"
      14             : 
      15             : using namespace mozilla;
      16             : using namespace mozilla::gfx;
      17             : 
      18         642 : static inline char16_t ToUpper(char16_t aCh)
      19             : {
      20         642 :   return aCh >= 'a' && aCh <= 'z' ? aCh - 'a' + 'A' : aCh;
      21             : }
      22             : 
      23             : bool
      24          44 : nsSVGPathDataParser::Parse()
      25             : {
      26          44 :   mPathSegList->Clear();
      27          44 :   return ParsePath();
      28             : }
      29             : 
      30             : //----------------------------------------------------------------------
      31             : 
      32             : bool
      33        1292 : nsSVGPathDataParser::ParseCoordPair(float& aX, float& aY)
      34             : {
      35        2584 :   return SVGContentUtils::ParseNumber(mIter, mEnd, aX) &&
      36        2584 :          SkipCommaWsp() &&
      37        2584 :          SVGContentUtils::ParseNumber(mIter, mEnd, aY);
      38             : }
      39             : 
      40             : bool
      41         224 : nsSVGPathDataParser::ParseFlag(bool& aFlag)
      42             : {
      43         224 :   if (mIter == mEnd || (*mIter != '0' && *mIter != '1')) {
      44           0 :     return false;
      45             :   }
      46         224 :   aFlag = (*mIter == '1');
      47             : 
      48         224 :   ++mIter;
      49         224 :   return true;
      50             : }
      51             : 
      52             : //----------------------------------------------------------------------
      53             : 
      54             : bool
      55         132 : nsSVGPathDataParser::ParsePath()
      56             : {
      57         220 :   while (SkipWsp()) {
      58          88 :     if (!ParseSubPath()) {
      59           0 :       return false;
      60             :     }
      61             :   }
      62             : 
      63          44 :   return true;
      64             : }
      65             : 
      66             : //----------------------------------------------------------------------
      67             : 
      68             : bool
      69          88 : nsSVGPathDataParser::ParseSubPath()
      70             : {
      71          88 :   return ParseMoveto() && ParseSubPathElements();
      72             : }
      73             : 
      74             : bool
      75         730 : nsSVGPathDataParser::ParseSubPathElements()
      76             : {
      77        1372 :   while (SkipWsp() && !IsStartOfSubPath()) {
      78         642 :     char16_t commandType = ToUpper(*mIter);
      79             : 
      80             :     // Upper case commands have absolute co-ordinates,
      81             :     // lower case commands have relative co-ordinates.
      82         642 :     bool absCoords = commandType == *mIter;
      83             : 
      84         642 :     ++mIter;
      85         642 :     SkipWsp();
      86             : 
      87         642 :     if (!ParseSubPathElement(commandType, absCoords)) {
      88           0 :       return false;
      89             :     }
      90             :   }
      91          88 :   return true;
      92             : }
      93             : 
      94             : bool
      95         642 : nsSVGPathDataParser::ParseSubPathElement(char16_t aCommandType,
      96             :                                          bool aAbsCoords)
      97             : {
      98         642 :   switch (aCommandType) {
      99             :     case 'Z':
     100          83 :       return ParseClosePath();
     101             :     case 'L':
     102         209 :       return ParseLineto(aAbsCoords);
     103             :     case 'H':
     104          31 :       return ParseHorizontalLineto(aAbsCoords);
     105             :     case 'V':
     106          27 :       return ParseVerticalLineto(aAbsCoords);
     107             :     case 'C':
     108         173 :       return ParseCurveto(aAbsCoords);
     109             :     case 'S':
     110          28 :       return ParseSmoothCurveto(aAbsCoords);
     111             :     case 'Q':
     112           1 :       return ParseQuadBezierCurveto(aAbsCoords);
     113             :     case 'T':
     114           0 :       return ParseSmoothQuadBezierCurveto(aAbsCoords);
     115             :     case 'A':
     116          90 :       return ParseEllipticalArc(aAbsCoords);
     117             :   }
     118           0 :   return false;
     119             : }
     120             : 
     121             : bool
     122         774 : nsSVGPathDataParser::IsStartOfSubPath() const
     123             : {
     124         774 :   return *mIter == 'm' || *mIter == 'M';
     125             : }
     126             : 
     127             : //----------------------------------------------------------------------
     128             : 
     129             : bool
     130          88 : nsSVGPathDataParser::ParseMoveto()
     131             : {
     132          88 :   if (!IsStartOfSubPath()) {
     133           0 :     return false;
     134             :   }
     135             : 
     136          88 :   bool absCoords = (*mIter == 'M');
     137             : 
     138          88 :   ++mIter;
     139          88 :   SkipWsp();
     140             : 
     141             :   float x, y;
     142          88 :   if (!ParseCoordPair(x, y)) {
     143           0 :     return false;
     144             :   }
     145             : 
     146          88 :   if (NS_FAILED(mPathSegList->AppendSeg(
     147             :                   absCoords ? PATHSEG_MOVETO_ABS : PATHSEG_MOVETO_REL,
     148             :                   x, y))) {
     149           0 :     return false;
     150             :   }
     151             : 
     152          88 :   if (!SkipWsp() || IsAlpha(*mIter)) {
     153             :     // End of data, or start of a new command
     154          82 :     return true;
     155             :   }
     156             : 
     157           6 :   SkipCommaWsp();
     158             : 
     159             :   // Per SVG 1.1 Section 8.3.2
     160             :   // If a moveto is followed by multiple pairs of coordinates,
     161             :   // the subsequent pairs are treated as implicit lineto commands
     162           6 :   return ParseLineto(absCoords);
     163             : }
     164             : 
     165             : //----------------------------------------------------------------------
     166             : 
     167             : bool
     168          83 : nsSVGPathDataParser::ParseClosePath()
     169             : {
     170          83 :   return NS_SUCCEEDED(mPathSegList->AppendSeg(PATHSEG_CLOSEPATH));
     171             : }
     172             : 
     173             : //----------------------------------------------------------------------
     174             : 
     175             : bool
     176         267 : nsSVGPathDataParser::ParseLineto(bool aAbsCoords)
     177             : {
     178             :   while (true) {
     179             :     float x, y;
     180         267 :     if (!ParseCoordPair(x, y)) {
     181         215 :       return false;
     182             :     }
     183             : 
     184         267 :     if (NS_FAILED(mPathSegList->AppendSeg(
     185             :                    aAbsCoords ? PATHSEG_LINETO_ABS : PATHSEG_LINETO_REL,
     186             :                    x, y))) {
     187           0 :       return false;
     188             :     }
     189             : 
     190         267 :     if (!SkipWsp() || IsAlpha(*mIter)) {
     191             :       // End of data, or start of a new command
     192         215 :       return true;
     193             :     }
     194          52 :     SkipCommaWsp();
     195          52 :   }
     196             : }
     197             : 
     198             : //----------------------------------------------------------------------
     199             : 
     200             : bool
     201          31 : nsSVGPathDataParser::ParseHorizontalLineto(bool aAbsCoords)
     202             : {
     203             :   while (true) {
     204             :     float x;
     205          31 :     if (!SVGContentUtils::ParseNumber(mIter, mEnd, x)) {
     206          31 :       return false;
     207             :     }
     208             : 
     209          31 :     if (NS_FAILED(mPathSegList->AppendSeg(
     210             :                     aAbsCoords ? PATHSEG_LINETO_HORIZONTAL_ABS : PATHSEG_LINETO_HORIZONTAL_REL,
     211             :                     x))) {
     212           0 :       return false;
     213             :     }
     214             : 
     215          31 :     if (!SkipWsp() || IsAlpha(*mIter)) {
     216             :       // End of data, or start of a new command
     217          31 :       return true;
     218             :     }
     219           0 :     SkipCommaWsp();
     220           0 :   }
     221             : }
     222             : 
     223             : //----------------------------------------------------------------------
     224             : 
     225             : bool
     226          27 : nsSVGPathDataParser::ParseVerticalLineto(bool aAbsCoords)
     227             : {
     228             :   while (true) {
     229             :     float y;
     230          27 :     if (!SVGContentUtils::ParseNumber(mIter, mEnd, y)) {
     231          27 :       return false;
     232             :     }
     233             : 
     234          27 :     if (NS_FAILED(mPathSegList->AppendSeg(
     235             :                     aAbsCoords ? PATHSEG_LINETO_VERTICAL_ABS : PATHSEG_LINETO_VERTICAL_REL,
     236             :                     y))) {
     237           0 :       return false;
     238             :     }
     239             : 
     240          27 :     if (!SkipWsp() || IsAlpha(*mIter)) {
     241             :       // End of data, or start of a new command
     242          27 :       return true;
     243             :     }
     244           0 :     SkipCommaWsp();
     245           0 :   }
     246             : }
     247             : 
     248             : //----------------------------------------------------------------------
     249             : 
     250             : bool
     251         253 : nsSVGPathDataParser::ParseCurveto(bool aAbsCoords)
     252             : {
     253             :   while (true) {
     254             :     float x1, y1, x2, y2, x, y;
     255             : 
     256         759 :     if (!(ParseCoordPair(x1, y1) &&
     257         506 :           SkipCommaWsp() &&
     258         506 :           ParseCoordPair(x2, y2) &&
     259         253 :           SkipCommaWsp() &&
     260         253 :           ParseCoordPair(x, y))) {
     261         173 :       return false;
     262             :     }
     263             : 
     264         253 :     if (NS_FAILED(mPathSegList->AppendSeg(
     265             :                     aAbsCoords ? PATHSEG_CURVETO_CUBIC_ABS : PATHSEG_CURVETO_CUBIC_REL,
     266             :                     x1, y1, x2, y2, x, y))) {
     267           0 :       return false;
     268             :     }
     269             : 
     270         253 :     if (!SkipWsp() || IsAlpha(*mIter)) {
     271             :       // End of data, or start of a new command
     272         173 :       return true;
     273             :     }
     274          80 :     SkipCommaWsp();
     275          80 :   }
     276             : }
     277             : 
     278             : //----------------------------------------------------------------------
     279             : 
     280             : bool
     281          32 : nsSVGPathDataParser::ParseSmoothCurveto(bool aAbsCoords)
     282             : {
     283             :   while (true) {
     284             :     float x2, y2, x, y;
     285          96 :     if (!(ParseCoordPair(x2, y2) &&
     286          32 :           SkipCommaWsp() &&
     287          32 :           ParseCoordPair(x, y))) {
     288          28 :       return false;
     289             :     }
     290             : 
     291          32 :     if (NS_FAILED(mPathSegList->AppendSeg(
     292             :                     aAbsCoords ? PATHSEG_CURVETO_CUBIC_SMOOTH_ABS : PATHSEG_CURVETO_CUBIC_SMOOTH_REL,
     293             :                     x2, y2, x, y))) {
     294           0 :       return false;
     295             :     }
     296             : 
     297          32 :     if (!SkipWsp() || IsAlpha(*mIter)) {
     298             :       // End of data, or start of a new command
     299          28 :       return true;
     300             :     }
     301           4 :     SkipCommaWsp();
     302           4 :   }
     303             : }
     304             : 
     305             : //----------------------------------------------------------------------
     306             : 
     307             : bool
     308           1 : nsSVGPathDataParser::ParseQuadBezierCurveto(bool aAbsCoords)
     309             : {
     310             :   while (true) {
     311             :     float x1, y1, x, y;
     312           3 :     if (!(ParseCoordPair(x1, y1) &&
     313           1 :          SkipCommaWsp() &&
     314           1 :          ParseCoordPair(x, y))) {
     315           1 :       return false;
     316             :     }
     317             : 
     318           1 :     if (NS_FAILED(mPathSegList->AppendSeg(
     319             :                     aAbsCoords ? PATHSEG_CURVETO_QUADRATIC_ABS : PATHSEG_CURVETO_QUADRATIC_REL,
     320             :                     x1, y1, x, y))) {
     321           0 :       return false;
     322             :     }
     323             : 
     324           1 :     if (!SkipWsp() || IsAlpha(*mIter)) {
     325             :       // Start of a new command
     326           1 :       return true;
     327             :     }
     328           0 :     SkipCommaWsp();
     329           0 :   }
     330             : }
     331             : 
     332             : //----------------------------------------------------------------------
     333             : 
     334             : bool
     335           0 : nsSVGPathDataParser::ParseSmoothQuadBezierCurveto(bool aAbsCoords)
     336             : {
     337             :   while (true) {
     338             :     float x, y;
     339           0 :     if (!ParseCoordPair(x, y)) {
     340           0 :       return false;
     341             :     }
     342             : 
     343           0 :     if (NS_FAILED(mPathSegList->AppendSeg(
     344             :                     aAbsCoords ? PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS : PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL,
     345             :                     x, y))) {
     346           0 :       return false;
     347             :     }
     348             : 
     349           0 :     if (!SkipWsp() || IsAlpha(*mIter)) {
     350             :       // End of data, or start of a new command
     351           0 :       return true;
     352             :     }
     353           0 :     SkipCommaWsp();
     354           0 :   }
     355             : }
     356             : 
     357             : //----------------------------------------------------------------------
     358             : 
     359             : bool
     360         112 : nsSVGPathDataParser::ParseEllipticalArc(bool aAbsCoords)
     361             : {
     362             :   while (true) {
     363             :     float r1, r2, angle, x, y;
     364             :     bool largeArcFlag, sweepFlag;
     365             : 
     366         336 :     if (!(SVGContentUtils::ParseNumber(mIter, mEnd, r1) &&
     367         224 :           SkipCommaWsp() &&
     368         224 :           SVGContentUtils::ParseNumber(mIter, mEnd, r2) &&
     369         224 :           SkipCommaWsp() &&
     370         224 :           SVGContentUtils::ParseNumber(mIter, mEnd, angle)&&
     371         224 :           SkipCommaWsp() &&
     372         224 :           ParseFlag(largeArcFlag) &&
     373         224 :           SkipCommaWsp() &&
     374         224 :           ParseFlag(sweepFlag) &&
     375         112 :           SkipCommaWsp() &&
     376         112 :           ParseCoordPair(x, y))) {
     377          90 :       return false;
     378             :     }
     379             : 
     380             :     // We can only pass floats after 'type', and per the SVG spec for arc,
     381             :     // non-zero args are treated at 'true'.
     382         112 :     if (NS_FAILED(mPathSegList->AppendSeg(
     383             :                     aAbsCoords ? PATHSEG_ARC_ABS : PATHSEG_ARC_REL,
     384             :                     r1, r2, angle,
     385             :                     largeArcFlag ? 1.0f : 0.0f,
     386             :                     sweepFlag ? 1.0f : 0.0f,
     387             :                     x, y))) {
     388           0 :       return false;
     389             :     }
     390             : 
     391         112 :     if (!SkipWsp() || IsAlpha(*mIter)) {
     392             :       // End of data, or start of a new command
     393          90 :       return true;
     394             :     }
     395          22 :     SkipCommaWsp();
     396          22 :   }
     397             : }
     398             : 
     399             : //-----------------------------------------------------------------------
     400             : 
     401             : 
     402             : 
     403             : 
     404             : static double
     405         208 : CalcVectorAngle(double ux, double uy, double vx, double vy)
     406             : {
     407         208 :   double ta = atan2(uy, ux);
     408         208 :   double tb = atan2(vy, vx);
     409         208 :   if (tb >= ta)
     410         102 :     return tb-ta;
     411         106 :   return 2 * M_PI - (ta-tb);
     412             : }
     413             : 
     414             : 
     415         104 : nsSVGArcConverter::nsSVGArcConverter(const Point& from,
     416             :                                      const Point& to,
     417             :                                      const Point& radii,
     418             :                                      double angle,
     419             :                                      bool largeArcFlag,
     420         104 :                                      bool sweepFlag)
     421             : {
     422         104 :   const double radPerDeg = M_PI/180.0;
     423         104 :   mSegIndex = 0;
     424             : 
     425         104 :   if (from == to) {
     426           0 :     mNumSegs = 0;
     427           0 :     return;
     428             :   }
     429             : 
     430             :   // Convert to center parameterization as shown in
     431             :   // http://www.w3.org/TR/SVG/implnote.html
     432         104 :   mRx = fabs(radii.x);
     433         104 :   mRy = fabs(radii.y);
     434             : 
     435         104 :   mSinPhi = sin(angle*radPerDeg);
     436         104 :   mCosPhi = cos(angle*radPerDeg);
     437             : 
     438         104 :   double x1dash =  mCosPhi * (from.x-to.x)/2.0 + mSinPhi * (from.y-to.y)/2.0;
     439         104 :   double y1dash = -mSinPhi * (from.x-to.x)/2.0 + mCosPhi * (from.y-to.y)/2.0;
     440             : 
     441             :   double root;
     442         104 :   double numerator = mRx*mRx*mRy*mRy - mRx*mRx*y1dash*y1dash -
     443         104 :                      mRy*mRy*x1dash*x1dash;
     444             : 
     445         104 :   if (numerator < 0.0) {
     446             :     //  If mRx , mRy and are such that there is no solution (basically,
     447             :     //  the ellipse is not big enough to reach from 'from' to 'to'
     448             :     //  then the ellipse is scaled up uniformly until there is
     449             :     //  exactly one solution (until the ellipse is just big enough).
     450             : 
     451             :     // -> find factor s, such that numerator' with mRx'=s*mRx and
     452             :     //    mRy'=s*mRy becomes 0 :
     453           0 :     double s = sqrt(1.0 - numerator/(mRx*mRx*mRy*mRy));
     454             : 
     455           0 :     mRx *= s;
     456           0 :     mRy *= s;
     457           0 :     root = 0.0;
     458             : 
     459             :   }
     460             :   else {
     461         208 :     root = (largeArcFlag == sweepFlag ? -1.0 : 1.0) *
     462         104 :       sqrt( numerator/(mRx*mRx*y1dash*y1dash + mRy*mRy*x1dash*x1dash) );
     463             :   }
     464             : 
     465         104 :   double cxdash = root*mRx*y1dash/mRy;
     466         104 :   double cydash = -root*mRy*x1dash/mRx;
     467             : 
     468         104 :   mC.x = mCosPhi * cxdash - mSinPhi * cydash + (from.x+to.x)/2.0;
     469         104 :   mC.y = mSinPhi * cxdash + mCosPhi * cydash + (from.y+to.y)/2.0;
     470         104 :   mTheta = CalcVectorAngle(1.0, 0.0, (x1dash-cxdash)/mRx, (y1dash-cydash)/mRy);
     471         208 :   double dtheta = CalcVectorAngle((x1dash-cxdash)/mRx, (y1dash-cydash)/mRy,
     472         312 :                                   (-x1dash-cxdash)/mRx, (-y1dash-cydash)/mRy);
     473         104 :   if (!sweepFlag && dtheta>0)
     474          64 :     dtheta -= 2.0*M_PI;
     475          40 :   else if (sweepFlag && dtheta<0)
     476           0 :     dtheta += 2.0*M_PI;
     477             : 
     478             :   // Convert into cubic bezier segments <= 90deg
     479         104 :   mNumSegs = static_cast<int>(ceil(fabs(dtheta/(M_PI/2.0))));
     480         104 :   mDelta = dtheta/mNumSegs;
     481         104 :   mT = 8.0/3.0 * sin(mDelta/4.0) * sin(mDelta/4.0) / sin(mDelta/2.0);
     482             : 
     483         104 :   mFrom = from;
     484             : }
     485             : 
     486             : bool
     487         252 : nsSVGArcConverter::GetNextSegment(Point* cp1, Point* cp2, Point* to)
     488             : {
     489         252 :   if (mSegIndex == mNumSegs) {
     490         104 :     return false;
     491             :   }
     492             : 
     493         148 :   double cosTheta1 = cos(mTheta);
     494         148 :   double sinTheta1 = sin(mTheta);
     495         148 :   double theta2 = mTheta + mDelta;
     496         148 :   double cosTheta2 = cos(theta2);
     497         148 :   double sinTheta2 = sin(theta2);
     498             : 
     499             :   // a) calculate endpoint of the segment:
     500         148 :   to->x = mCosPhi * mRx*cosTheta2 - mSinPhi * mRy*sinTheta2 + mC.x;
     501         148 :   to->y = mSinPhi * mRx*cosTheta2 + mCosPhi * mRy*sinTheta2 + mC.y;
     502             : 
     503             :   // b) calculate gradients at start/end points of segment:
     504         148 :   cp1->x = mFrom.x + mT * ( - mCosPhi * mRx*sinTheta1 - mSinPhi * mRy*cosTheta1);
     505         148 :   cp1->y = mFrom.y + mT * ( - mSinPhi * mRx*sinTheta1 + mCosPhi * mRy*cosTheta1);
     506             : 
     507         148 :   cp2->x = to->x + mT * ( mCosPhi * mRx*sinTheta2 + mSinPhi * mRy*cosTheta2);
     508         148 :   cp2->y = to->y + mT * ( mSinPhi * mRx*sinTheta2 - mCosPhi * mRy*cosTheta2);
     509             : 
     510             :   // do next segment
     511         148 :   mTheta = theta2;
     512         148 :   mFrom = *to;
     513         148 :   ++mSegIndex;
     514             : 
     515         148 :   return true;
     516             : }

Generated by: LCOV version 1.13