diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /dom/svg/SVGPathSegUtils.h | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/svg/SVGPathSegUtils.h')
-rw-r--r-- | dom/svg/SVGPathSegUtils.h | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/dom/svg/SVGPathSegUtils.h b/dom/svg/SVGPathSegUtils.h new file mode 100644 index 0000000000..0f16c066a9 --- /dev/null +++ b/dom/svg/SVGPathSegUtils.h @@ -0,0 +1,286 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef DOM_SVG_SVGPATHSEGUTILS_H_ +#define DOM_SVG_SVGPATHSEGUTILS_H_ + +#include "mozilla/ArrayUtils.h" +#include "mozilla/dom/SVGPathSegBinding.h" +#include "mozilla/gfx/Point.h" +#include "mozilla/gfx/Rect.h" +#include "nsDebug.h" + +namespace mozilla { + +struct StylePathCommand; + +#define NS_SVG_PATH_SEG_MAX_ARGS 7 +#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 +#define NS_SVG_PATH_SEG_TYPE_COUNT (NS_SVG_PATH_SEG_LAST_VALID_TYPE + 1) + +/** + * 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 { + using Point = gfx::Point; + + enum TraversalMode { eUpdateAll, eUpdateOnlyStartAndCurrentPos }; + + SVGPathTraversalState() + : start(0.0, 0.0), + pos(0.0, 0.0), + cp1(0.0, 0.0), + cp2(0.0, 0.0), + length(0.0), + mode(eUpdateAll) {} + + bool ShouldUpdateLengthAndControlPoints() { return mode == eUpdateAll; } + + Point start; // start point of current sub path (reset each moveto) + + Point pos; // current position (end point of previous segment) + + 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 + + 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 + + float length; // accumulated path length + + TraversalMode mode; // indicates what to track while traversing a path +}; + +/** + * 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: + SVGPathSegUtils() = default; // private to prevent instances + + public: + 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 + * conversion by requiring uint32_t and float to be of the same size so we + * can simply do a bitwise uint32_t<->float copy. + */ + static float EncodeType(uint32_t aType) { + static_assert(sizeof(uint32_t) == sizeof(float), + "sizeof uint32_t and float must be the same"); + MOZ_ASSERT(IsValidType(aType), "Seg type not recognized"); + return *(reinterpret_cast<float*>(&aType)); + } + + static uint32_t DecodeType(float aType) { + static_assert(sizeof(uint32_t) == sizeof(float), + "sizeof uint32_t and float must be the same"); + uint32_t type = *(reinterpret_cast<uint32_t*>(&aType)); + MOZ_ASSERT(IsValidType(type), "Seg type not recognized"); + return type; + } + + static char16_t GetPathSegTypeAsLetter(uint32_t aType) { + MOZ_ASSERT(IsValidType(aType), "Seg type not recognized"); + + 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 + }; + static_assert(MOZ_ARRAY_LENGTH(table) == NS_SVG_PATH_SEG_TYPE_COUNT, + "Unexpected table size"); + + return table[aType]; + } + + static uint32_t ArgCountForType(uint32_t aType) { + MOZ_ASSERT(IsValidType(aType), "Seg type not recognized"); + + static const uint8_t table[] = { + 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 + }; + static_assert(MOZ_ARRAY_LENGTH(table) == NS_SVG_PATH_SEG_TYPE_COUNT, + "Unexpected table size"); + + return table[aType]; + } + + /** + * Convenience so that callers can pass a float containing an encoded type + * and have it decoded implicitly. + */ + static uint32_t ArgCountForType(float aType) { + return ArgCountForType(DecodeType(aType)); + } + + static bool IsValidType(uint32_t aType) { + return aType >= NS_SVG_PATH_SEG_FIRST_VALID_TYPE && + aType <= NS_SVG_PATH_SEG_LAST_VALID_TYPE; + } + + static bool IsCubicType(uint32_t aType) { + 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; + } + + static bool IsQuadraticType(uint32_t aType) { + 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; + } + + static bool IsArcType(uint32_t aType) { + return aType == dom::SVGPathSeg_Binding::PATHSEG_ARC_ABS || + aType == dom::SVGPathSeg_Binding::PATHSEG_ARC_REL; + } + + static bool IsRelativeOrAbsoluteType(uint32_t aType) { + MOZ_ASSERT(IsValidType(aType), "Seg type not recognized"); + + // When adding a new path segment type, ensure that the returned condition + // below is still correct. + static_assert( + NS_SVG_PATH_SEG_LAST_VALID_TYPE == + dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, + "Unexpected type"); + + return aType >= dom::SVGPathSeg_Binding::PATHSEG_MOVETO_ABS; + } + + static bool IsRelativeType(uint32_t aType) { + 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. + static_assert( + NS_SVG_PATH_SEG_LAST_VALID_TYPE == + dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, + "Unexpected type"); + + return aType & 1; + } + + static uint32_t RelativeVersionOfType(uint32_t aType) { + 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. + static_assert( + NS_SVG_PATH_SEG_LAST_VALID_TYPE == + dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, + "Unexpected type"); + + return aType | 1; + } + + static uint32_t SameTypeModuloRelativeness(uint32_t aType1, uint32_t aType2) { + if (!IsRelativeOrAbsoluteType(aType1)) { + return aType1 == aType2; + } + + return RelativeVersionOfType(aType1) == RelativeVersionOfType(aType2); + } + + /** + * Traverse the given path segment and update the SVGPathTraversalState + * object. + */ + static void TraversePathSegment(const float* aData, + SVGPathTraversalState& aState); + + /** + * 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); +}; + +/// 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); + +} // namespace mozilla + +#endif // DOM_SVG_SVGPATHSEGUTILS_H_ |