Coverage Report

Created: 2018-09-25 14:53

/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