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 #ifndef mozilla_MotionPathUtils_h
8 #define mozilla_MotionPathUtils_h
9 
10 #include "mozilla/gfx/2D.h"
11 #include "mozilla/gfx/Point.h"
12 #include "mozilla/gfx/Rect.h"
13 #include "mozilla/Maybe.h"
14 #include "mozilla/ServoStyleConsts.h"
15 #include "Units.h"
16 
17 class nsIFrame;
18 
19 namespace nsStyleTransformMatrix {
20 class TransformReferenceBox;
21 }
22 
23 namespace mozilla {
24 
25 using RayFunction = StyleRayFunction<StyleAngle>;
26 
27 namespace layers {
28 class MotionPathData;
29 class PathCommand;
30 }  // namespace layers
31 
32 struct ResolvedMotionPathData {
33   gfx::Point mTranslate;
34   float mRotate;
35   // The delta value between transform-origin and offset-anchor.
36   gfx::Point mShift;
37 };
38 
39 struct RayReferenceData {
40   // The initial position related to the containing block.
41   CSSPoint mInitialPosition;
42   // The rect of the containing block.
43   CSSRect mContainingBlockRect;
44 
45   RayReferenceData() = default;
46   explicit RayReferenceData(const nsIFrame* aFrame);
47 
48   bool operator==(const RayReferenceData& aOther) const {
49     return mInitialPosition == aOther.mInitialPosition &&
50            mContainingBlockRect == aOther.mContainingBlockRect;
51   }
52 };
53 
54 // The collected information for offset-path. We preprocess the value of
55 // offset-path and use this data for resolving motion path.
56 struct OffsetPathData {
57   enum class Type : uint8_t {
58     None,
59     Path,
60     Ray,
61   };
62 
63   struct PathData {
64     RefPtr<gfx::Path> mGfxPath;
65     bool mIsClosedIntervals;
66   };
67 
68   struct RayData {
69     const RayFunction* mRay;
70     RayReferenceData mData;
71   };
72 
73   Type mType;
74   union {
75     PathData mPath;
76     RayData mRay;
77   };
78 
NoneOffsetPathData79   static OffsetPathData None() { return OffsetPathData(); }
PathOffsetPathData80   static OffsetPathData Path(const StyleSVGPathData& aPath,
81                              already_AddRefed<gfx::Path>&& aGfxPath) {
82     const auto& path = aPath._0.AsSpan();
83     return OffsetPathData(std::move(aGfxPath),
84                           !path.empty() && path.rbegin()->IsClosePath());
85   }
RayOffsetPathData86   static OffsetPathData Ray(const RayFunction& aRay,
87                             const RayReferenceData& aData) {
88     return OffsetPathData(&aRay, aData);
89   }
RayOffsetPathData90   static OffsetPathData Ray(const RayFunction& aRay, RayReferenceData&& aData) {
91     return OffsetPathData(&aRay, std::move(aData));
92   }
93 
IsNoneOffsetPathData94   bool IsNone() const { return mType == Type::None; }
IsPathOffsetPathData95   bool IsPath() const { return mType == Type::Path; }
IsRayOffsetPathData96   bool IsRay() const { return mType == Type::Ray; }
97 
AsPathOffsetPathData98   const PathData& AsPath() const {
99     MOZ_ASSERT(IsPath());
100     return mPath;
101   }
102 
AsRayOffsetPathData103   const RayData& AsRay() const {
104     MOZ_ASSERT(IsRay());
105     return mRay;
106   }
107 
~OffsetPathDataOffsetPathData108   ~OffsetPathData() {
109     switch (mType) {
110       case Type::Path:
111         mPath.~PathData();
112         break;
113       case Type::Ray:
114         mRay.~RayData();
115         break;
116       default:
117         break;
118     }
119   }
120 
OffsetPathDataOffsetPathData121   OffsetPathData(const OffsetPathData& aOther) : mType(aOther.mType) {
122     switch (mType) {
123       case Type::Path:
124         mPath = aOther.mPath;
125         break;
126       case Type::Ray:
127         mRay = aOther.mRay;
128         break;
129       default:
130         break;
131     }
132   }
133 
OffsetPathDataOffsetPathData134   OffsetPathData(OffsetPathData&& aOther) : mType(aOther.mType) {
135     switch (mType) {
136       case Type::Path:
137         mPath = std::move(aOther.mPath);
138         break;
139       case Type::Ray:
140         mRay = std::move(aOther.mRay);
141         break;
142       default:
143         break;
144     }
145   }
146 
147  private:
OffsetPathDataOffsetPathData148   OffsetPathData() : mType(Type::None) {}
OffsetPathDataOffsetPathData149   OffsetPathData(already_AddRefed<gfx::Path>&& aPath, bool aIsClosed)
150       : mType(Type::Path), mPath{std::move(aPath), aIsClosed} {}
OffsetPathDataOffsetPathData151   OffsetPathData(const RayFunction* aRay, RayReferenceData&& aRef)
152       : mType(Type::Ray), mRay{aRay, std::move(aRef)} {}
OffsetPathDataOffsetPathData153   OffsetPathData(const RayFunction* aRay, const RayReferenceData& aRef)
154       : mType(Type::Ray), mRay{aRay, aRef} {}
155   OffsetPathData& operator=(const OffsetPathData&) = delete;
156   OffsetPathData& operator=(OffsetPathData&&) = delete;
157 };
158 
159 // MotionPathUtils is a namespace class containing utility functions related to
160 // processing motion path in the [motion-1].
161 // https://drafts.fxtf.org/motion-1/
162 class MotionPathUtils final {
163   using TransformReferenceBox = nsStyleTransformMatrix::TransformReferenceBox;
164 
165  public:
166   // SVG frames (unlike other frames) have a reference box that can be (and
167   // typically is) offset from the TopLeft() of the frame.
168   //
169   // In motion path, we have to make sure the object is aligned with offset-path
170   // when using content area, so we should tweak the anchor point by a given
171   // offset.
172   static CSSPoint ComputeAnchorPointAdjustment(const nsIFrame& aFrame);
173 
174   /**
175    * Generate the motion path transform result. This function may be called on
176    * the compositor thread.
177    */
178   static Maybe<ResolvedMotionPathData> ResolveMotionPath(
179       const OffsetPathData& aPath, const LengthPercentage& aDistance,
180       const StyleOffsetRotate& aRotate, const StylePositionOrAuto& aAnchor,
181       const CSSPoint& aTransformOrigin, TransformReferenceBox&,
182       const CSSPoint& aAnchorPointAdjustment);
183 
184   /**
185    * Generate the motion path transform result with |nsIFrame|. This is only
186    * called in the main thread.
187    */
188   static Maybe<ResolvedMotionPathData> ResolveMotionPath(
189       const nsIFrame* aFrame, TransformReferenceBox&);
190 
191   /**
192    * Generate the motion path transfrom result with styles and
193    * layers::MotionPathData.
194    * This is only called by the compositor.
195    */
196   static Maybe<ResolvedMotionPathData> ResolveMotionPath(
197       const StyleOffsetPath* aPath, const StyleLengthPercentage* aDistance,
198       const StyleOffsetRotate* aRotate, const StylePositionOrAuto* aAnchor,
199       const Maybe<layers::MotionPathData>& aMotionPathData,
200       TransformReferenceBox&, gfx::Path* aCachedMotionPath);
201 
202   /**
203    * Normalize StyleSVGPathData.
204    *
205    * The algorithm of normalization is the same as normalize() in
206    * servo/components/style/values/specified/svg_path.rs
207    * FIXME: Bug 1489392: We don't have to normalize the path here if we accept
208    * the spec issue which would like to normalize svg paths at computed time.
209    * https://github.com/w3c/svgwg/issues/321
210    */
211   static StyleSVGPathData NormalizeSVGPathData(const StyleSVGPathData& aPath);
212 
213   /**
214    * Build a gfx::Path from the computed svg path. We should give it a path
215    * builder. If |aPathBuilder| is nullptr, we return null path.
216    * */
217   static already_AddRefed<gfx::Path> BuildPath(const StyleSVGPathData& aPath,
218                                                gfx::PathBuilder* aPathBuilder);
219 
220   /**
221    * Get a path builder for compositor.
222    */
223   static already_AddRefed<gfx::PathBuilder> GetCompositorPathBuilder();
224 };
225 
226 }  // namespace mozilla
227 
228 #endif  // mozilla_MotionPathUtils_h
229