| /* |
| * Copyright (C) 2003-2020 Apple Inc. All rights reserved. |
| * Copyright (C) 2006 Rob Buis <buis@kde.org> |
| * Copyright (C) 2007-2008 Torch Mobile, Inc. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #pragma once |
| |
| #include "FloatRect.h" |
| #include "InlinePathData.h" |
| #include "WindRule.h" |
| #include <wtf/EnumTraits.h> |
| #include <wtf/FastMalloc.h> |
| #include <wtf/Function.h> |
| #include <wtf/Forward.h> |
| |
| #if USE(CG) |
| |
| #include <wtf/RetainPtr.h> |
| #include <CoreGraphics/CGPath.h> |
| typedef struct CGPath PlatformPath; |
| |
| #elif USE(CAIRO) |
| #include "RefPtrCairo.h" |
| |
| #else |
| |
| typedef void PlatformPath; |
| |
| #endif |
| |
| #if !USE(CAIRO) |
| typedef PlatformPath* PlatformPathPtr; |
| |
| #if USE(CG) |
| using PlatformPathStorageType = RetainPtr<CGMutablePathRef>; |
| #else |
| using PlatformPathStorageType = PlatformPathPtr; |
| #endif |
| #endif |
| |
| namespace WTF { |
| class TextStream; |
| } |
| |
| namespace WebCore { |
| |
| class AffineTransform; |
| class FloatPoint; |
| class FloatRoundedRect; |
| class FloatSize; |
| class GraphicsContext; |
| class PathTraversalState; |
| class RoundedRect; |
| |
| // The points in the structure are the same as those that would be used with the |
| // add... method. For example, a line returns the endpoint, while a cubic returns |
| // two tangent points and the endpoint. |
| struct PathElement { |
| enum class Type : uint8_t { |
| MoveToPoint, // The points member will contain 1 value. |
| AddLineToPoint, // The points member will contain 1 value. |
| AddQuadCurveToPoint, // The points member will contain 2 values. |
| AddCurveToPoint, // The points member will contain 3 values. |
| CloseSubpath // The points member will contain no values. |
| }; |
| |
| FloatPoint points[3]; |
| Type type; |
| }; |
| |
| using PathApplierFunction = Function<void(const PathElement&)>; |
| |
| class Path { |
| WTF_MAKE_FAST_ALLOCATED; |
| public: |
| WEBCORE_EXPORT Path(); |
| #if USE(CG) |
| Path(RetainPtr<CGMutablePathRef>&&); |
| #endif |
| #if USE(CAIRO) |
| explicit Path(RefPtr<cairo_t>&&); |
| #endif |
| WEBCORE_EXPORT ~Path(); |
| |
| WEBCORE_EXPORT Path(const Path&); |
| WEBCORE_EXPORT Path(Path&&); |
| WEBCORE_EXPORT Path& operator=(const Path&); |
| WEBCORE_EXPORT Path& operator=(Path&&); |
| |
| #if ENABLE(INLINE_PATH_DATA) |
| static Path from(const InlinePathData& inlineData) |
| { |
| Path path; |
| path.m_inlineData = inlineData; |
| return path; |
| } |
| #endif |
| |
| static Path polygonPathFromPoints(const Vector<FloatPoint>&); |
| |
| bool contains(const FloatPoint&, WindRule = WindRule::NonZero) const; |
| bool strokeContains(const FloatPoint&, const Function<void(GraphicsContext&)>& strokeStyleApplier) const; |
| |
| // fastBoundingRect() should equal or contain boundingRect(); boundingRect() |
| // should perfectly bound the points within the path. |
| FloatRect boundingRect() const; |
| WEBCORE_EXPORT FloatRect fastBoundingRect() const; |
| FloatRect strokeBoundingRect(const Function<void(GraphicsContext&)>& strokeStyleApplier = { }) const; |
| |
| WEBCORE_EXPORT size_t elementCount() const; |
| float length() const; |
| PathTraversalState traversalStateAtLength(float length) const; |
| FloatPoint pointAtLength(float length) const; |
| |
| WEBCORE_EXPORT void clear(); |
| WEBCORE_EXPORT bool isNull() const; |
| bool isEmpty() const; |
| // Gets the current point of the current path, which is conceptually the final point reached by the path so far. |
| // Note the Path can be empty (isEmpty() == true) and still have a current point. |
| // FIXME: The above comment might need to be updated; on all supported platforms, the result of hasCurrentPoint() is identical |
| // to !isEmpty(). |
| bool hasCurrentPoint() const; |
| FloatPoint currentPoint() const; |
| |
| bool isClosed() const; |
| |
| WEBCORE_EXPORT void moveTo(const FloatPoint&); |
| WEBCORE_EXPORT void addLineTo(const FloatPoint&); |
| WEBCORE_EXPORT void addQuadCurveTo(const FloatPoint& controlPoint, const FloatPoint& endPoint); |
| WEBCORE_EXPORT void addBezierCurveTo(const FloatPoint& controlPoint1, const FloatPoint& controlPoint2, const FloatPoint& endPoint); |
| void addArcTo(const FloatPoint&, const FloatPoint&, float radius); |
| WEBCORE_EXPORT void closeSubpath(); |
| |
| void addArc(const FloatPoint&, float radius, float startAngle, float endAngle, bool anticlockwise); |
| WEBCORE_EXPORT void addRect(const FloatRect&); |
| void addEllipse(FloatPoint, float radiusX, float radiusY, float rotation, float startAngle, float endAngle, bool anticlockwise); |
| void addEllipse(const FloatRect&); |
| |
| enum class RoundedRectStrategy : uint8_t { |
| PreferNative, |
| PreferBezier |
| }; |
| |
| WEBCORE_EXPORT void addRoundedRect(const FloatRect&, const FloatSize& roundingRadii, RoundedRectStrategy = RoundedRectStrategy::PreferNative); |
| WEBCORE_EXPORT void addRoundedRect(const FloatRoundedRect&, RoundedRectStrategy = RoundedRectStrategy::PreferNative); |
| void addRoundedRect(const RoundedRect&); |
| |
| void addPath(const Path&, const AffineTransform&); |
| |
| void translate(const FloatSize&); |
| |
| // To keep Path() cheap, it does not allocate a PlatformPath immediately |
| // meaning Path::platformPath() can return null. |
| #if USE(CG) |
| WEBCORE_EXPORT PlatformPathPtr platformPath() const; |
| #elif USE(CAIRO) |
| cairo_t* cairoPath() const { return m_path.get(); } |
| #else |
| PlatformPathPtr platformPath() const { return m_path; } |
| #endif |
| |
| #if !USE(CAIRO) |
| // ensurePlatformPath() will allocate a PlatformPath if it has not yet been and will never return null. |
| WEBCORE_EXPORT PlatformPathPtr ensurePlatformPath(); |
| #endif |
| |
| WEBCORE_EXPORT void apply(const PathApplierFunction&) const; |
| void transform(const AffineTransform&); |
| |
| static float circleControlPoint() |
| { |
| // Approximation of control point positions on a bezier to simulate a quarter of a circle. |
| // This is 1-kappa, where kappa = 4 * (sqrt(2) - 1) / 3 |
| return 0.447715; |
| } |
| |
| void addBeziersForRoundedRect(const FloatRect&, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius); |
| |
| #if USE(CG) |
| void platformAddPathForRoundedRect(const FloatRect&, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius); |
| #endif |
| |
| #ifndef NDEBUG |
| void dump() const; |
| #endif |
| |
| template<class Encoder> void encode(Encoder&) const; |
| template<class Decoder> static std::optional<Path> decode(Decoder&); |
| |
| #if ENABLE(INLINE_PATH_DATA) |
| template<typename DataType> const DataType& inlineData() const; |
| InlinePathData inlineData() const { return m_inlineData; } |
| template<typename DataType> bool hasInlineData() const; |
| bool hasInlineData() const; |
| #endif |
| |
| private: |
| #if ENABLE(INLINE_PATH_DATA) |
| template<typename DataType> DataType& inlineData(); |
| std::optional<FloatRect> fastBoundingRectFromInlineData() const; |
| std::optional<FloatRect> boundingRectFromInlineData() const; |
| #endif |
| |
| void moveToSlowCase(const FloatPoint&); |
| void addLineToSlowCase(const FloatPoint&); |
| void addArcSlowCase(const FloatPoint&, float radius, float startAngle, float endAngle, bool anticlockwise); |
| void addQuadCurveToSlowCase(const FloatPoint& controlPoint, const FloatPoint& endPoint); |
| void addBezierCurveToSlowCase(const FloatPoint& controlPoint1, const FloatPoint& controlPoint2, const FloatPoint& endPoint); |
| |
| FloatRect boundingRectSlowCase() const; |
| FloatRect fastBoundingRectSlowCase() const; |
| bool isEmptySlowCase() const; |
| FloatPoint currentPointSlowCase() const; |
| size_t elementCountSlowCase() const; |
| void applySlowCase(const PathApplierFunction&) const; |
| |
| #if USE(CG) |
| void createCGPath() const; |
| void swap(Path&); |
| #endif |
| |
| #if USE(CAIRO) |
| cairo_t* ensureCairoPath(); |
| void appendElement(PathElement::Type, Vector<FloatPoint, 3>&&); |
| #endif |
| |
| #if USE(CAIRO) |
| RefPtr<cairo_t> m_path; |
| #else |
| mutable PlatformPathStorageType m_path; |
| #endif |
| |
| #if ENABLE(INLINE_PATH_DATA) |
| InlinePathData m_inlineData; |
| #endif |
| #if USE(CG) |
| mutable bool m_copyPathBeforeMutation { false }; |
| #endif |
| #if USE(CAIRO) |
| std::optional<Vector<PathElement>> m_elements; |
| #endif |
| }; |
| |
| WEBCORE_EXPORT WTF::TextStream& operator<<(WTF::TextStream&, const Path&); |
| |
| template<class Encoder> void Path::encode(Encoder& encoder) const |
| { |
| #if ENABLE(INLINE_PATH_DATA) |
| bool hasInlineData = this->hasInlineData(); |
| encoder << hasInlineData; |
| if (hasInlineData) { |
| encoder << m_inlineData; |
| return; |
| } |
| #endif |
| |
| encoder << static_cast<uint64_t>(elementCount()); |
| |
| apply([&](auto& element) { |
| encoder << element.type; |
| |
| switch (element.type) { |
| case PathElement::Type::MoveToPoint: |
| encoder << element.points[0]; |
| break; |
| case PathElement::Type::AddLineToPoint: |
| encoder << element.points[0]; |
| break; |
| case PathElement::Type::AddQuadCurveToPoint: |
| encoder << element.points[0]; |
| encoder << element.points[1]; |
| break; |
| case PathElement::Type::AddCurveToPoint: |
| encoder << element.points[0]; |
| encoder << element.points[1]; |
| encoder << element.points[2]; |
| break; |
| case PathElement::Type::CloseSubpath: |
| break; |
| } |
| }); |
| } |
| |
| template<class Decoder> std::optional<Path> Path::decode(Decoder& decoder) |
| { |
| Path path; |
| |
| #if ENABLE(INLINE_PATH_DATA) |
| bool hasInlineData; |
| if (!decoder.decode(hasInlineData)) |
| return std::nullopt; |
| |
| if (hasInlineData) { |
| if (!decoder.decode(path.m_inlineData)) |
| return std::nullopt; |
| |
| return path; |
| } |
| #endif |
| |
| uint64_t numPoints; |
| if (!decoder.decode(numPoints)) |
| return std::nullopt; |
| |
| path.clear(); |
| |
| for (uint64_t i = 0; i < numPoints; ++i) { |
| PathElement::Type elementType; |
| if (!decoder.decode(elementType)) |
| return std::nullopt; |
| |
| switch (elementType) { |
| case PathElement::Type::MoveToPoint: { |
| FloatPoint point; |
| if (!decoder.decode(point)) |
| return std::nullopt; |
| path.moveTo(point); |
| break; |
| } |
| case PathElement::Type::AddLineToPoint: { |
| FloatPoint point; |
| if (!decoder.decode(point)) |
| return std::nullopt; |
| path.addLineTo(point); |
| break; |
| } |
| case PathElement::Type::AddQuadCurveToPoint: { |
| FloatPoint controlPoint; |
| if (!decoder.decode(controlPoint)) |
| return std::nullopt; |
| |
| FloatPoint endPoint; |
| if (!decoder.decode(endPoint)) |
| return std::nullopt; |
| |
| path.addQuadCurveTo(controlPoint, endPoint); |
| break; |
| } |
| case PathElement::Type::AddCurveToPoint: { |
| FloatPoint controlPoint1; |
| if (!decoder.decode(controlPoint1)) |
| return std::nullopt; |
| |
| FloatPoint controlPoint2; |
| if (!decoder.decode(controlPoint2)) |
| return std::nullopt; |
| |
| FloatPoint endPoint; |
| if (!decoder.decode(endPoint)) |
| return std::nullopt; |
| |
| path.addBezierCurveTo(controlPoint1, controlPoint2, endPoint); |
| break; |
| } |
| case PathElement::Type::CloseSubpath: |
| path.closeSubpath(); |
| break; |
| } |
| } |
| |
| return path; |
| } |
| |
| #if ENABLE(INLINE_PATH_DATA) |
| |
| template <typename DataType> inline bool Path::hasInlineData() const |
| { |
| return std::holds_alternative<DataType>(m_inlineData); |
| } |
| |
| template<typename DataType> inline const DataType& Path::inlineData() const |
| { |
| return std::get<DataType>(m_inlineData); |
| } |
| |
| template<typename DataType> inline DataType& Path::inlineData() |
| { |
| return std::get<DataType>(m_inlineData); |
| } |
| |
| inline bool Path::hasInlineData() const |
| { |
| return !hasInlineData<std::monostate>(); |
| } |
| |
| #endif |
| |
| } // namespace WebCore |
| |
| namespace WTF { |
| |
| template<> struct EnumTraits<WebCore::PathElement::Type> { |
| using values = EnumValues< |
| WebCore::PathElement::Type, |
| WebCore::PathElement::Type::MoveToPoint, |
| WebCore::PathElement::Type::AddLineToPoint, |
| WebCore::PathElement::Type::AddQuadCurveToPoint, |
| WebCore::PathElement::Type::AddCurveToPoint, |
| WebCore::PathElement::Type::CloseSubpath |
| >; |
| }; |
| |
| } // namespace WTF |