Coverage Report

Created: 2018-09-25 14:53

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