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