LCOV - code coverage report
Current view: directory - content/svg/content/src - SVGPathData.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 434 0 0.0 %
Date: 2012-06-02 Functions: 16 0 0.0 %

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

Generated by: LCOV version 1.7