2015-05-03 22:32:37 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
2012-05-21 15:12:37 +04:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2010-11-08 18:07:00 +03:00
|
|
|
|
2020-07-15 02:40:05 +03:00
|
|
|
#ifndef DOM_SVG_SVGPATHSEGUTILS_H_
|
|
|
|
#define DOM_SVG_SVGPATHSEGUTILS_H_
|
2010-11-08 18:07:00 +03:00
|
|
|
|
2014-01-30 22:26:54 +04:00
|
|
|
#include "mozilla/ArrayUtils.h"
|
2018-02-18 18:53:13 +03:00
|
|
|
#include "mozilla/dom/SVGPathSegBinding.h"
|
2013-10-24 16:46:38 +04:00
|
|
|
#include "mozilla/gfx/Point.h"
|
2022-02-21 16:35:44 +03:00
|
|
|
#include "mozilla/gfx/Rect.h"
|
2012-01-26 13:57:21 +04:00
|
|
|
#include "nsDebug.h"
|
2010-11-08 18:07:00 +03:00
|
|
|
|
2012-12-23 08:54:22 +04:00
|
|
|
namespace mozilla {
|
|
|
|
|
2021-06-10 00:17:21 +03:00
|
|
|
struct StylePathCommand;
|
|
|
|
|
2011-04-08 02:17:36 +04:00
|
|
|
#define NS_SVG_PATH_SEG_MAX_ARGS 7
|
2018-06-26 00:20:54 +03:00
|
|
|
#define NS_SVG_PATH_SEG_FIRST_VALID_TYPE \
|
|
|
|
dom::SVGPathSeg_Binding::PATHSEG_CLOSEPATH
|
|
|
|
#define NS_SVG_PATH_SEG_LAST_VALID_TYPE \
|
|
|
|
dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL
|
2011-04-08 02:17:36 +04:00
|
|
|
#define NS_SVG_PATH_SEG_TYPE_COUNT (NS_SVG_PATH_SEG_LAST_VALID_TYPE + 1)
|
2010-11-08 18:07:00 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Code that works with path segments can use an instance of this class to
|
|
|
|
* store/provide information about the start of the current subpath and the
|
|
|
|
* last path segment (if any).
|
|
|
|
*/
|
|
|
|
struct SVGPathTraversalState {
|
2020-07-15 13:37:55 +03:00
|
|
|
using Point = gfx::Point;
|
2013-10-24 16:46:38 +04:00
|
|
|
|
2011-04-08 02:17:36 +04:00
|
|
|
enum TraversalMode { eUpdateAll, eUpdateOnlyStartAndCurrentPos };
|
|
|
|
|
2010-11-08 18:07:00 +03:00
|
|
|
SVGPathTraversalState()
|
|
|
|
: start(0.0, 0.0),
|
|
|
|
pos(0.0, 0.0),
|
|
|
|
cp1(0.0, 0.0),
|
|
|
|
cp2(0.0, 0.0),
|
2011-04-08 02:17:36 +04:00
|
|
|
length(0.0),
|
|
|
|
mode(eUpdateAll) {}
|
2010-11-08 18:07:00 +03:00
|
|
|
|
2011-09-29 10:19:26 +04:00
|
|
|
bool ShouldUpdateLengthAndControlPoints() { return mode == eUpdateAll; }
|
2011-04-08 02:17:36 +04:00
|
|
|
|
2013-10-24 16:46:38 +04:00
|
|
|
Point start; // start point of current sub path (reset each moveto)
|
2010-11-08 18:07:00 +03:00
|
|
|
|
2013-10-24 16:46:38 +04:00
|
|
|
Point pos; // current position (end point of previous segment)
|
2010-11-08 18:07:00 +03:00
|
|
|
|
2013-10-24 16:46:38 +04:00
|
|
|
Point cp1; // quadratic control point - if the previous segment was a
|
|
|
|
// quadratic bezier curve then this is set to the absolute
|
|
|
|
// position of its control point, otherwise its set to pos
|
2010-11-08 18:07:00 +03:00
|
|
|
|
2013-10-24 16:46:38 +04:00
|
|
|
Point cp2; // cubic control point - if the previous segment was a cubic
|
|
|
|
// bezier curve then this is set to the absolute position of
|
|
|
|
// its second control point, otherwise it's set to pos
|
2011-04-08 02:17:36 +04:00
|
|
|
|
|
|
|
float length; // accumulated path length
|
|
|
|
|
|
|
|
TraversalMode mode; // indicates what to track while traversing a path
|
2010-11-08 18:07:00 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This class is just a collection of static methods - it doesn't have any data
|
|
|
|
* members, and it's not possible to create instances of this class. This class
|
|
|
|
* exists purely as a convenient place to gather together a bunch of methods
|
|
|
|
* related to manipulating and answering questions about path segments.
|
|
|
|
* Internally we represent path segments purely as an array of floats. See the
|
|
|
|
* comment documenting SVGPathData for more info on that.
|
|
|
|
*
|
|
|
|
* The DOM wrapper classes for encoded path segments (data contained in
|
|
|
|
* instances of SVGPathData) is DOMSVGPathSeg and its sub-classes. Note that
|
|
|
|
* there are multiple different DOM classes for path segs - one for each of the
|
|
|
|
* 19 SVG 1.1 segment types.
|
|
|
|
*/
|
|
|
|
class SVGPathSegUtils {
|
|
|
|
private:
|
2019-04-10 07:34:35 +03:00
|
|
|
SVGPathSegUtils() = default; // private to prevent instances
|
2010-11-08 18:07:00 +03:00
|
|
|
|
2018-11-30 13:46:48 +03:00
|
|
|
public:
|
2010-11-08 18:07:00 +03:00
|
|
|
static void GetValueAsString(const float* aSeg, nsAString& aValue);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Encode a segment type enum to a float.
|
|
|
|
*
|
|
|
|
* At some point in the future we will likely want to encode other
|
|
|
|
* information into the float, such as whether the command was explicit or
|
|
|
|
* not. For now all this method does is save on int to float runtime
|
2012-08-22 19:56:38 +04:00
|
|
|
* conversion by requiring uint32_t and float to be of the same size so we
|
|
|
|
* can simply do a bitwise uint32_t<->float copy.
|
2010-11-08 18:07:00 +03:00
|
|
|
*/
|
2012-08-22 19:56:38 +04:00
|
|
|
static float EncodeType(uint32_t aType) {
|
2013-10-19 04:34:57 +04:00
|
|
|
static_assert(sizeof(uint32_t) == sizeof(float),
|
|
|
|
"sizeof uint32_t and float must be the same");
|
2015-02-10 01:34:50 +03:00
|
|
|
MOZ_ASSERT(IsValidType(aType), "Seg type not recognized");
|
2010-11-08 18:07:00 +03:00
|
|
|
return *(reinterpret_cast<float*>(&aType));
|
|
|
|
}
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
static uint32_t DecodeType(float aType) {
|
2013-10-19 04:34:57 +04:00
|
|
|
static_assert(sizeof(uint32_t) == sizeof(float),
|
|
|
|
"sizeof uint32_t and float must be the same");
|
2012-08-22 19:56:38 +04:00
|
|
|
uint32_t type = *(reinterpret_cast<uint32_t*>(&aType));
|
2015-02-10 01:34:50 +03:00
|
|
|
MOZ_ASSERT(IsValidType(type), "Seg type not recognized");
|
2010-11-08 18:07:00 +03:00
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
2014-01-04 19:02:17 +04:00
|
|
|
static char16_t GetPathSegTypeAsLetter(uint32_t aType) {
|
2015-02-10 01:34:50 +03:00
|
|
|
MOZ_ASSERT(IsValidType(aType), "Seg type not recognized");
|
2010-11-08 18:07:00 +03:00
|
|
|
|
2014-01-04 19:02:17 +04:00
|
|
|
static const char16_t table[] = {
|
|
|
|
char16_t('x'), // 0 == PATHSEG_UNKNOWN
|
|
|
|
char16_t('z'), // 1 == PATHSEG_CLOSEPATH
|
|
|
|
char16_t('M'), // 2 == PATHSEG_MOVETO_ABS
|
|
|
|
char16_t('m'), // 3 == PATHSEG_MOVETO_REL
|
|
|
|
char16_t('L'), // 4 == PATHSEG_LINETO_ABS
|
|
|
|
char16_t('l'), // 5 == PATHSEG_LINETO_REL
|
|
|
|
char16_t('C'), // 6 == PATHSEG_CURVETO_CUBIC_ABS
|
|
|
|
char16_t('c'), // 7 == PATHSEG_CURVETO_CUBIC_REL
|
|
|
|
char16_t('Q'), // 8 == PATHSEG_CURVETO_QUADRATIC_ABS
|
|
|
|
char16_t('q'), // 9 == PATHSEG_CURVETO_QUADRATIC_REL
|
|
|
|
char16_t('A'), // 10 == PATHSEG_ARC_ABS
|
|
|
|
char16_t('a'), // 11 == PATHSEG_ARC_REL
|
|
|
|
char16_t('H'), // 12 == PATHSEG_LINETO_HORIZONTAL_ABS
|
|
|
|
char16_t('h'), // 13 == PATHSEG_LINETO_HORIZONTAL_REL
|
|
|
|
char16_t('V'), // 14 == PATHSEG_LINETO_VERTICAL_ABS
|
|
|
|
char16_t('v'), // 15 == PATHSEG_LINETO_VERTICAL_REL
|
|
|
|
char16_t('S'), // 16 == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS
|
|
|
|
char16_t('s'), // 17 == PATHSEG_CURVETO_CUBIC_SMOOTH_REL
|
|
|
|
char16_t('T'), // 18 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS
|
|
|
|
char16_t('t') // 19 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL
|
2010-11-08 18:07:00 +03:00
|
|
|
};
|
2014-01-30 22:26:54 +04:00
|
|
|
static_assert(MOZ_ARRAY_LENGTH(table) == NS_SVG_PATH_SEG_TYPE_COUNT,
|
|
|
|
"Unexpected table size");
|
2010-11-08 18:07:00 +03:00
|
|
|
|
|
|
|
return table[aType];
|
|
|
|
}
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
static uint32_t ArgCountForType(uint32_t aType) {
|
2015-02-10 01:34:50 +03:00
|
|
|
MOZ_ASSERT(IsValidType(aType), "Seg type not recognized");
|
2010-11-08 18:07:00 +03:00
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
static const uint8_t table[] = {
|
2010-11-08 18:07:00 +03:00
|
|
|
0, // 0 == PATHSEG_UNKNOWN
|
|
|
|
0, // 1 == PATHSEG_CLOSEPATH
|
|
|
|
2, // 2 == PATHSEG_MOVETO_ABS
|
|
|
|
2, // 3 == PATHSEG_MOVETO_REL
|
|
|
|
2, // 4 == PATHSEG_LINETO_ABS
|
|
|
|
2, // 5 == PATHSEG_LINETO_REL
|
|
|
|
6, // 6 == PATHSEG_CURVETO_CUBIC_ABS
|
|
|
|
6, // 7 == PATHSEG_CURVETO_CUBIC_REL
|
|
|
|
4, // 8 == PATHSEG_CURVETO_QUADRATIC_ABS
|
|
|
|
4, // 9 == PATHSEG_CURVETO_QUADRATIC_REL
|
|
|
|
7, // 10 == PATHSEG_ARC_ABS
|
|
|
|
7, // 11 == PATHSEG_ARC_REL
|
|
|
|
1, // 12 == PATHSEG_LINETO_HORIZONTAL_ABS
|
|
|
|
1, // 13 == PATHSEG_LINETO_HORIZONTAL_REL
|
|
|
|
1, // 14 == PATHSEG_LINETO_VERTICAL_ABS
|
|
|
|
1, // 15 == PATHSEG_LINETO_VERTICAL_REL
|
|
|
|
4, // 16 == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS
|
|
|
|
4, // 17 == PATHSEG_CURVETO_CUBIC_SMOOTH_REL
|
|
|
|
2, // 18 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS
|
|
|
|
2 // 19 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL
|
|
|
|
};
|
2014-01-30 22:26:54 +04:00
|
|
|
static_assert(MOZ_ARRAY_LENGTH(table) == NS_SVG_PATH_SEG_TYPE_COUNT,
|
|
|
|
"Unexpected table size");
|
2010-11-08 18:07:00 +03:00
|
|
|
|
|
|
|
return table[aType];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convenience so that callers can pass a float containing an encoded type
|
|
|
|
* and have it decoded implicitly.
|
|
|
|
*/
|
2012-08-22 19:56:38 +04:00
|
|
|
static uint32_t ArgCountForType(float aType) {
|
2010-11-08 18:07:00 +03:00
|
|
|
return ArgCountForType(DecodeType(aType));
|
|
|
|
}
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
static bool IsValidType(uint32_t aType) {
|
2011-04-08 02:17:36 +04:00
|
|
|
return aType >= NS_SVG_PATH_SEG_FIRST_VALID_TYPE &&
|
|
|
|
aType <= NS_SVG_PATH_SEG_LAST_VALID_TYPE;
|
2010-11-08 18:07:00 +03:00
|
|
|
}
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
static bool IsCubicType(uint32_t aType) {
|
2018-06-26 00:20:54 +03:00
|
|
|
return aType == dom::SVGPathSeg_Binding::PATHSEG_CURVETO_CUBIC_REL ||
|
|
|
|
aType == dom::SVGPathSeg_Binding::PATHSEG_CURVETO_CUBIC_ABS ||
|
|
|
|
aType == dom::SVGPathSeg_Binding::PATHSEG_CURVETO_CUBIC_SMOOTH_REL ||
|
|
|
|
aType == dom::SVGPathSeg_Binding::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS;
|
2010-11-08 18:07:00 +03:00
|
|
|
}
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
static bool IsQuadraticType(uint32_t aType) {
|
2018-06-26 00:20:54 +03:00
|
|
|
return aType == dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_REL ||
|
|
|
|
aType == dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_ABS ||
|
|
|
|
aType ==
|
|
|
|
dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL ||
|
|
|
|
aType ==
|
|
|
|
dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS;
|
2010-11-08 18:07:00 +03:00
|
|
|
}
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
static bool IsArcType(uint32_t aType) {
|
2018-06-26 00:20:54 +03:00
|
|
|
return aType == dom::SVGPathSeg_Binding::PATHSEG_ARC_ABS ||
|
|
|
|
aType == dom::SVGPathSeg_Binding::PATHSEG_ARC_REL;
|
2011-04-08 02:17:36 +04:00
|
|
|
}
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
static bool IsRelativeOrAbsoluteType(uint32_t aType) {
|
2015-02-10 01:34:50 +03:00
|
|
|
MOZ_ASSERT(IsValidType(aType), "Seg type not recognized");
|
2011-04-08 02:17:36 +04:00
|
|
|
|
|
|
|
// When adding a new path segment type, ensure that the returned condition
|
|
|
|
// below is still correct.
|
2018-02-18 18:53:13 +03:00
|
|
|
static_assert(
|
|
|
|
NS_SVG_PATH_SEG_LAST_VALID_TYPE ==
|
2018-06-26 00:20:54 +03:00
|
|
|
dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL,
|
2013-10-19 04:34:57 +04:00
|
|
|
"Unexpected type");
|
2011-04-08 02:17:36 +04:00
|
|
|
|
2018-06-26 00:20:54 +03:00
|
|
|
return aType >= dom::SVGPathSeg_Binding::PATHSEG_MOVETO_ABS;
|
2011-04-08 02:17:36 +04:00
|
|
|
}
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
static bool IsRelativeType(uint32_t aType) {
|
2011-04-08 02:17:36 +04:00
|
|
|
MOZ_ASSERT(IsRelativeOrAbsoluteType(aType),
|
|
|
|
"IsRelativeType called with segment type that does not come in "
|
|
|
|
"relative and absolute forms");
|
|
|
|
|
|
|
|
// When adding a new path segment type, ensure that the returned condition
|
|
|
|
// below is still correct.
|
2018-02-18 18:53:13 +03:00
|
|
|
static_assert(
|
|
|
|
NS_SVG_PATH_SEG_LAST_VALID_TYPE ==
|
2018-06-26 00:20:54 +03:00
|
|
|
dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL,
|
2013-10-19 04:34:57 +04:00
|
|
|
"Unexpected type");
|
2011-04-08 02:17:36 +04:00
|
|
|
|
|
|
|
return aType & 1;
|
|
|
|
}
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
static uint32_t RelativeVersionOfType(uint32_t aType) {
|
2011-04-08 02:17:36 +04:00
|
|
|
MOZ_ASSERT(IsRelativeOrAbsoluteType(aType),
|
|
|
|
"RelativeVersionOfType called with segment type that does not "
|
|
|
|
"come in relative and absolute forms");
|
|
|
|
|
|
|
|
// When adding a new path segment type, ensure that the returned condition
|
|
|
|
// below is still correct.
|
2018-02-18 18:53:13 +03:00
|
|
|
static_assert(
|
|
|
|
NS_SVG_PATH_SEG_LAST_VALID_TYPE ==
|
2018-06-26 00:20:54 +03:00
|
|
|
dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL,
|
2013-10-19 04:34:57 +04:00
|
|
|
"Unexpected type");
|
2011-04-08 02:17:36 +04:00
|
|
|
|
|
|
|
return aType | 1;
|
|
|
|
}
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
static uint32_t SameTypeModuloRelativeness(uint32_t aType1, uint32_t aType2) {
|
2011-04-08 02:17:36 +04:00
|
|
|
if (!IsRelativeOrAbsoluteType(aType1)) {
|
|
|
|
return aType1 == aType2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return RelativeVersionOfType(aType1) == RelativeVersionOfType(aType2);
|
|
|
|
}
|
|
|
|
|
2010-11-08 18:07:00 +03:00
|
|
|
/**
|
2011-04-08 02:17:36 +04:00
|
|
|
* Traverse the given path segment and update the SVGPathTraversalState
|
|
|
|
* object.
|
2010-11-08 18:07:00 +03:00
|
|
|
*/
|
2011-04-08 02:17:36 +04:00
|
|
|
static void TraversePathSegment(const float* aData,
|
|
|
|
SVGPathTraversalState& aState);
|
2021-06-10 00:17:21 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Traverse the given path segment and update the SVGPathTraversalState
|
|
|
|
* object. This is identical to the above one but accepts StylePathCommand.
|
|
|
|
*/
|
|
|
|
static void TraversePathSegment(const StylePathCommand& aCommand,
|
|
|
|
SVGPathTraversalState& aState);
|
2010-11-08 18:07:00 +03:00
|
|
|
};
|
|
|
|
|
2022-02-21 16:35:44 +03:00
|
|
|
/// Detect whether the path represents a rectangle (for both filling AND
|
|
|
|
/// stroking) and if so returns it.
|
|
|
|
///
|
|
|
|
/// This is typically useful for google slides which has many of these rectangle
|
|
|
|
/// shaped paths. It handles the same scenarios as skia's
|
|
|
|
/// SkPathPriv::IsRectContour which it is inspried from, including zero-length
|
|
|
|
/// edges and multiple points on edges of the rectangle, and doesn't attempt to
|
|
|
|
/// detect flat curves (that could easily be added but the expectation is that
|
|
|
|
/// since skia doesn't fast path it we're not likely to run into it in
|
|
|
|
/// practice).
|
|
|
|
///
|
|
|
|
/// We could implement something similar for polygons.
|
|
|
|
Maybe<gfx::Rect> SVGPathToAxisAlignedRect(Span<const StylePathCommand> aPath);
|
|
|
|
|
2010-11-08 18:07:00 +03:00
|
|
|
} // namespace mozilla
|
|
|
|
|
2020-07-15 02:40:05 +03:00
|
|
|
#endif // DOM_SVG_SVGPATHSEGUTILS_H_
|