LCOV - code coverage report
Current view: top level - dom/svg - SVGPathSegUtils.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 246 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 27 0.0 %
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 "mozilla/ArrayUtils.h" // MOZ_ARRAY_LENGTH
       8             : 
       9             : #include "SVGPathSegUtils.h"
      10             : 
      11             : #include "gfx2DGlue.h"
      12             : #include "nsSVGPathDataParser.h"
      13             : #include "nsTextFormatter.h"
      14             : 
      15             : using namespace mozilla;
      16             : using namespace mozilla::gfx;
      17             : 
      18             : static const float PATH_SEG_LENGTH_TOLERANCE = 0.0000001f;
      19             : static const uint32_t MAX_RECURSION = 10;
      20             : 
      21             : 
      22             : /* static */ void
      23           0 : SVGPathSegUtils::GetValueAsString(const float* aSeg, nsAString& aValue)
      24             : {
      25             :   // Adding new seg type? Is the formatting below acceptable for the new types?
      26             :   static_assert(NS_SVG_PATH_SEG_LAST_VALID_TYPE ==
      27             :                 PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL,
      28             :                 "Update GetValueAsString for the new value.");
      29             :   static_assert(NS_SVG_PATH_SEG_MAX_ARGS == 7,
      30             :                 "Add another case to the switch below.");
      31             : 
      32           0 :   uint32_t type = DecodeType(aSeg[0]);
      33           0 :   char16_t typeAsChar = GetPathSegTypeAsLetter(type);
      34             : 
      35             :   // Special case arcs:
      36           0 :   if (IsArcType(type)) {
      37           0 :     bool largeArcFlag = aSeg[4] != 0.0f;
      38           0 :     bool sweepFlag = aSeg[5] != 0.0f;
      39           0 :     nsTextFormatter::ssprintf(aValue,
      40             :                               u"%c%g,%g %g %d,%d %g,%g",
      41           0 :                               typeAsChar, aSeg[1], aSeg[2], aSeg[3],
      42           0 :                               largeArcFlag, sweepFlag, aSeg[6], aSeg[7]);
      43             :   } else {
      44             : 
      45           0 :     switch (ArgCountForType(type)) {
      46             :     case 0:
      47           0 :       aValue = typeAsChar;
      48           0 :       break;
      49             : 
      50             :     case 1:
      51           0 :       nsTextFormatter::ssprintf(aValue, u"%c%g",
      52           0 :                                 typeAsChar, aSeg[1]);
      53           0 :       break;
      54             : 
      55             :     case 2:
      56           0 :       nsTextFormatter::ssprintf(aValue, u"%c%g,%g",
      57           0 :                                 typeAsChar, aSeg[1], aSeg[2]);
      58           0 :       break;
      59             : 
      60             :     case 4:
      61           0 :       nsTextFormatter::ssprintf(aValue, u"%c%g,%g %g,%g",
      62           0 :                                 typeAsChar, aSeg[1], aSeg[2], aSeg[3], aSeg[4]);
      63           0 :       break;
      64             : 
      65             :     case 6:
      66           0 :       nsTextFormatter::ssprintf(aValue,
      67             :                                 u"%c%g,%g %g,%g %g,%g",
      68           0 :                                 typeAsChar, aSeg[1], aSeg[2], aSeg[3], aSeg[4],
      69           0 :                                 aSeg[5], aSeg[6]);
      70           0 :       break;
      71             : 
      72             :     default:
      73           0 :       MOZ_ASSERT(false, "Unknown segment type");
      74             :       aValue = u"<unknown-segment-type>";
      75             :       return;
      76             :     }
      77             :   }
      78             : 
      79             :   // nsTextFormatter::ssprintf is one of the nsTextFormatter methods that
      80             :   // randomly appends '\0' to its output string, which means that the length
      81             :   // of the output string is one too long. We need to manually remove that '\0'
      82             :   // until nsTextFormatter is fixed.
      83             :   //
      84           0 :   if (aValue[aValue.Length() - 1] == char16_t('\0')) {
      85           0 :     aValue.SetLength(aValue.Length() - 1);
      86             :   }
      87             : }
      88             : 
      89             : 
      90             : static float
      91           0 : CalcDistanceBetweenPoints(const Point& aP1, const Point& aP2)
      92             : {
      93           0 :   return NS_hypot(aP2.x - aP1.x, aP2.y - aP1.y);
      94             : }
      95             : 
      96             : 
      97             : static void
      98           0 : SplitQuadraticBezier(const Point* aCurve, Point* aLeft, Point* aRight)
      99             : {
     100           0 :   aLeft[0].x = aCurve[0].x;
     101           0 :   aLeft[0].y = aCurve[0].y;
     102           0 :   aRight[2].x = aCurve[2].x;
     103           0 :   aRight[2].y = aCurve[2].y;
     104           0 :   aLeft[1].x = (aCurve[0].x + aCurve[1].x) / 2;
     105           0 :   aLeft[1].y = (aCurve[0].y + aCurve[1].y) / 2;
     106           0 :   aRight[1].x = (aCurve[1].x + aCurve[2].x) / 2;
     107           0 :   aRight[1].y = (aCurve[1].y + aCurve[2].y) / 2;
     108           0 :   aLeft[2].x = aRight[0].x = (aLeft[1].x + aRight[1].x) / 2;
     109           0 :   aLeft[2].y = aRight[0].y = (aLeft[1].y + aRight[1].y) / 2;
     110           0 : }
     111             : 
     112             : static void
     113           0 : SplitCubicBezier(const Point* aCurve, Point* aLeft, Point* aRight)
     114             : {
     115           0 :   Point tmp;
     116           0 :   tmp.x = (aCurve[1].x + aCurve[2].x) / 4;
     117           0 :   tmp.y = (aCurve[1].y + aCurve[2].y) / 4;
     118           0 :   aLeft[0].x = aCurve[0].x;
     119           0 :   aLeft[0].y = aCurve[0].y;
     120           0 :   aRight[3].x = aCurve[3].x;
     121           0 :   aRight[3].y = aCurve[3].y;
     122           0 :   aLeft[1].x = (aCurve[0].x + aCurve[1].x) / 2;
     123           0 :   aLeft[1].y = (aCurve[0].y + aCurve[1].y) / 2;
     124           0 :   aRight[2].x = (aCurve[2].x + aCurve[3].x) / 2;
     125           0 :   aRight[2].y = (aCurve[2].y + aCurve[3].y) / 2;
     126           0 :   aLeft[2].x = aLeft[1].x / 2 + tmp.x;
     127           0 :   aLeft[2].y = aLeft[1].y / 2 + tmp.y;
     128           0 :   aRight[1].x = aRight[2].x / 2 + tmp.x;
     129           0 :   aRight[1].y = aRight[2].y / 2 + tmp.y;
     130           0 :   aLeft[3].x = aRight[0].x = (aLeft[2].x + aRight[1].x) / 2;
     131           0 :   aLeft[3].y = aRight[0].y = (aLeft[2].y + aRight[1].y) / 2;
     132           0 : }
     133             : 
     134             : static float
     135           0 : CalcBezLengthHelper(const Point* aCurve, uint32_t aNumPts,
     136             :                     uint32_t aRecursionCount,
     137             :                     void (*aSplit)(const Point*, Point*, Point*))
     138             : {
     139           0 :   Point left[4];
     140           0 :   Point right[4];
     141           0 :   float length = 0, dist;
     142           0 :   for (uint32_t i = 0; i < aNumPts - 1; i++) {
     143           0 :     length += CalcDistanceBetweenPoints(aCurve[i], aCurve[i+1]);
     144             :   }
     145           0 :   dist = CalcDistanceBetweenPoints(aCurve[0], aCurve[aNumPts - 1]);
     146           0 :   if (length - dist > PATH_SEG_LENGTH_TOLERANCE &&
     147             :       aRecursionCount < MAX_RECURSION) {
     148           0 :     aSplit(aCurve, left, right);
     149           0 :     ++aRecursionCount;
     150           0 :     return CalcBezLengthHelper(left, aNumPts, aRecursionCount, aSplit) +
     151           0 :            CalcBezLengthHelper(right, aNumPts, aRecursionCount, aSplit);
     152             :   }
     153           0 :   return length;
     154             : }
     155             : 
     156             : static inline float
     157           0 : CalcLengthOfCubicBezier(const Point& aPos, const Point &aCP1,
     158             :                         const Point& aCP2, const Point &aTo)
     159             : {
     160           0 :   Point curve[4] = { aPos, aCP1, aCP2, aTo };
     161           0 :   return CalcBezLengthHelper(curve, 4, 0, SplitCubicBezier);
     162             : }
     163             : 
     164             : static inline float
     165           0 : CalcLengthOfQuadraticBezier(const Point& aPos, const Point& aCP,
     166             :                             const Point& aTo)
     167             : {
     168           0 :   Point curve[3] = { aPos, aCP, aTo };
     169           0 :   return CalcBezLengthHelper(curve, 3, 0, SplitQuadraticBezier);
     170             : }
     171             : 
     172             : 
     173             : static void
     174           0 : TraverseClosePath(const float* aArgs, SVGPathTraversalState& aState)
     175             : {
     176           0 :   if (aState.ShouldUpdateLengthAndControlPoints()) {
     177           0 :     aState.length += CalcDistanceBetweenPoints(aState.pos, aState.start);
     178           0 :     aState.cp1 = aState.cp2 = aState.start;
     179             :   }
     180           0 :   aState.pos = aState.start;
     181           0 : }
     182             : 
     183             : static void
     184           0 : TraverseMovetoAbs(const float* aArgs, SVGPathTraversalState& aState)
     185             : {
     186           0 :   aState.start = aState.pos = Point(aArgs[0], aArgs[1]);
     187           0 :   if (aState.ShouldUpdateLengthAndControlPoints()) {
     188             :     // aState.length is unchanged, since move commands don't affect path length.
     189           0 :     aState.cp1 = aState.cp2 = aState.start;
     190             :   }
     191           0 : }
     192             : 
     193             : static void
     194           0 : TraverseMovetoRel(const float* aArgs, SVGPathTraversalState& aState)
     195             : {
     196           0 :   aState.start = aState.pos += Point(aArgs[0], aArgs[1]);
     197           0 :   if (aState.ShouldUpdateLengthAndControlPoints()) {
     198             :     // aState.length is unchanged, since move commands don't affect path length.
     199           0 :     aState.cp1 = aState.cp2 = aState.start;
     200             :   }
     201           0 : }
     202             : 
     203             : static void
     204           0 : TraverseLinetoAbs(const float* aArgs, SVGPathTraversalState& aState)
     205             : {
     206           0 :   Point to(aArgs[0], aArgs[1]);
     207           0 :   if (aState.ShouldUpdateLengthAndControlPoints()) {
     208           0 :     aState.length += CalcDistanceBetweenPoints(aState.pos, to);
     209           0 :     aState.cp1 = aState.cp2 = to;
     210             :   }
     211           0 :   aState.pos = to;
     212           0 : }
     213             : 
     214             : static void
     215           0 : TraverseLinetoRel(const float* aArgs, SVGPathTraversalState& aState)
     216             : {
     217           0 :   Point to = aState.pos + Point(aArgs[0], aArgs[1]);
     218           0 :   if (aState.ShouldUpdateLengthAndControlPoints()) {
     219           0 :     aState.length += CalcDistanceBetweenPoints(aState.pos, to);
     220           0 :     aState.cp1 = aState.cp2 = to;
     221             :   }
     222           0 :   aState.pos = to;
     223           0 : }
     224             : 
     225             : static void
     226           0 : TraverseLinetoHorizontalAbs(const float* aArgs, SVGPathTraversalState& aState)
     227             : {
     228           0 :   Point to(aArgs[0], aState.pos.y);
     229           0 :   if (aState.ShouldUpdateLengthAndControlPoints()) {
     230           0 :     aState.length += fabs(to.x - aState.pos.x);
     231           0 :     aState.cp1 = aState.cp2 = to;
     232             :   }
     233           0 :   aState.pos = to;
     234           0 : }
     235             : 
     236             : static void
     237           0 : TraverseLinetoHorizontalRel(const float* aArgs, SVGPathTraversalState& aState)
     238             : {
     239           0 :   aState.pos.x += aArgs[0];
     240           0 :   if (aState.ShouldUpdateLengthAndControlPoints()) {
     241           0 :     aState.length += fabs(aArgs[0]);
     242           0 :     aState.cp1 = aState.cp2 = aState.pos;
     243             :   }
     244           0 : }
     245             : 
     246             : static void
     247           0 : TraverseLinetoVerticalAbs(const float* aArgs, SVGPathTraversalState& aState)
     248             : {
     249           0 :   Point to(aState.pos.x, aArgs[0]);
     250           0 :   if (aState.ShouldUpdateLengthAndControlPoints()) {
     251           0 :     aState.length += fabs(to.y - aState.pos.y);
     252           0 :     aState.cp1 = aState.cp2 = to;
     253             :   }
     254           0 :   aState.pos = to;
     255           0 : }
     256             : 
     257             : static void
     258           0 : TraverseLinetoVerticalRel(const float* aArgs, SVGPathTraversalState& aState)
     259             : {
     260           0 :   aState.pos.y += aArgs[0];
     261           0 :   if (aState.ShouldUpdateLengthAndControlPoints()) {
     262           0 :     aState.length += fabs(aArgs[0]);
     263           0 :     aState.cp1 = aState.cp2 = aState.pos;
     264             :   }
     265           0 : }
     266             : 
     267             : static void
     268           0 : TraverseCurvetoCubicAbs(const float* aArgs, SVGPathTraversalState& aState)
     269             : {
     270           0 :   Point to(aArgs[4], aArgs[5]);
     271           0 :   if (aState.ShouldUpdateLengthAndControlPoints()) {
     272           0 :     Point cp1(aArgs[0], aArgs[1]);
     273           0 :     Point cp2(aArgs[2], aArgs[3]);
     274           0 :     aState.length += (float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to);
     275           0 :     aState.cp2 = cp2;
     276           0 :     aState.cp1 = to;
     277             :   }
     278           0 :   aState.pos = to;
     279           0 : }
     280             : 
     281             : static void
     282           0 : TraverseCurvetoCubicSmoothAbs(const float* aArgs, SVGPathTraversalState& aState)
     283             : {
     284           0 :   Point to(aArgs[2], aArgs[3]);
     285           0 :   if (aState.ShouldUpdateLengthAndControlPoints()) {
     286           0 :     Point cp1 = aState.pos - (aState.cp2 - aState.pos);
     287           0 :     Point cp2(aArgs[0], aArgs[1]);
     288           0 :     aState.length += (float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to);
     289           0 :     aState.cp2 = cp2;
     290           0 :     aState.cp1 = to;
     291             :   }
     292           0 :   aState.pos = to;
     293           0 : }
     294             : 
     295             : static void
     296           0 : TraverseCurvetoCubicRel(const float* aArgs, SVGPathTraversalState& aState)
     297             : {
     298           0 :   Point to = aState.pos + Point(aArgs[4], aArgs[5]);
     299           0 :   if (aState.ShouldUpdateLengthAndControlPoints()) {
     300           0 :     Point cp1 = aState.pos + Point(aArgs[0], aArgs[1]);
     301           0 :     Point cp2 = aState.pos + Point(aArgs[2], aArgs[3]);
     302           0 :     aState.length += (float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to);
     303           0 :     aState.cp2 = cp2;
     304           0 :     aState.cp1 = to;
     305             :   }
     306           0 :   aState.pos = to;
     307           0 : }
     308             : 
     309             : static void
     310           0 : TraverseCurvetoCubicSmoothRel(const float* aArgs, SVGPathTraversalState& aState)
     311             : {
     312           0 :   Point to = aState.pos + Point(aArgs[2], aArgs[3]);
     313           0 :   if (aState.ShouldUpdateLengthAndControlPoints()) {
     314           0 :     Point cp1 = aState.pos - (aState.cp2 - aState.pos);
     315           0 :     Point cp2 = aState.pos + Point(aArgs[0], aArgs[1]);
     316           0 :     aState.length += (float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to);
     317           0 :     aState.cp2 = cp2;
     318           0 :     aState.cp1 = to;
     319             :   }
     320           0 :   aState.pos = to;
     321           0 : }
     322             : 
     323             : static void
     324           0 : TraverseCurvetoQuadraticAbs(const float* aArgs, SVGPathTraversalState& aState)
     325             : {
     326           0 :   Point to(aArgs[2], aArgs[3]);
     327           0 :   if (aState.ShouldUpdateLengthAndControlPoints()) {
     328           0 :     Point cp(aArgs[0], aArgs[1]);
     329           0 :     aState.length += (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to);
     330           0 :     aState.cp1 = cp;
     331           0 :     aState.cp2 = to;
     332             :   }
     333           0 :   aState.pos = to;
     334           0 : }
     335             : 
     336             : static void
     337           0 : TraverseCurvetoQuadraticSmoothAbs(const float* aArgs,
     338             :                                   SVGPathTraversalState& aState)
     339             : {
     340           0 :   Point to(aArgs[0], aArgs[1]);
     341           0 :   if (aState.ShouldUpdateLengthAndControlPoints()) {
     342           0 :     Point cp = aState.pos - (aState.cp1 - aState.pos);
     343           0 :     aState.length += (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to);
     344           0 :     aState.cp1 = cp;
     345           0 :     aState.cp2 = to;
     346             :   }
     347           0 :   aState.pos = to;
     348           0 : }
     349             : 
     350             : static void
     351           0 : TraverseCurvetoQuadraticRel(const float* aArgs, SVGPathTraversalState& aState)
     352             : {
     353           0 :   Point to = aState.pos + Point(aArgs[2], aArgs[3]);
     354           0 :   if (aState.ShouldUpdateLengthAndControlPoints()) {
     355           0 :     Point cp = aState.pos + Point(aArgs[0], aArgs[1]);
     356           0 :     aState.length += (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to);
     357           0 :     aState.cp1 = cp;
     358           0 :     aState.cp2 = to;
     359             :   }
     360           0 :   aState.pos = to;
     361           0 : }
     362             : 
     363             : static void
     364           0 : TraverseCurvetoQuadraticSmoothRel(const float* aArgs,
     365             :                                   SVGPathTraversalState& aState)
     366             : {
     367           0 :   Point to = aState.pos + Point(aArgs[0], aArgs[1]);
     368           0 :   if (aState.ShouldUpdateLengthAndControlPoints()) {
     369           0 :     Point cp = aState.pos - (aState.cp1 - aState.pos);
     370           0 :     aState.length += (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to);
     371           0 :     aState.cp1 = cp;
     372           0 :     aState.cp2 = to;
     373             :   }
     374           0 :   aState.pos = to;
     375           0 : }
     376             : 
     377             : static void
     378           0 : TraverseArcAbs(const float* aArgs, SVGPathTraversalState& aState)
     379             : {
     380           0 :   Point to(aArgs[5], aArgs[6]);
     381           0 :   if (aState.ShouldUpdateLengthAndControlPoints()) {
     382           0 :     float dist = 0;
     383           0 :     Point radii(aArgs[0], aArgs[1]);
     384           0 :     Point bez[4] = { aState.pos, Point(0, 0), Point(0, 0), Point(0, 0) };
     385           0 :     nsSVGArcConverter converter(aState.pos, to, radii, aArgs[2],
     386           0 :                                 aArgs[3] != 0, aArgs[4] != 0);
     387           0 :     while (converter.GetNextSegment(&bez[1], &bez[2], &bez[3])) {
     388           0 :       dist += CalcBezLengthHelper(bez, 4, 0, SplitCubicBezier);
     389           0 :       bez[0] = bez[3];
     390             :     }
     391           0 :     aState.length += dist;
     392           0 :     aState.cp1 = aState.cp2 = to;
     393             :   }
     394           0 :   aState.pos = to;
     395           0 : }
     396             : 
     397             : static void
     398           0 : TraverseArcRel(const float* aArgs, SVGPathTraversalState& aState)
     399             : {
     400           0 :   Point to = aState.pos + Point(aArgs[5], aArgs[6]);
     401           0 :   if (aState.ShouldUpdateLengthAndControlPoints()) {
     402           0 :     float dist = 0;
     403           0 :     Point radii(aArgs[0], aArgs[1]);
     404           0 :     Point bez[4] = { aState.pos, Point(0, 0), Point(0, 0), Point(0, 0) };
     405           0 :     nsSVGArcConverter converter(aState.pos, to, radii, aArgs[2],
     406           0 :                                 aArgs[3] != 0, aArgs[4] != 0);
     407           0 :     while (converter.GetNextSegment(&bez[1], &bez[2], &bez[3])) {
     408           0 :       dist += CalcBezLengthHelper(bez, 4, 0, SplitCubicBezier);
     409           0 :       bez[0] = bez[3];
     410             :     }
     411           0 :     aState.length += dist;
     412           0 :     aState.cp1 = aState.cp2 = to;
     413             :   }
     414           0 :   aState.pos = to;
     415           0 : }
     416             : 
     417             : 
     418             : typedef void (*TraverseFunc)(const float*, SVGPathTraversalState&);
     419             : 
     420             : static TraverseFunc gTraverseFuncTable[NS_SVG_PATH_SEG_TYPE_COUNT] = {
     421             :   nullptr, //  0 == PATHSEG_UNKNOWN
     422             :   TraverseClosePath,
     423             :   TraverseMovetoAbs,
     424             :   TraverseMovetoRel,
     425             :   TraverseLinetoAbs,
     426             :   TraverseLinetoRel,
     427             :   TraverseCurvetoCubicAbs,
     428             :   TraverseCurvetoCubicRel,
     429             :   TraverseCurvetoQuadraticAbs,
     430             :   TraverseCurvetoQuadraticRel,
     431             :   TraverseArcAbs,
     432             :   TraverseArcRel,
     433             :   TraverseLinetoHorizontalAbs,
     434             :   TraverseLinetoHorizontalRel,
     435             :   TraverseLinetoVerticalAbs,
     436             :   TraverseLinetoVerticalRel,
     437             :   TraverseCurvetoCubicSmoothAbs,
     438             :   TraverseCurvetoCubicSmoothRel,
     439             :   TraverseCurvetoQuadraticSmoothAbs,
     440             :   TraverseCurvetoQuadraticSmoothRel
     441             : };
     442             : 
     443             : /* static */ void
     444           0 : SVGPathSegUtils::TraversePathSegment(const float* aData,
     445             :                                      SVGPathTraversalState& aState)
     446             : {
     447             :   static_assert(MOZ_ARRAY_LENGTH(gTraverseFuncTable) ==
     448             :                 NS_SVG_PATH_SEG_TYPE_COUNT,
     449             :                 "gTraverseFuncTable is out of date");
     450           0 :   uint32_t type = DecodeType(aData[0]);
     451           0 :   gTraverseFuncTable[type](aData + 1, aState);
     452           0 : }

Generated by: LCOV version 1.13