LCOV - code coverage report
Current view: top level - dom/svg - SVGPathData.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 139 416 33.4 %
Date: 2017-07-14 16:53:18 Functions: 6 18 33.3 %
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 "SVGPathData.h"
       8             : 
       9             : #include "gfx2DGlue.h"
      10             : #include "gfxPlatform.h"
      11             : #include "mozilla/gfx/2D.h"
      12             : #include "mozilla/gfx/Types.h"
      13             : #include "mozilla/gfx/Point.h"
      14             : #include "mozilla/RefPtr.h"
      15             : #include "nsError.h"
      16             : #include "nsString.h"
      17             : #include "nsSVGPathDataParser.h"
      18             : #include <stdarg.h>
      19             : #include "nsStyleConsts.h"
      20             : #include "SVGContentUtils.h"
      21             : #include "SVGGeometryElement.h" // for nsSVGMark
      22             : #include "SVGPathSegUtils.h"
      23             : #include <algorithm>
      24             : 
      25             : using namespace mozilla;
      26             : using namespace mozilla::gfx;
      27             : 
      28          28 : static bool IsMoveto(uint16_t aSegType)
      29             : {
      30          28 :   return aSegType == PATHSEG_MOVETO_ABS ||
      31          28 :          aSegType == PATHSEG_MOVETO_REL;
      32             : }
      33             : 
      34             : nsresult
      35          44 : SVGPathData::CopyFrom(const SVGPathData& rhs)
      36             : {
      37          44 :   if (!mData.Assign(rhs.mData, fallible)) {
      38           0 :     return NS_ERROR_OUT_OF_MEMORY;
      39             :   }
      40          44 :   return NS_OK;
      41             : }
      42             : 
      43             : void
      44           0 : SVGPathData::GetValueAsString(nsAString& aValue) const
      45             : {
      46             :   // we need this function in DidChangePathSegList
      47           0 :   aValue.Truncate();
      48           0 :   if (!Length()) {
      49           0 :     return;
      50             :   }
      51           0 :   uint32_t i = 0;
      52             :   for (;;) {
      53           0 :     nsAutoString segAsString;
      54           0 :     SVGPathSegUtils::GetValueAsString(&mData[i], segAsString);
      55             :     // We ignore OOM, since it's not useful for us to return an error.
      56           0 :     aValue.Append(segAsString);
      57           0 :     i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]);
      58           0 :     if (i >= mData.Length()) {
      59           0 :       MOZ_ASSERT(i == mData.Length(), "Very, very bad - mData corrupt");
      60           0 :       return;
      61             :     }
      62           0 :     aValue.Append(' ');
      63           0 :   }
      64             : }
      65             : 
      66             : nsresult
      67          44 : SVGPathData::SetValueFromString(const nsAString& aValue)
      68             : {
      69             :   // We don't use a temp variable since the spec says to parse everything up to
      70             :   // the first error. We still return any error though so that callers know if
      71             :   // there's a problem.
      72             : 
      73          44 :   nsSVGPathDataParser pathParser(aValue, this);
      74          44 :   return pathParser.Parse() ? NS_OK : NS_ERROR_DOM_SYNTAX_ERR;
      75             : }
      76             : 
      77             : nsresult
      78         894 : SVGPathData::AppendSeg(uint32_t aType, ...)
      79             : {
      80         894 :   uint32_t oldLength = mData.Length();
      81         894 :   uint32_t newLength = oldLength + 1 + SVGPathSegUtils::ArgCountForType(aType);
      82         894 :   if (!mData.SetLength(newLength, fallible)) {
      83           0 :     return NS_ERROR_OUT_OF_MEMORY;
      84             :   }
      85             : 
      86         894 :   mData[oldLength] = SVGPathSegUtils::EncodeType(aType);
      87             :   va_list args;
      88         894 :   va_start(args, aType);
      89        4096 :   for (uint32_t i = oldLength + 1; i < newLength; ++i) {
      90             :     // NOTE! 'float' is promoted to 'double' when passed through '...'!
      91        3202 :     mData[i] = float(va_arg(args, double));
      92             :   }
      93         894 :   va_end(args);
      94         894 :   return NS_OK;
      95             : }
      96             : 
      97             : float
      98           0 : SVGPathData::GetPathLength() const
      99             : {
     100           0 :   SVGPathTraversalState state;
     101             : 
     102           0 :   uint32_t i = 0;
     103           0 :   while (i < mData.Length()) {
     104           0 :     SVGPathSegUtils::TraversePathSegment(&mData[i], state);
     105           0 :     i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]);
     106             :   }
     107             : 
     108           0 :   MOZ_ASSERT(i == mData.Length(), "Very, very bad - mData corrupt");
     109             : 
     110           0 :   return state.length;
     111             : }
     112             : 
     113             : #ifdef DEBUG
     114             : uint32_t
     115           0 : SVGPathData::CountItems() const
     116             : {
     117           0 :   uint32_t i = 0, count = 0;
     118             : 
     119           0 :   while (i < mData.Length()) {
     120           0 :     i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]);
     121           0 :     count++;
     122             :   }
     123             : 
     124           0 :   MOZ_ASSERT(i == mData.Length(), "Very, very bad - mData corrupt");
     125             : 
     126           0 :   return count;
     127             : }
     128             : #endif
     129             : 
     130             : bool
     131           0 : SVGPathData::GetSegmentLengths(nsTArray<double> *aLengths) const
     132             : {
     133           0 :   aLengths->Clear();
     134           0 :   SVGPathTraversalState state;
     135             : 
     136           0 :   uint32_t i = 0;
     137           0 :   while (i < mData.Length()) {
     138           0 :     state.length = 0.0;
     139           0 :     SVGPathSegUtils::TraversePathSegment(&mData[i], state);
     140           0 :     if (!aLengths->AppendElement(state.length)) {
     141           0 :       aLengths->Clear();
     142           0 :       return false;
     143             :     }
     144           0 :     i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]);
     145             :   }
     146             : 
     147           0 :   MOZ_ASSERT(i == mData.Length(), "Very, very bad - mData corrupt");
     148             : 
     149           0 :   return true;
     150             : }
     151             : 
     152             : bool
     153           0 : SVGPathData::GetDistancesFromOriginToEndsOfVisibleSegments(FallibleTArray<double> *aOutput) const
     154             : {
     155           0 :   SVGPathTraversalState state;
     156             : 
     157           0 :   aOutput->Clear();
     158             : 
     159           0 :   uint32_t i = 0;
     160           0 :   while (i < mData.Length()) {
     161           0 :     uint32_t segType = SVGPathSegUtils::DecodeType(mData[i]);
     162           0 :     SVGPathSegUtils::TraversePathSegment(&mData[i], state);
     163             : 
     164             :     // We skip all moveto commands except an initial moveto. See the text 'A
     165             :     // "move to" command does not count as an additional point when dividing up
     166             :     // the duration...':
     167             :     //
     168             :     // http://www.w3.org/TR/SVG11/animate.html#AnimateMotionElement
     169             :     //
     170             :     // This is important in the non-default case of calcMode="linear". In
     171             :     // this case an equal amount of time is spent on each path segment,
     172             :     // except on moveto segments which are jumped over immediately.
     173             : 
     174           0 :     if (i == 0 || (segType != PATHSEG_MOVETO_ABS &&
     175             :                    segType != PATHSEG_MOVETO_REL)) {
     176           0 :       if (!aOutput->AppendElement(state.length, fallible)) {
     177           0 :         return false;
     178             :       }
     179             :     }
     180           0 :     i += 1 + SVGPathSegUtils::ArgCountForType(segType);
     181             :   }
     182             : 
     183           0 :   MOZ_ASSERT(i == mData.Length(), "Very, very bad - mData corrupt?");
     184             : 
     185           0 :   return true;
     186             : }
     187             : 
     188             : uint32_t
     189           0 : SVGPathData::GetPathSegAtLength(float aDistance) const
     190             : {
     191             :   // TODO [SVGWG issue] get specified what happen if 'aDistance' < 0, or
     192             :   // 'aDistance' > the length of the path, or the seg list is empty.
     193             :   // Return -1? Throwing would better help authors avoid tricky bugs (DOM
     194             :   // could do that if we return -1).
     195             : 
     196           0 :   uint32_t i = 0, segIndex = 0;
     197           0 :   SVGPathTraversalState state;
     198             : 
     199           0 :   while (i < mData.Length()) {
     200           0 :     SVGPathSegUtils::TraversePathSegment(&mData[i], state);
     201           0 :     if (state.length >= aDistance) {
     202           0 :       return segIndex;
     203             :     }
     204           0 :     i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]);
     205           0 :     segIndex++;
     206             :   }
     207             : 
     208           0 :   MOZ_ASSERT(i == mData.Length(), "Very, very bad - mData corrupt");
     209             : 
     210           0 :   return std::max(1U, segIndex) - 1; // -1 because while loop takes us 1 too far
     211             : }
     212             : 
     213             : /**
     214             :  * The SVG spec says we have to paint stroke caps for zero length subpaths:
     215             :  *
     216             :  *   http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
     217             :  *
     218             :  * Cairo only does this for |stroke-linecap: round| and not for
     219             :  * |stroke-linecap: square| (since that's what Adobe Acrobat has always done).
     220             :  * Most likely the other backends that DrawTarget uses have the same behavior.
     221             :  *
     222             :  * To help us conform to the SVG spec we have this helper function to draw an
     223             :  * approximation of square caps for zero length subpaths. It does this by
     224             :  * inserting a subpath containing a single user space axis aligned straight
     225             :  * line that is as small as it can be while minimizing the risk of it being
     226             :  * thrown away by the DrawTarget's backend for being too small to affect
     227             :  * rendering. The idea is that we'll then get stroke caps drawn for this axis
     228             :  * aligned line, creating an axis aligned rectangle that approximates the
     229             :  * square that would ideally be drawn.
     230             :  *
     231             :  * Since we don't have any information about transforms from user space to
     232             :  * device space, we choose the length of the small line that we insert by
     233             :  * making it a small percentage of the stroke width of the path. This should
     234             :  * hopefully allow us to make the line as long as possible (to avoid rounding
     235             :  * issues in the backend resulting in the backend seeing it as having zero
     236             :  * length) while still avoiding the small rectangle being noticably different
     237             :  * from a square.
     238             :  *
     239             :  * Note that this function inserts a subpath into the current gfx path that
     240             :  * will be present during both fill and stroke operations.
     241             :  */
     242             : static void
     243           0 : ApproximateZeroLengthSubpathSquareCaps(PathBuilder* aPB,
     244             :                                        const Point& aPoint,
     245             :                                        Float aStrokeWidth)
     246             : {
     247             :   // Note that caps are proportional to stroke width, so if stroke width is
     248             :   // zero it's actually fine for |tinyLength| below to end up being zero.
     249             :   // However, it would be a waste to inserting a LineTo in that case, so better
     250             :   // not to.
     251           0 :   MOZ_ASSERT(aStrokeWidth > 0.0f,
     252             :              "Make the caller check for this, or check it here");
     253             : 
     254             :   // The fraction of the stroke width that we choose for the length of the
     255             :   // line is rather arbitrary, other than being chosen to meet the requirements
     256             :   // described in the comment above.
     257             : 
     258           0 :   Float tinyLength = aStrokeWidth / SVG_ZERO_LENGTH_PATH_FIX_FACTOR;
     259             : 
     260           0 :   aPB->LineTo(aPoint + Point(tinyLength, 0));
     261           0 :   aPB->MoveTo(aPoint);
     262           0 : }
     263             : 
     264             : #define MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT               \
     265             :   do {                                                                        \
     266             :     if (!subpathHasLength && hasLineCaps && aStrokeWidth > 0 &&               \
     267             :         subpathContainsNonMoveTo &&                                           \
     268             :         SVGPathSegUtils::IsValidType(prevSegType) &&                          \
     269             :         (!IsMoveto(prevSegType) || segType == PATHSEG_CLOSEPATH)) {           \
     270             :       ApproximateZeroLengthSubpathSquareCaps(builder, segStart, aStrokeWidth);\
     271             :     }                                                                         \
     272             :   } while(0)
     273             : 
     274             : already_AddRefed<Path>
     275          28 : SVGPathData::BuildPath(PathBuilder* builder,
     276             :                        uint8_t aStrokeLineCap,
     277             :                        Float aStrokeWidth) const
     278             : {
     279          28 :   if (mData.IsEmpty() || !IsMoveto(SVGPathSegUtils::DecodeType(mData[0]))) {
     280           0 :     return nullptr; // paths without an initial moveto are invalid
     281             :   }
     282             : 
     283          28 :   bool hasLineCaps = aStrokeLineCap != NS_STYLE_STROKE_LINECAP_BUTT;
     284          28 :   bool subpathHasLength = false;  // visual length
     285          28 :   bool subpathContainsNonMoveTo = false;
     286             : 
     287          28 :   uint32_t segType     = PATHSEG_UNKNOWN;
     288          28 :   uint32_t prevSegType = PATHSEG_UNKNOWN;
     289          28 :   Point pathStart(0.0, 0.0); // start point of [sub]path
     290          28 :   Point segStart(0.0, 0.0);
     291          28 :   Point segEnd;
     292          28 :   Point cp1, cp2;            // previous bezier's control points
     293          28 :   Point tcp1, tcp2;          // temporaries
     294             : 
     295             :   // Regarding cp1 and cp2: If the previous segment was a cubic bezier curve,
     296             :   // then cp2 is its second control point. If the previous segment was a
     297             :   // quadratic curve, then cp1 is its (only) control point.
     298             : 
     299          28 :   uint32_t i = 0;
     300        1536 :   while (i < mData.Length()) {
     301         754 :     segType = SVGPathSegUtils::DecodeType(mData[i++]);
     302         754 :     uint32_t argCount = SVGPathSegUtils::ArgCountForType(segType);
     303             : 
     304         754 :     switch (segType)
     305             :     {
     306             :     case PATHSEG_CLOSEPATH:
     307             :       // set this early to allow drawing of square caps for "M{x},{y} Z":
     308          71 :       subpathContainsNonMoveTo = true;
     309          71 :       MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT;
     310          71 :       segEnd = pathStart;
     311          71 :       builder->Close();
     312          71 :       break;
     313             : 
     314             :     case PATHSEG_MOVETO_ABS:
     315          56 :       MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT;
     316          56 :       pathStart = segEnd = Point(mData[i], mData[i+1]);
     317          56 :       builder->MoveTo(segEnd);
     318          56 :       subpathHasLength = false;
     319          56 :       break;
     320             : 
     321             :     case PATHSEG_MOVETO_REL:
     322          16 :       MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT;
     323          16 :       pathStart = segEnd = segStart + Point(mData[i], mData[i+1]);
     324          16 :       builder->MoveTo(segEnd);
     325          16 :       subpathHasLength = false;
     326          16 :       break;
     327             : 
     328             :     case PATHSEG_LINETO_ABS:
     329         153 :       segEnd = Point(mData[i], mData[i+1]);
     330         153 :       if (segEnd != segStart) {
     331         150 :         subpathHasLength = true;
     332         150 :         builder->LineTo(segEnd);
     333             :       }
     334         153 :       break;
     335             : 
     336             :     case PATHSEG_LINETO_REL:
     337          76 :       segEnd = segStart + Point(mData[i], mData[i+1]);
     338          76 :       if (segEnd != segStart) {
     339          75 :         subpathHasLength = true;
     340          75 :         builder->LineTo(segEnd);
     341             :       }
     342          76 :       break;
     343             : 
     344             :     case PATHSEG_CURVETO_CUBIC_ABS:
     345          58 :       cp1 = Point(mData[i], mData[i+1]);
     346          58 :       cp2 = Point(mData[i+2], mData[i+3]);
     347          58 :       segEnd = Point(mData[i+4], mData[i+5]);
     348          58 :       if (segEnd != segStart || segEnd != cp1 || segEnd != cp2) {
     349          55 :         subpathHasLength = true;
     350          55 :         builder->BezierTo(cp1, cp2, segEnd);
     351             :       }
     352          58 :       break;
     353             : 
     354             :     case PATHSEG_CURVETO_CUBIC_REL:
     355         139 :       cp1 = segStart + Point(mData[i], mData[i+1]);
     356         139 :       cp2 = segStart + Point(mData[i+2], mData[i+3]);
     357         139 :       segEnd = segStart + Point(mData[i+4], mData[i+5]);
     358         139 :       if (segEnd != segStart || segEnd != cp1 || segEnd != cp2) {
     359         139 :         subpathHasLength = true;
     360         139 :         builder->BezierTo(cp1, cp2, segEnd);
     361             :       }
     362         139 :       break;
     363             : 
     364             :     case PATHSEG_CURVETO_QUADRATIC_ABS:
     365           0 :       cp1 = Point(mData[i], mData[i+1]);
     366             :       // Convert quadratic curve to cubic curve:
     367           0 :       tcp1 = segStart + (cp1 - segStart) * 2 / 3;
     368           0 :       segEnd = Point(mData[i+2], mData[i+3]); // set before setting tcp2!
     369           0 :       tcp2 = cp1 + (segEnd - cp1) / 3;
     370           0 :       if (segEnd != segStart || segEnd != cp1) {
     371           0 :         subpathHasLength = true;
     372           0 :         builder->BezierTo(tcp1, tcp2, segEnd);
     373             :       }
     374           0 :       break;
     375             : 
     376             :     case PATHSEG_CURVETO_QUADRATIC_REL:
     377           1 :       cp1 = segStart + Point(mData[i], mData[i+1]);
     378             :       // Convert quadratic curve to cubic curve:
     379           1 :       tcp1 = segStart + (cp1 - segStart) * 2 / 3;
     380           1 :       segEnd = segStart + Point(mData[i+2], mData[i+3]); // set before setting tcp2!
     381           1 :       tcp2 = cp1 + (segEnd - cp1) / 3;
     382           1 :       if (segEnd != segStart || segEnd != cp1) {
     383           1 :         subpathHasLength = true;
     384           1 :         builder->BezierTo(tcp1, tcp2, segEnd);
     385             :       }
     386           1 :       break;
     387             : 
     388             :     case PATHSEG_ARC_ABS:
     389             :     case PATHSEG_ARC_REL:
     390             :     {
     391         104 :       Point radii(mData[i], mData[i+1]);
     392         104 :       segEnd = Point(mData[i+5], mData[i+6]);
     393         104 :       if (segType == PATHSEG_ARC_REL) {
     394          94 :         segEnd += segStart;
     395             :       }
     396         104 :       if (segEnd != segStart) {
     397         104 :         subpathHasLength = true;
     398         104 :         if (radii.x == 0.0f || radii.y == 0.0f) {
     399           0 :           builder->LineTo(segEnd);
     400             :         } else {
     401         104 :           nsSVGArcConverter converter(segStart, segEnd, radii, mData[i+2],
     402         208 :                                       mData[i+3] != 0, mData[i+4] != 0);
     403         400 :           while (converter.GetNextSegment(&cp1, &cp2, &segEnd)) {
     404         148 :             builder->BezierTo(cp1, cp2, segEnd);
     405             :           }
     406             :         }
     407             :       }
     408         104 :       break;
     409             :     }
     410             : 
     411             :     case PATHSEG_LINETO_HORIZONTAL_ABS:
     412          12 :       segEnd = Point(mData[i], segStart.y);
     413          12 :       if (segEnd != segStart) {
     414          12 :         subpathHasLength = true;
     415          12 :         builder->LineTo(segEnd);
     416             :       }
     417          12 :       break;
     418             : 
     419             :     case PATHSEG_LINETO_HORIZONTAL_REL:
     420          17 :       segEnd = segStart + Point(mData[i], 0.0f);
     421          17 :       if (segEnd != segStart) {
     422          15 :         subpathHasLength = true;
     423          15 :         builder->LineTo(segEnd);
     424             :       }
     425          17 :       break;
     426             : 
     427             :     case PATHSEG_LINETO_VERTICAL_ABS:
     428           6 :       segEnd = Point(segStart.x, mData[i]);
     429           6 :       if (segEnd != segStart) {
     430           6 :         subpathHasLength = true;
     431           6 :         builder->LineTo(segEnd);
     432             :       }
     433           6 :       break;
     434             : 
     435             :     case PATHSEG_LINETO_VERTICAL_REL:
     436          16 :       segEnd = segStart + Point(0.0f, mData[i]);
     437          16 :       if (segEnd != segStart) {
     438          16 :         subpathHasLength = true;
     439          16 :         builder->LineTo(segEnd);
     440             :       }
     441          16 :       break;
     442             : 
     443             :     case PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
     444           1 :       cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ? segStart * 2 - cp2 : segStart;
     445           1 :       cp2 = Point(mData[i],   mData[i+1]);
     446           1 :       segEnd = Point(mData[i+2], mData[i+3]);
     447           1 :       if (segEnd != segStart || segEnd != cp1 || segEnd != cp2) {
     448           1 :         subpathHasLength = true;
     449           1 :         builder->BezierTo(cp1, cp2, segEnd);
     450             :       }
     451           1 :       break;
     452             : 
     453             :     case PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
     454          28 :       cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ? segStart * 2 - cp2 : segStart;
     455          28 :       cp2 = segStart + Point(mData[i], mData[i+1]);
     456          28 :       segEnd = segStart + Point(mData[i+2], mData[i+3]);
     457          28 :       if (segEnd != segStart || segEnd != cp1 || segEnd != cp2) {
     458          28 :         subpathHasLength = true;
     459          28 :         builder->BezierTo(cp1, cp2, segEnd);
     460             :       }
     461          28 :       break;
     462             : 
     463             :     case PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
     464           0 :       cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ? segStart * 2 - cp1 : segStart;
     465             :       // Convert quadratic curve to cubic curve:
     466           0 :       tcp1 = segStart + (cp1 - segStart) * 2 / 3;
     467           0 :       segEnd = Point(mData[i], mData[i+1]); // set before setting tcp2!
     468           0 :       tcp2 = cp1 + (segEnd - cp1) / 3;
     469           0 :       if (segEnd != segStart || segEnd != cp1) {
     470           0 :         subpathHasLength = true;
     471           0 :         builder->BezierTo(tcp1, tcp2, segEnd);
     472             :       }
     473           0 :       break;
     474             : 
     475             :     case PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
     476           0 :       cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ? segStart * 2 - cp1 : segStart;
     477             :       // Convert quadratic curve to cubic curve:
     478           0 :       tcp1 = segStart + (cp1 - segStart) * 2 / 3;
     479           0 :       segEnd = segStart + Point(mData[i], mData[i+1]); // changed before setting tcp2!
     480           0 :       tcp2 = cp1 + (segEnd - cp1) / 3;
     481           0 :       if (segEnd != segStart || segEnd != cp1) {
     482           0 :         subpathHasLength = true;
     483           0 :         builder->BezierTo(tcp1, tcp2, segEnd);
     484             :       }
     485           0 :       break;
     486             : 
     487             :     default:
     488           0 :       NS_NOTREACHED("Bad path segment type");
     489           0 :       return nullptr; // according to spec we'd use everything up to the bad seg anyway
     490             :     }
     491             : 
     492         754 :     subpathContainsNonMoveTo = segType != PATHSEG_MOVETO_ABS &&
     493             :                                segType != PATHSEG_MOVETO_REL;
     494         754 :     i += argCount;
     495         754 :     prevSegType = segType;
     496         754 :     segStart = segEnd;
     497             :   }
     498             : 
     499          28 :   MOZ_ASSERT(i == mData.Length(), "Very, very bad - mData corrupt");
     500          28 :   MOZ_ASSERT(prevSegType == segType,
     501             :              "prevSegType should be left at the final segType");
     502             : 
     503          28 :   MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT;
     504             : 
     505          28 :   return builder->Finish();
     506             : }
     507             : 
     508             : already_AddRefed<Path>
     509           0 : SVGPathData::BuildPathForMeasuring() const
     510             : {
     511             :   // Since the path that we return will not be used for painting it doesn't
     512             :   // matter what we pass to CreatePathBuilder as aFillRule. Hawever, we do want
     513             :   // to pass something other than NS_STYLE_STROKE_LINECAP_SQUARE as
     514             :   // aStrokeLineCap to avoid the insertion of extra little lines (by
     515             :   // ApproximateZeroLengthSubpathSquareCaps), in which case the value that we
     516             :   // pass as aStrokeWidth doesn't matter (since it's only used to determine the
     517             :   // length of those extra little lines).
     518             : 
     519             :   RefPtr<DrawTarget> drawTarget =
     520           0 :     gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
     521             :   RefPtr<PathBuilder> builder =
     522           0 :     drawTarget->CreatePathBuilder(FillRule::FILL_WINDING);
     523           0 :   return BuildPath(builder, NS_STYLE_STROKE_LINECAP_BUTT, 0);
     524             : }
     525             : 
     526             : static double
     527           0 : AngleOfVector(const Point& aVector)
     528             : {
     529             :   // C99 says about atan2 "A domain error may occur if both arguments are
     530             :   // zero" and "On a domain error, the function returns an implementation-
     531             :   // defined value". In the case of atan2 the implementation-defined value
     532             :   // seems to commonly be zero, but it could just as easily be a NaN value.
     533             :   // We specifically want zero in this case, hence the check:
     534             : 
     535           0 :   return (aVector != Point(0.0, 0.0)) ? atan2(aVector.y, aVector.x) : 0.0;
     536             : }
     537             : 
     538             : static float
     539           0 : AngleOfVector(const Point& cp1, const Point& cp2)
     540             : {
     541           0 :   return static_cast<float>(AngleOfVector(cp1 - cp2));
     542             : }
     543             : 
     544             : void
     545           0 : SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
     546             : {
     547             :   // This code should assume that ANY type of segment can appear at ANY index.
     548             :   // It should also assume that segments such as M and Z can appear in weird
     549             :   // places, and repeat multiple times consecutively.
     550             : 
     551             :   // info on current [sub]path (reset every M command):
     552           0 :   Point pathStart(0.0, 0.0);
     553           0 :   float pathStartAngle = 0.0f;
     554             : 
     555             :   // info on previous segment:
     556           0 :   uint16_t prevSegType = PATHSEG_UNKNOWN;
     557           0 :   Point prevSegEnd(0.0, 0.0);
     558           0 :   float prevSegEndAngle = 0.0f;
     559           0 :   Point prevCP; // if prev seg was a bezier, this was its last control point
     560             : 
     561           0 :   uint32_t i = 0;
     562           0 :   while (i < mData.Length()) {
     563             : 
     564             :     // info on current segment:
     565             :     uint16_t segType =
     566           0 :       SVGPathSegUtils::DecodeType(mData[i++]); // advances i to args
     567           0 :     Point& segStart = prevSegEnd;
     568           0 :     Point segEnd;
     569             :     float segStartAngle, segEndAngle;
     570             : 
     571           0 :     switch (segType) // to find segStartAngle, segEnd and segEndAngle
     572             :     {
     573             :     case PATHSEG_CLOSEPATH:
     574           0 :       segEnd = pathStart;
     575           0 :       segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart);
     576           0 :       break;
     577             : 
     578             :     case PATHSEG_MOVETO_ABS:
     579             :     case PATHSEG_MOVETO_REL:
     580           0 :       if (segType == PATHSEG_MOVETO_ABS) {
     581           0 :         segEnd = Point(mData[i], mData[i+1]);
     582             :       } else {
     583           0 :         segEnd = segStart + Point(mData[i], mData[i+1]);
     584             :       }
     585           0 :       pathStart = segEnd;
     586             :       // If authors are going to specify multiple consecutive moveto commands
     587             :       // with markers, me might as well make the angle do something useful:
     588           0 :       segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart);
     589           0 :       i += 2;
     590           0 :       break;
     591             : 
     592             :     case PATHSEG_LINETO_ABS:
     593             :     case PATHSEG_LINETO_REL:
     594           0 :       if (segType == PATHSEG_LINETO_ABS) {
     595           0 :         segEnd = Point(mData[i], mData[i+1]);
     596             :       } else {
     597           0 :         segEnd = segStart + Point(mData[i], mData[i+1]);
     598             :       }
     599           0 :       segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart);
     600           0 :       i += 2;
     601           0 :       break;
     602             : 
     603             :     case PATHSEG_CURVETO_CUBIC_ABS:
     604             :     case PATHSEG_CURVETO_CUBIC_REL:
     605             :     {
     606           0 :       Point cp1, cp2; // control points
     607           0 :       if (segType == PATHSEG_CURVETO_CUBIC_ABS) {
     608           0 :         cp1 = Point(mData[i], mData[i+1]);
     609           0 :         cp2 = Point(mData[i+2], mData[i+3]);
     610           0 :         segEnd = Point(mData[i+4], mData[i+5]);
     611             :       } else {
     612           0 :         cp1 = segStart + Point(mData[i], mData[i+1]);
     613           0 :         cp2 = segStart + Point(mData[i+2], mData[i+3]);
     614           0 :         segEnd = segStart + Point(mData[i+4], mData[i+5]);
     615             :       }
     616           0 :       prevCP = cp2;
     617             :       segStartAngle =
     618           0 :         AngleOfVector(cp1 == segStart ? (cp1 == cp2 ? segEnd : cp2) : cp1, segStart);
     619             :       segEndAngle =
     620           0 :         AngleOfVector(segEnd, cp2 == segEnd ? (cp1 == cp2 ? segStart : cp1) : cp2);
     621           0 :       i += 6;
     622           0 :       break;
     623             :     }
     624             : 
     625             :     case PATHSEG_CURVETO_QUADRATIC_ABS:
     626             :     case PATHSEG_CURVETO_QUADRATIC_REL:
     627             :     {
     628           0 :       Point cp1; // control point
     629           0 :       if (segType == PATHSEG_CURVETO_QUADRATIC_ABS) {
     630           0 :         cp1 = Point(mData[i], mData[i+1]);
     631           0 :         segEnd = Point(mData[i+2], mData[i+3]);
     632             :       } else {
     633           0 :         cp1 = segStart + Point(mData[i], mData[i+1]);
     634           0 :         segEnd = segStart + Point(mData[i+2], mData[i+3]);
     635             :       }
     636           0 :       prevCP = cp1;
     637           0 :       segStartAngle = AngleOfVector(cp1 == segStart ? segEnd : cp1, segStart);
     638           0 :       segEndAngle = AngleOfVector(segEnd, cp1 == segEnd ? segStart : cp1);
     639           0 :       i += 4;
     640           0 :       break;
     641             :     }
     642             : 
     643             :     case PATHSEG_ARC_ABS:
     644             :     case PATHSEG_ARC_REL:
     645             :     {
     646           0 :       double rx = mData[i];
     647           0 :       double ry = mData[i+1];
     648           0 :       double angle = mData[i+2];
     649           0 :       bool largeArcFlag = mData[i+3] != 0.0f;
     650           0 :       bool sweepFlag = mData[i+4] != 0.0f;
     651           0 :       if (segType == PATHSEG_ARC_ABS) {
     652           0 :         segEnd = Point(mData[i+5], mData[i+6]);
     653             :       } else {
     654           0 :         segEnd = segStart + Point(mData[i+5], mData[i+6]);
     655             :       }
     656             : 
     657             :       // See section F.6 of SVG 1.1 for details on what we're doing here:
     658             :       // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
     659             : 
     660           0 :       if (segStart == segEnd) {
     661             :         // F.6.2 says "If the endpoints (x1, y1) and (x2, y2) are identical,
     662             :         // then this is equivalent to omitting the elliptical arc segment
     663             :         // entirely." We take that very literally here, not adding a mark, and
     664             :         // not even setting any of the 'prev' variables so that it's as if this
     665             :         // arc had never existed; note the difference this will make e.g. if
     666             :         // the arc is proceeded by a bezier curve and followed by a "smooth"
     667             :         // bezier curve of the same degree!
     668           0 :         i += 7;
     669           0 :         continue;
     670             :       }
     671             : 
     672             :       // Below we have funny interleaving of F.6.6 (Correction of out-of-range
     673             :       // radii) and F.6.5 (Conversion from endpoint to center parameterization)
     674             :       // which is designed to avoid some unnecessary calculations.
     675             : 
     676           0 :       if (rx == 0.0 || ry == 0.0) {
     677             :         // F.6.6 step 1 - straight line or coincidental points
     678           0 :         segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart);
     679           0 :         i += 7;
     680           0 :         break;
     681             :       }
     682           0 :       rx = fabs(rx); // F.6.6.1
     683           0 :       ry = fabs(ry);
     684             : 
     685             :       // F.6.5.1:
     686           0 :       angle = angle * M_PI/180.0;
     687           0 :       double x1p =  cos(angle) * (segStart.x - segEnd.x) / 2.0
     688           0 :                   + sin(angle) * (segStart.y - segEnd.y) / 2.0;
     689           0 :       double y1p = -sin(angle) * (segStart.x - segEnd.x) / 2.0
     690           0 :                   + cos(angle) * (segStart.y - segEnd.y) / 2.0;
     691             : 
     692             :       // This is the root in F.6.5.2 and the numerator under that root:
     693             :       double root;
     694           0 :       double numerator = rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p;
     695             : 
     696           0 :       if (numerator >= 0.0) {
     697           0 :         root = sqrt(numerator/(rx*rx*y1p*y1p + ry*ry*x1p*x1p));
     698           0 :         if (largeArcFlag == sweepFlag)
     699           0 :           root = -root;
     700             :       } else {
     701             :         // F.6.6 step 3 - |numerator < 0.0|. This is equivalent to the result
     702             :         // of F.6.6.2 (lamedh) being greater than one. What we have here is
     703             :         // ellipse radii that are too small for the ellipse to reach between
     704             :         // segStart and segEnd. We scale the radii up uniformly so that the
     705             :         // ellipse is just big enough to fit (i.e. to the point where there is
     706             :         // exactly one solution).
     707             : 
     708           0 :         double lamedh = 1.0 - numerator/(rx*rx*ry*ry); // equiv to eqn F.6.6.2
     709           0 :         double s = sqrt(lamedh);
     710           0 :         rx *= s;  // F.6.6.3
     711           0 :         ry *= s;
     712           0 :         root = 0.0;
     713             :       }
     714             : 
     715           0 :       double cxp =  root * rx * y1p / ry;  // F.6.5.2
     716           0 :       double cyp = -root * ry * x1p / rx;
     717             : 
     718             :       double theta, delta;
     719           0 :       theta = AngleOfVector(Point((x1p-cxp)/rx, (y1p-cyp)/ry));    // F.6.5.5
     720           0 :       delta = AngleOfVector(Point((-x1p-cxp)/rx, (-y1p-cyp)/ry)) - // F.6.5.6
     721             :               theta;
     722           0 :       if (!sweepFlag && delta > 0)
     723           0 :         delta -= 2.0 * M_PI;
     724           0 :       else if (sweepFlag && delta < 0)
     725           0 :         delta += 2.0 * M_PI;
     726             : 
     727             :       double tx1, ty1, tx2, ty2;
     728           0 :       tx1 = -cos(angle)*rx*sin(theta) - sin(angle)*ry*cos(theta);
     729           0 :       ty1 = -sin(angle)*rx*sin(theta) + cos(angle)*ry*cos(theta);
     730           0 :       tx2 = -cos(angle)*rx*sin(theta+delta) - sin(angle)*ry*cos(theta+delta);
     731           0 :       ty2 = -sin(angle)*rx*sin(theta+delta) + cos(angle)*ry*cos(theta+delta);
     732             : 
     733           0 :       if (delta < 0.0f) {
     734           0 :         tx1 = -tx1;
     735           0 :         ty1 = -ty1;
     736           0 :         tx2 = -tx2;
     737           0 :         ty2 = -ty2;
     738             :       }
     739             : 
     740           0 :       segStartAngle = static_cast<float>(atan2(ty1, tx1));
     741           0 :       segEndAngle = static_cast<float>(atan2(ty2, tx2));
     742           0 :       i += 7;
     743           0 :       break;
     744             :     }
     745             : 
     746             :     case PATHSEG_LINETO_HORIZONTAL_ABS:
     747             :     case PATHSEG_LINETO_HORIZONTAL_REL:
     748           0 :       if (segType == PATHSEG_LINETO_HORIZONTAL_ABS) {
     749           0 :         segEnd = Point(mData[i++], segStart.y);
     750             :       } else {
     751           0 :         segEnd = segStart + Point(mData[i++], 0.0f);
     752             :       }
     753           0 :       segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart);
     754           0 :       break;
     755             : 
     756             :     case PATHSEG_LINETO_VERTICAL_ABS:
     757             :     case PATHSEG_LINETO_VERTICAL_REL:
     758           0 :       if (segType == PATHSEG_LINETO_VERTICAL_ABS) {
     759           0 :         segEnd = Point(segStart.x, mData[i++]);
     760             :       } else {
     761           0 :         segEnd = segStart + Point(0.0f, mData[i++]);
     762             :       }
     763           0 :       segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart);
     764           0 :       break;
     765             : 
     766             :     case PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
     767             :     case PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
     768             :     {
     769           0 :       Point cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ?
     770           0 :                        segStart * 2 - prevCP : segStart;
     771           0 :       Point cp2;
     772           0 :       if (segType == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS) {
     773           0 :         cp2 = Point(mData[i], mData[i+1]);
     774           0 :         segEnd = Point(mData[i+2], mData[i+3]);
     775             :       } else {
     776           0 :         cp2 = segStart + Point(mData[i], mData[i+1]);
     777           0 :         segEnd = segStart + Point(mData[i+2], mData[i+3]);
     778             :       }
     779           0 :       prevCP = cp2;
     780             :       segStartAngle =
     781           0 :         AngleOfVector(cp1 == segStart ? (cp1 == cp2 ? segEnd : cp2) : cp1, segStart);
     782             :       segEndAngle =
     783           0 :         AngleOfVector(segEnd, cp2 == segEnd ? (cp1 == cp2 ? segStart : cp1) : cp2);
     784           0 :       i += 4;
     785           0 :       break;
     786             :     }
     787             : 
     788             :     case PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
     789             :     case PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
     790             :     {
     791           0 :       Point cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ?
     792           0 :                        segStart * 2 - prevCP : segStart;
     793           0 :       if (segType == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS) {
     794           0 :         segEnd = Point(mData[i], mData[i+1]);
     795             :       } else {
     796           0 :         segEnd = segStart + Point(mData[i], mData[i+1]);
     797             :       }
     798           0 :       prevCP = cp1;
     799           0 :       segStartAngle = AngleOfVector(cp1 == segStart ? segEnd : cp1, segStart);
     800           0 :       segEndAngle = AngleOfVector(segEnd, cp1 == segEnd ? segStart : cp1);
     801           0 :       i += 2;
     802           0 :       break;
     803             :     }
     804             : 
     805             :     default:
     806             :       // Leave any existing marks in aMarks so we have a visual indication of
     807             :       // when things went wrong.
     808           0 :       MOZ_ASSERT(false, "Unknown segment type - path corruption?");
     809             :       return;
     810             :     }
     811             : 
     812             :     // Set the angle of the mark at the start of this segment:
     813           0 :     if (aMarks->Length()) {
     814           0 :       nsSVGMark &mark = aMarks->LastElement();
     815           0 :       if (!IsMoveto(segType) && IsMoveto(prevSegType)) {
     816             :         // start of new subpath
     817           0 :         pathStartAngle = mark.angle = segStartAngle;
     818           0 :       } else if (IsMoveto(segType) && !IsMoveto(prevSegType)) {
     819             :         // end of a subpath
     820           0 :         if (prevSegType != PATHSEG_CLOSEPATH)
     821           0 :           mark.angle = prevSegEndAngle;
     822             :       } else {
     823           0 :         if (!(segType == PATHSEG_CLOSEPATH &&
     824             :               prevSegType == PATHSEG_CLOSEPATH))
     825           0 :           mark.angle = SVGContentUtils::AngleBisect(prevSegEndAngle, segStartAngle);
     826             :       }
     827             :     }
     828             : 
     829             :     // Add the mark at the end of this segment, and set its position:
     830           0 :     if (!aMarks->AppendElement(nsSVGMark(static_cast<float>(segEnd.x),
     831           0 :                                          static_cast<float>(segEnd.y),
     832             :                                          0.0f,
     833             :                                          nsSVGMark::eMid))) {
     834           0 :       aMarks->Clear(); // OOM, so try to free some
     835           0 :       return;
     836             :     }
     837             : 
     838           0 :     if (segType == PATHSEG_CLOSEPATH &&
     839             :         prevSegType != PATHSEG_CLOSEPATH) {
     840           0 :       aMarks->LastElement().angle =
     841             :         //aMarks->ElementAt(pathStartIndex).angle =
     842           0 :         SVGContentUtils::AngleBisect(segEndAngle, pathStartAngle);
     843             :     }
     844             : 
     845           0 :     prevSegType = segType;
     846           0 :     prevSegEnd = segEnd;
     847           0 :     prevSegEndAngle = segEndAngle;
     848             :   }
     849             : 
     850           0 :   MOZ_ASSERT(i == mData.Length(), "Very, very bad - mData corrupt");
     851             : 
     852           0 :   if (aMarks->Length()) {
     853           0 :     if (prevSegType != PATHSEG_CLOSEPATH) {
     854           0 :       aMarks->LastElement().angle = prevSegEndAngle;
     855             :     }
     856           0 :     aMarks->LastElement().type = nsSVGMark::eEnd;
     857           0 :     aMarks->ElementAt(0).type = nsSVGMark::eStart;
     858             :   }
     859             : }
     860             : 
     861             : size_t
     862          33 : SVGPathData::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
     863             : {
     864          33 :   return mData.ShallowSizeOfExcludingThis(aMallocSizeOf);
     865             : }
     866             : 
     867             : size_t
     868           0 : SVGPathData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
     869             : {
     870           0 :   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
     871             : }
     872             : 

Generated by: LCOV version 1.13