/src/mozilla-central/layout/style/nsStyleTransformMatrix.h
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 | | /* |
8 | | * A class representing three matrices that can be used for style transforms. |
9 | | */ |
10 | | |
11 | | #ifndef nsStyleTransformMatrix_h_ |
12 | | #define nsStyleTransformMatrix_h_ |
13 | | |
14 | | #include "gfxPoint.h" |
15 | | #include "mozilla/gfx/Matrix.h" |
16 | | #include "mozilla/EnumeratedArray.h" |
17 | | #include "nsCSSValue.h" |
18 | | #include "nsSize.h" |
19 | | |
20 | | #include <limits> |
21 | | |
22 | | class nsIFrame; |
23 | | class nsPresContext; |
24 | | struct gfxQuaternion; |
25 | | struct nsRect; |
26 | | |
27 | | namespace mozilla { |
28 | | struct MotionPathData; |
29 | | } |
30 | | |
31 | | /** |
32 | | * A helper to generate gfxMatrixes from css transform functions. |
33 | | */ |
34 | | namespace nsStyleTransformMatrix { |
35 | | // The operator passed to Servo backend. |
36 | | enum class MatrixTransformOperator: uint8_t { |
37 | | Interpolate, |
38 | | Accumulate |
39 | | }; |
40 | | |
41 | | // Function for applying perspective() transform function. We treat |
42 | | // any value smaller than epsilon as perspective(infinity), which |
43 | | // follows CSSWG's resolution on perspective(0). See bug 1316236. |
44 | | inline void ApplyPerspectiveToMatrix(mozilla::gfx::Matrix4x4& aMatrix, |
45 | | float aDepth) |
46 | 0 | { |
47 | 0 | if (aDepth >= std::numeric_limits<float>::epsilon()) { |
48 | 0 | aMatrix.Perspective(aDepth); |
49 | 0 | } |
50 | 0 | } |
51 | | |
52 | | /** |
53 | | * This class provides on-demand access to the 'reference box' for CSS |
54 | | * transforms (needed to resolve percentage values in 'transform', |
55 | | * 'transform-origin', etc.): |
56 | | * |
57 | | * http://dev.w3.org/csswg/css-transforms/#reference-box |
58 | | * |
59 | | * This class helps us to avoid calculating the reference box unless and |
60 | | * until it is actually needed. This is important for performance when |
61 | | * transforms are applied to SVG elements since the reference box for SVG is |
62 | | * much more expensive to calculate (than for elements with a CSS layout box |
63 | | * where we can use the nsIFrame's cached mRect), much more common (than on |
64 | | * HTML), and yet very rarely have percentage values that require the |
65 | | * reference box to be resolved. We also don't want to cause SVG frames to |
66 | | * cache lots of ObjectBoundingBoxProperty objects that aren't needed. |
67 | | * |
68 | | * If UNIFIED_CONTINUATIONS (experimental, and currently broke) is defined, |
69 | | * we consider the reference box for non-SVG frames to be the smallest |
70 | | * rectangle containing a frame and all of its continuations. For example, |
71 | | * if there is a <span> element with several continuations split over |
72 | | * several lines, this function will return the rectangle containing all of |
73 | | * those continuations. (This behavior is not currently in a spec.) |
74 | | */ |
75 | | class MOZ_STACK_CLASS TransformReferenceBox final { |
76 | | public: |
77 | | typedef nscoord (TransformReferenceBox::*DimensionGetter)(); |
78 | | |
79 | | explicit TransformReferenceBox() |
80 | | : mFrame(nullptr) |
81 | | , mX(0) |
82 | | , mY(0) |
83 | | , mWidth(0) |
84 | | , mHeight(0) |
85 | | , mIsCached(false) |
86 | 0 | {} |
87 | | |
88 | | explicit TransformReferenceBox(const nsIFrame* aFrame) |
89 | | : mFrame(aFrame) |
90 | | , mX(0) |
91 | | , mY(0) |
92 | | , mWidth(0) |
93 | | , mHeight(0) |
94 | | , mIsCached(false) |
95 | 0 | { |
96 | 0 | MOZ_ASSERT(mFrame); |
97 | 0 | } |
98 | | |
99 | | explicit TransformReferenceBox(const nsIFrame* aFrame, |
100 | | const nsSize& aFallbackDimensions) |
101 | | : mX(0) |
102 | | , mY(0) |
103 | | , mWidth(0) |
104 | | , mHeight(0) |
105 | 0 | { |
106 | 0 | mFrame = aFrame; |
107 | 0 | mIsCached = false; |
108 | 0 | if (!mFrame) { |
109 | 0 | Init(aFallbackDimensions); |
110 | 0 | } |
111 | 0 | } |
112 | | |
113 | 0 | void Init(const nsIFrame* aFrame) { |
114 | 0 | MOZ_ASSERT(!mFrame && !mIsCached); |
115 | 0 | mFrame = aFrame; |
116 | 0 | } |
117 | | |
118 | | void Init(const nsSize& aDimensions); |
119 | | |
120 | | /** |
121 | | * The offset of the reference box from the nsIFrame's TopLeft(). This |
122 | | * is non-zero only in the case of SVG content. If we can successfully |
123 | | * implement UNIFIED_CONTINUATIONS at some point in the future then it |
124 | | * may also be non-zero for non-SVG content. |
125 | | */ |
126 | 0 | nscoord X() { |
127 | 0 | EnsureDimensionsAreCached(); |
128 | 0 | return mX; |
129 | 0 | } |
130 | 0 | nscoord Y() { |
131 | 0 | EnsureDimensionsAreCached(); |
132 | 0 | return mY; |
133 | 0 | } |
134 | | |
135 | | /** |
136 | | * The size of the reference box. |
137 | | */ |
138 | 0 | nscoord Width() { |
139 | 0 | EnsureDimensionsAreCached(); |
140 | 0 | return mWidth; |
141 | 0 | } |
142 | 0 | nscoord Height() { |
143 | 0 | EnsureDimensionsAreCached(); |
144 | 0 | return mHeight; |
145 | 0 | } |
146 | | |
147 | 0 | bool IsEmpty() { |
148 | 0 | return !mFrame; |
149 | 0 | } |
150 | | |
151 | | private: |
152 | | // We don't really need to prevent copying, but since none of our consumers |
153 | | // currently need to copy, preventing copying may allow us to catch some |
154 | | // cases where we use pass-by-value instead of pass-by-reference. |
155 | | TransformReferenceBox(const TransformReferenceBox&) = delete; |
156 | | |
157 | | void EnsureDimensionsAreCached(); |
158 | | |
159 | | const nsIFrame* mFrame; |
160 | | nscoord mX, mY, mWidth, mHeight; |
161 | | bool mIsCached; |
162 | | }; |
163 | | |
164 | | /** |
165 | | * Return the transform function, as an nsCSSKeyword, for the given |
166 | | * nsCSSValue::Array from a transform list. |
167 | | */ |
168 | | nsCSSKeyword TransformFunctionOf(const nsCSSValue::Array* aData); |
169 | | |
170 | | void SetIdentityMatrix(nsCSSValue::Array* aMatrix); |
171 | | |
172 | | float ProcessTranslatePart(const nsCSSValue& aValue, |
173 | | TransformReferenceBox* aRefBox, |
174 | | TransformReferenceBox::DimensionGetter aDimensionGetter = nullptr); |
175 | | |
176 | | void |
177 | | ProcessInterpolateMatrix(mozilla::gfx::Matrix4x4& aMatrix, |
178 | | const nsCSSValue::Array* aData, |
179 | | TransformReferenceBox& aBounds, |
180 | | bool* aContains3dTransform); |
181 | | |
182 | | void |
183 | | ProcessAccumulateMatrix(mozilla::gfx::Matrix4x4& aMatrix, |
184 | | const nsCSSValue::Array* aData, |
185 | | TransformReferenceBox& aBounds, |
186 | | bool* aContains3dTransform); |
187 | | |
188 | | |
189 | | /** |
190 | | * Given an nsCSSValueList containing -moz-transform functions, |
191 | | * returns a matrix containing the value of those functions. |
192 | | * |
193 | | * @param aData The nsCSSValueList containing the transform functions |
194 | | * @param aBounds The frame's bounding rectangle. |
195 | | * @param aAppUnitsPerMatrixUnit The number of app units per device pixel. |
196 | | * @param aContains3dTransform [out] Set to true if aList contains at least |
197 | | * one 3d transform function (as defined in the CSS transforms |
198 | | * specification), false otherwise. |
199 | | * |
200 | | * eCSSUnit_Pixel (as they are in an StyleAnimationValue) |
201 | | */ |
202 | | mozilla::gfx::Matrix4x4 ReadTransforms(const nsCSSValueList* aList, |
203 | | TransformReferenceBox& aBounds, |
204 | | float aAppUnitsPerMatrixUnit, |
205 | | bool* aContains3dTransform); |
206 | | |
207 | | // Generate the gfx::Matrix for CSS Transform Module Level 2. |
208 | | // https://drafts.csswg.org/css-transforms-2/#ctm |
209 | | mozilla::gfx::Matrix4x4 |
210 | | ReadTransforms(const nsCSSValueList* aIndividualTransforms, |
211 | | const mozilla::Maybe<mozilla::MotionPathData>& aMotion, |
212 | | const nsCSSValueList* aTransform, |
213 | | TransformReferenceBox& aRefBox, |
214 | | float aAppUnitsPerMatrixUnit, |
215 | | bool* aContains3dTransform); |
216 | | |
217 | | /** |
218 | | * Given two nsStyleCoord values, compute the 2d position with respect to the |
219 | | * given TransformReferenceBox that these values describe, in device pixels. |
220 | | */ |
221 | | mozilla::gfx::Point Convert2DPosition(nsStyleCoord const (&aValue)[2], |
222 | | TransformReferenceBox& aRefBox, |
223 | | int32_t aAppUnitsPerDevPixel); |
224 | | |
225 | | // Shear type for decomposition. |
226 | | enum class ShearType { |
227 | | XYSHEAR, |
228 | | XZSHEAR, |
229 | | YZSHEAR, |
230 | | Count |
231 | | }; |
232 | | using ShearArray = |
233 | | mozilla::EnumeratedArray<ShearType, ShearType::Count, float>; |
234 | | |
235 | | /* |
236 | | * Implements the 2d transform matrix decomposition algorithm. |
237 | | */ |
238 | | bool Decompose2DMatrix(const mozilla::gfx::Matrix& aMatrix, |
239 | | mozilla::gfx::Point3D& aScale, |
240 | | ShearArray& aShear, |
241 | | gfxQuaternion& aRotate, |
242 | | mozilla::gfx::Point3D& aTranslate); |
243 | | /* |
244 | | * Implements the 3d transform matrix decomposition algorithm. |
245 | | */ |
246 | | bool Decompose3DMatrix(const mozilla::gfx::Matrix4x4& aMatrix, |
247 | | mozilla::gfx::Point3D& aScale, |
248 | | ShearArray& aShear, |
249 | | gfxQuaternion& aRotate, |
250 | | mozilla::gfx::Point3D& aTranslate, |
251 | | mozilla::gfx::Point4D& aPerspective); |
252 | | |
253 | | mozilla::gfx::Matrix CSSValueArrayTo2DMatrix(nsCSSValue::Array* aArray); |
254 | | mozilla::gfx::Matrix4x4 CSSValueArrayTo3DMatrix(nsCSSValue::Array* aArray); |
255 | | |
256 | | mozilla::gfx::Size GetScaleValue(const nsCSSValueSharedList* aList, |
257 | | const nsIFrame* aForFrame); |
258 | | } // namespace nsStyleTransformMatrix |
259 | | |
260 | | #endif |