1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef UI_GFX_TRANSFORM_H_
6#define UI_GFX_TRANSFORM_H_
7
8#include <iosfwd>
9#include <string>
10
11#include "base/compiler_specific.h"
12#include "third_party/skia/include/utils/SkMatrix44.h"
13#include "ui/gfx/gfx_export.h"
14#include "ui/gfx/vector2d_f.h"
15
16namespace gfx {
17
18class BoxF;
19class RectF;
20class Point;
21class Point3F;
22class Vector3dF;
23
24// 4x4 transformation matrix. Transform is cheap and explicitly allows
25// copy/assign.
26class GFX_EXPORT Transform {
27 public:
28
29  enum SkipInitialization {
30    kSkipInitialization
31  };
32
33  Transform() : matrix_(SkMatrix44::kIdentity_Constructor) {}
34
35  // Skips initializing this matrix to avoid overhead, when we know it will be
36  // initialized before use.
37  Transform(SkipInitialization)
38      : matrix_(SkMatrix44::kUninitialized_Constructor) {}
39  Transform(const Transform& rhs) : matrix_(rhs.matrix_) {}
40  // Initialize with the concatenation of lhs * rhs.
41  Transform(const Transform& lhs, const Transform& rhs)
42      : matrix_(lhs.matrix_, rhs.matrix_) {}
43  // Constructs a transform from explicit 16 matrix elements. Elements
44  // should be given in row-major order.
45  Transform(SkMScalar col1row1,
46            SkMScalar col2row1,
47            SkMScalar col3row1,
48            SkMScalar col4row1,
49            SkMScalar col1row2,
50            SkMScalar col2row2,
51            SkMScalar col3row2,
52            SkMScalar col4row2,
53            SkMScalar col1row3,
54            SkMScalar col2row3,
55            SkMScalar col3row3,
56            SkMScalar col4row3,
57            SkMScalar col1row4,
58            SkMScalar col2row4,
59            SkMScalar col3row4,
60            SkMScalar col4row4);
61  // Constructs a transform from explicit 2d elements. All other matrix
62  // elements remain the same as the corresponding elements of an identity
63  // matrix.
64  Transform(SkMScalar col1row1,
65            SkMScalar col2row1,
66            SkMScalar col1row2,
67            SkMScalar col2row2,
68            SkMScalar x_translation,
69            SkMScalar y_translation);
70  ~Transform() {}
71
72  bool operator==(const Transform& rhs) const { return matrix_ == rhs.matrix_; }
73  bool operator!=(const Transform& rhs) const { return matrix_ != rhs.matrix_; }
74
75  // Resets this transform to the identity transform.
76  void MakeIdentity() { matrix_.setIdentity(); }
77
78  // Applies the current transformation on a 2d rotation and assigns the result
79  // to |this|.
80  void Rotate(double degrees) { RotateAboutZAxis(degrees); }
81
82  // Applies the current transformation on an axis-angle rotation and assigns
83  // the result to |this|.
84  void RotateAboutXAxis(double degrees);
85  void RotateAboutYAxis(double degrees);
86  void RotateAboutZAxis(double degrees);
87  void RotateAbout(const Vector3dF& axis, double degrees);
88
89  // Applies the current transformation on a scaling and assigns the result
90  // to |this|.
91  void Scale(SkMScalar x, SkMScalar y);
92  void Scale3d(SkMScalar x, SkMScalar y, SkMScalar z);
93  gfx::Vector2dF Scale2d() const {
94    return gfx::Vector2dF(matrix_.get(0, 0), matrix_.get(1, 1));
95  }
96
97  // Applies the current transformation on a translation and assigns the result
98  // to |this|.
99  void Translate(SkMScalar x, SkMScalar y);
100  void Translate3d(SkMScalar x, SkMScalar y, SkMScalar z);
101
102  // Applies the current transformation on a skew and assigns the result
103  // to |this|.
104  void SkewX(double angle_x);
105  void SkewY(double angle_y);
106
107  // Applies the current transformation on a perspective transform and assigns
108  // the result to |this|.
109  void ApplyPerspectiveDepth(SkMScalar depth);
110
111  // Applies a transformation on the current transformation
112  // (i.e. 'this = this * transform;').
113  void PreconcatTransform(const Transform& transform);
114
115  // Applies a transformation on the current transformation
116  // (i.e. 'this = transform * this;').
117  void ConcatTransform(const Transform& transform);
118
119  // Returns true if this is the identity matrix.
120  bool IsIdentity() const { return matrix_.isIdentity(); }
121
122  // Returns true if the matrix is either identity or pure translation.
123  bool IsIdentityOrTranslation() const { return matrix_.isTranslate(); }
124
125  // Returns true if the matrix is either identity or pure translation,
126  // allowing for an amount of inaccuracy as specified by the parameter.
127  bool IsApproximatelyIdentityOrTranslation(SkMScalar tolerance) const;
128
129  // Returns true if the matrix is either a positive scale and/or a translation.
130  bool IsPositiveScaleOrTranslation() const {
131    if (!IsScaleOrTranslation())
132      return false;
133    return matrix_.get(0, 0) > 0.0 && matrix_.get(1, 1) > 0.0 &&
134           matrix_.get(2, 2) > 0.0;
135  }
136
137  // Returns true if the matrix is either identity or pure, non-fractional
138  // translation.
139  bool IsIdentityOrIntegerTranslation() const;
140
141  // Returns true if the matrix had only scaling components.
142  bool IsScale2d() const {
143    return !(matrix_.getType() & ~SkMatrix44::kScale_Mask);
144  }
145
146  // Returns true if the matrix is has only scaling and translation components.
147  bool IsScaleOrTranslation() const { return matrix_.isScaleTranslate(); }
148
149  // Returns true if axis-aligned 2d rects will remain axis-aligned after being
150  // transformed by this matrix.
151  bool Preserves2dAxisAlignment() const;
152
153  // Returns true if the matrix has any perspective component that would
154  // change the w-component of a homogeneous point.
155  bool HasPerspective() const { return matrix_.hasPerspective(); }
156
157  // Returns true if this transform is non-singular.
158  bool IsInvertible() const { return matrix_.invert(NULL); }
159
160  // Returns true if a layer with a forward-facing normal of (0, 0, 1) would
161  // have its back side facing frontwards after applying the transform.
162  bool IsBackFaceVisible() const;
163
164  // Inverts the transform which is passed in. Returns true if successful.
165  bool GetInverse(Transform* transform) const WARN_UNUSED_RESULT;
166
167  // Transposes this transform in place.
168  void Transpose();
169
170  // Set 3rd row and 3rd colum to (0, 0, 1, 0). Note that this flattening
171  // operation is not quite the same as an orthographic projection and is
172  // technically not a linear operation.
173  //
174  // One useful interpretation of doing this operation:
175  //  - For x and y values, the new transform behaves effectively like an
176  //    orthographic projection was added to the matrix sequence.
177  //  - For z values, the new transform overrides any effect that the transform
178  //    had on z, and instead it preserves the z value for any points that are
179  //    transformed.
180  //  - Because of linearity of transforms, this flattened transform also
181  //    preserves the effect that any subsequent (multiplied from the right)
182  //    transforms would have on z values.
183  //
184  void FlattenTo2d();
185
186  // Returns the x and y translation components of the matrix.
187  Vector2dF To2dTranslation() const;
188
189  // Applies the transformation to the point.
190  void TransformPoint(Point3F* point) const;
191
192  // Applies the transformation to the point.
193  void TransformPoint(Point* point) const;
194
195  // Applies the reverse transformation on the point. Returns true if the
196  // transformation can be inverted.
197  bool TransformPointReverse(Point3F* point) const;
198
199  // Applies the reverse transformation on the point. Returns true if the
200  // transformation can be inverted. Rounds the result to the nearest point.
201  bool TransformPointReverse(Point* point) const;
202
203  // Applies transformation on the given rect. After the function completes,
204  // |rect| will be the smallest axis aligned bounding rect containing the
205  // transformed rect.
206  void TransformRect(RectF* rect) const;
207
208  // Applies the reverse transformation on the given rect. After the function
209  // completes, |rect| will be the smallest axis aligned bounding rect
210  // containing the transformed rect. Returns false if the matrix cannot be
211  // inverted.
212  bool TransformRectReverse(RectF* rect) const;
213
214  // Applies transformation on the given box. After the function completes,
215  // |box| will be the smallest axis aligned bounding box containing the
216  // transformed box.
217  void TransformBox(BoxF* box) const;
218
219  // Applies the reverse transformation on the given box. After the function
220  // completes, |box| will be the smallest axis aligned bounding box
221  // containing the transformed box. Returns false if the matrix cannot be
222  // inverted.
223  bool TransformBoxReverse(BoxF* box) const;
224
225  // Decomposes |this| and |from|, interpolates the decomposed values, and
226  // sets |this| to the reconstituted result. Returns false if either matrix
227  // can't be decomposed. Uses routines described in this spec:
228  // http://www.w3.org/TR/css3-3d-transforms/.
229  //
230  // Note: this call is expensive since we need to decompose the transform. If
231  // you're going to be calling this rapidly (e.g., in an animation) you should
232  // decompose once using gfx::DecomposeTransforms and reuse your
233  // DecomposedTransform.
234  bool Blend(const Transform& from, double progress);
235
236  // Returns |this| * |other|.
237  Transform operator*(const Transform& other) const {
238    return Transform(*this, other);
239  }
240
241  // Sets |this| = |this| * |other|
242  Transform& operator*=(const Transform& other) {
243    PreconcatTransform(other);
244    return *this;
245  }
246
247  // Returns the underlying matrix.
248  const SkMatrix44& matrix() const { return matrix_; }
249  SkMatrix44& matrix() { return matrix_; }
250
251  std::string ToString() const;
252
253 private:
254  void TransformPointInternal(const SkMatrix44& xform,
255                              Point* point) const;
256
257  void TransformPointInternal(const SkMatrix44& xform,
258                              Point3F* point) const;
259
260  SkMatrix44 matrix_;
261
262  // copy/assign are allowed.
263};
264
265// This is declared here for use in gtest-based unit tests but is defined in
266// the gfx_test_support target. Depend on that to use this in your unit test.
267// This should not be used in production code - call ToString() instead.
268void PrintTo(const Transform& transform, ::std::ostream* os);
269
270}  // namespace gfx
271
272#endif  // UI_GFX_TRANSFORM_H_
273