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 // MSVC++ requires this to be set before any other includes to get M_PI.
      6 #define _USE_MATH_DEFINES
      7 
      8 #include "ui/gfx/transform.h"
      9 
     10 #include <cmath>
     11 
     12 #include "base/strings/stringprintf.h"
     13 #include "ui/gfx/point.h"
     14 #include "ui/gfx/point3_f.h"
     15 #include "ui/gfx/rect.h"
     16 #include "ui/gfx/safe_integer_conversions.h"
     17 #include "ui/gfx/skia_util.h"
     18 #include "ui/gfx/transform_util.h"
     19 #include "ui/gfx/vector3d_f.h"
     20 
     21 namespace gfx {
     22 
     23 namespace {
     24 
     25 // Taken from SkMatrix44.
     26 const double kEpsilon = 1e-8;
     27 
     28 double TanDegrees(double degrees) {
     29   double radians = degrees * M_PI / 180;
     30   return std::tan(radians);
     31 }
     32 
     33 }  // namespace
     34 
     35 Transform::Transform(
     36     double col1row1, double col2row1, double col3row1, double col4row1,
     37     double col1row2, double col2row2, double col3row2, double col4row2,
     38     double col1row3, double col2row3, double col3row3, double col4row3,
     39     double col1row4, double col2row4, double col3row4, double col4row4)
     40     : matrix_(SkMatrix44::kUninitialized_Constructor)
     41 {
     42   matrix_.setDouble(0, 0, col1row1);
     43   matrix_.setDouble(1, 0, col1row2);
     44   matrix_.setDouble(2, 0, col1row3);
     45   matrix_.setDouble(3, 0, col1row4);
     46 
     47   matrix_.setDouble(0, 1, col2row1);
     48   matrix_.setDouble(1, 1, col2row2);
     49   matrix_.setDouble(2, 1, col2row3);
     50   matrix_.setDouble(3, 1, col2row4);
     51 
     52   matrix_.setDouble(0, 2, col3row1);
     53   matrix_.setDouble(1, 2, col3row2);
     54   matrix_.setDouble(2, 2, col3row3);
     55   matrix_.setDouble(3, 2, col3row4);
     56 
     57   matrix_.setDouble(0, 3, col4row1);
     58   matrix_.setDouble(1, 3, col4row2);
     59   matrix_.setDouble(2, 3, col4row3);
     60   matrix_.setDouble(3, 3, col4row4);
     61 }
     62 
     63 Transform::Transform(
     64     double col1row1, double col2row1,
     65     double col1row2, double col2row2,
     66     double x_translation, double y_translation)
     67     : matrix_(SkMatrix44::kIdentity_Constructor)
     68 {
     69   matrix_.setDouble(0, 0, col1row1);
     70   matrix_.setDouble(1, 0, col1row2);
     71   matrix_.setDouble(0, 1, col2row1);
     72   matrix_.setDouble(1, 1, col2row2);
     73   matrix_.setDouble(0, 3, x_translation);
     74   matrix_.setDouble(1, 3, y_translation);
     75 }
     76 
     77 void Transform::RotateAboutXAxis(double degrees) {
     78   double radians = degrees * M_PI / 180;
     79   double cosTheta = std::cos(radians);
     80   double sinTheta = std::sin(radians);
     81   if (matrix_.isIdentity()) {
     82       matrix_.set3x3(1, 0, 0,
     83                      0, cosTheta, sinTheta,
     84                      0, -sinTheta, cosTheta);
     85   } else {
     86     SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
     87     rot.set3x3(1, 0, 0,
     88                0, cosTheta, sinTheta,
     89                0, -sinTheta, cosTheta);
     90     matrix_.preConcat(rot);
     91   }
     92 }
     93 
     94 void Transform::RotateAboutYAxis(double degrees) {
     95   double radians = degrees * M_PI / 180;
     96   double cosTheta = std::cos(radians);
     97   double sinTheta = std::sin(radians);
     98   if (matrix_.isIdentity()) {
     99       // Note carefully the placement of the -sinTheta for rotation about
    100       // y-axis is different than rotation about x-axis or z-axis.
    101       matrix_.set3x3(cosTheta, 0, -sinTheta,
    102                      0, 1, 0,
    103                      sinTheta, 0, cosTheta);
    104   } else {
    105     SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
    106     rot.set3x3(cosTheta, 0, -sinTheta,
    107                0, 1, 0,
    108                sinTheta, 0, cosTheta);
    109     matrix_.preConcat(rot);
    110   }
    111 }
    112 
    113 void Transform::RotateAboutZAxis(double degrees) {
    114   double radians = degrees * M_PI / 180;
    115   double cosTheta = std::cos(radians);
    116   double sinTheta = std::sin(radians);
    117   if (matrix_.isIdentity()) {
    118       matrix_.set3x3(cosTheta, sinTheta, 0,
    119                      -sinTheta, cosTheta, 0,
    120                      0, 0, 1);
    121   } else {
    122     SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
    123     rot.set3x3(cosTheta, sinTheta, 0,
    124                -sinTheta, cosTheta, 0,
    125                0, 0, 1);
    126     matrix_.preConcat(rot);
    127   }
    128 }
    129 
    130 void Transform::RotateAbout(const Vector3dF& axis, double degrees) {
    131   if (matrix_.isIdentity()) {
    132     matrix_.setRotateDegreesAbout(SkDoubleToMScalar(axis.x()),
    133                                   SkDoubleToMScalar(axis.y()),
    134                                   SkDoubleToMScalar(axis.z()),
    135                                   SkDoubleToMScalar(degrees));
    136   } else {
    137     SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
    138     rot.setRotateDegreesAbout(SkDoubleToMScalar(axis.x()),
    139                               SkDoubleToMScalar(axis.y()),
    140                               SkDoubleToMScalar(axis.z()),
    141                               SkDoubleToMScalar(degrees));
    142     matrix_.preConcat(rot);
    143   }
    144 }
    145 
    146 void Transform::Scale(double x, double y) {
    147   matrix_.preScale(SkDoubleToMScalar(x), SkDoubleToMScalar(y), 1);
    148 }
    149 
    150 void Transform::Scale3d(double x, double y, double z) {
    151   matrix_.preScale(SkDoubleToMScalar(x),
    152                    SkDoubleToMScalar(y),
    153                    SkDoubleToMScalar(z));
    154 }
    155 
    156 void Transform::Translate(double x, double y) {
    157   matrix_.preTranslate(SkDoubleToMScalar(x), SkDoubleToMScalar(y), 0);
    158 }
    159 
    160 void Transform::Translate3d(double x, double y, double z) {
    161   matrix_.preTranslate(SkDoubleToMScalar(x),
    162                        SkDoubleToMScalar(y),
    163                        SkDoubleToMScalar(z));
    164 }
    165 
    166 void Transform::SkewX(double angle_x) {
    167   if (matrix_.isIdentity())
    168     matrix_.setDouble(0, 1, TanDegrees(angle_x));
    169   else {
    170     SkMatrix44 skew(SkMatrix44::kIdentity_Constructor);
    171     skew.setDouble(0, 1, TanDegrees(angle_x));
    172     matrix_.preConcat(skew);
    173   }
    174 }
    175 
    176 void Transform::SkewY(double angle_y) {
    177   if (matrix_.isIdentity())
    178     matrix_.setDouble(1, 0, TanDegrees(angle_y));
    179   else {
    180     SkMatrix44 skew(SkMatrix44::kIdentity_Constructor);
    181     skew.setDouble(1, 0, TanDegrees(angle_y));
    182     matrix_.preConcat(skew);
    183   }
    184 }
    185 
    186 void Transform::ApplyPerspectiveDepth(double depth) {
    187   if (depth == 0)
    188     return;
    189   if (matrix_.isIdentity())
    190     matrix_.setDouble(3, 2, -1.0 / depth);
    191   else {
    192     SkMatrix44 m(SkMatrix44::kIdentity_Constructor);
    193     m.setDouble(3, 2, -1.0 / depth);
    194     matrix_.preConcat(m);
    195   }
    196 }
    197 
    198 void Transform::PreconcatTransform(const Transform& transform) {
    199   matrix_.preConcat(transform.matrix_);
    200 }
    201 
    202 void Transform::ConcatTransform(const Transform& transform) {
    203   matrix_.postConcat(transform.matrix_);
    204 }
    205 
    206 bool Transform::IsIdentityOrIntegerTranslation() const {
    207   if (!IsIdentityOrTranslation())
    208     return false;
    209 
    210   bool no_fractional_translation =
    211       static_cast<int>(matrix_.getDouble(0, 3)) == matrix_.getDouble(0, 3) &&
    212       static_cast<int>(matrix_.getDouble(1, 3)) == matrix_.getDouble(1, 3) &&
    213       static_cast<int>(matrix_.getDouble(2, 3)) == matrix_.getDouble(2, 3);
    214 
    215   return no_fractional_translation;
    216 }
    217 
    218 bool Transform::IsBackFaceVisible() const {
    219   // Compute whether a layer with a forward-facing normal of (0, 0, 1, 0)
    220   // would have its back face visible after applying the transform.
    221   if (matrix_.isIdentity())
    222     return false;
    223 
    224   // This is done by transforming the normal and seeing if the resulting z
    225   // value is positive or negative. However, note that transforming a normal
    226   // actually requires using the inverse-transpose of the original transform.
    227   //
    228   // We can avoid inverting and transposing the matrix since we know we want
    229   // to transform only the specific normal vector (0, 0, 1, 0). In this case,
    230   // we only need the 3rd row, 3rd column of the inverse-transpose. We can
    231   // calculate only the 3rd row 3rd column element of the inverse, skipping
    232   // everything else.
    233   //
    234   // For more information, refer to:
    235   //   http://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution
    236   //
    237 
    238   double determinant = matrix_.determinant();
    239 
    240   // If matrix was not invertible, then just assume back face is not visible.
    241   if (std::abs(determinant) <= kEpsilon)
    242     return false;
    243 
    244   // Compute the cofactor of the 3rd row, 3rd column.
    245   double cofactor_part_1 =
    246       matrix_.getDouble(0, 0) *
    247       matrix_.getDouble(1, 1) *
    248       matrix_.getDouble(3, 3);
    249 
    250   double cofactor_part_2 =
    251       matrix_.getDouble(0, 1) *
    252       matrix_.getDouble(1, 3) *
    253       matrix_.getDouble(3, 0);
    254 
    255   double cofactor_part_3 =
    256       matrix_.getDouble(0, 3) *
    257       matrix_.getDouble(1, 0) *
    258       matrix_.getDouble(3, 1);
    259 
    260   double cofactor_part_4 =
    261       matrix_.getDouble(0, 0) *
    262       matrix_.getDouble(1, 3) *
    263       matrix_.getDouble(3, 1);
    264 
    265   double cofactor_part_5 =
    266       matrix_.getDouble(0, 1) *
    267       matrix_.getDouble(1, 0) *
    268       matrix_.getDouble(3, 3);
    269 
    270   double cofactor_part_6 =
    271       matrix_.getDouble(0, 3) *
    272       matrix_.getDouble(1, 1) *
    273       matrix_.getDouble(3, 0);
    274 
    275   double cofactor33 =
    276       cofactor_part_1 +
    277       cofactor_part_2 +
    278       cofactor_part_3 -
    279       cofactor_part_4 -
    280       cofactor_part_5 -
    281       cofactor_part_6;
    282 
    283   // Technically the transformed z component is cofactor33 / determinant.  But
    284   // we can avoid the costly division because we only care about the resulting
    285   // +/- sign; we can check this equivalently by multiplication.
    286   return cofactor33 * determinant < 0;
    287 }
    288 
    289 bool Transform::GetInverse(Transform* transform) const {
    290   if (!matrix_.invert(&transform->matrix_)) {
    291     // Initialize the return value to identity if this matrix turned
    292     // out to be un-invertible.
    293     transform->MakeIdentity();
    294     return false;
    295   }
    296 
    297   return true;
    298 }
    299 
    300 bool Transform::Preserves2dAxisAlignment() const {
    301   // Check whether an axis aligned 2-dimensional rect would remain axis-aligned
    302   // after being transformed by this matrix (and implicitly projected by
    303   // dropping any non-zero z-values).
    304   //
    305   // The 4th column can be ignored because translations don't affect axis
    306   // alignment. The 3rd column can be ignored because we are assuming 2d
    307   // inputs, where z-values will be zero. The 3rd row can also be ignored
    308   // because we are assuming 2d outputs, and any resulting z-value is dropped
    309   // anyway. For the inner 2x2 portion, the only effects that keep a rect axis
    310   // aligned are (1) swapping axes and (2) scaling axes. This can be checked by
    311   // verifying only 1 element of every column and row is non-zero.  Degenerate
    312   // cases that project the x or y dimension to zero are considered to preserve
    313   // axis alignment.
    314   //
    315   // If the matrix does have perspective component that is affected by x or y
    316   // values: The current implementation conservatively assumes that axis
    317   // alignment is not preserved.
    318 
    319   bool has_x_or_y_perspective = matrix_.getDouble(3, 0) != 0 ||
    320       matrix_.getDouble(3, 1) != 0;
    321 
    322   int num_non_zero_in_row_0 = 0;
    323   int num_non_zero_in_row_1 = 0;
    324   int num_non_zero_in_col_0 = 0;
    325   int num_non_zero_in_col_1 = 0;
    326 
    327   if (std::abs(matrix_.getDouble(0, 0)) > kEpsilon) {
    328     num_non_zero_in_row_0++;
    329     num_non_zero_in_col_0++;
    330   }
    331 
    332   if (std::abs(matrix_.getDouble(0, 1)) > kEpsilon) {
    333     num_non_zero_in_row_0++;
    334     num_non_zero_in_col_1++;
    335   }
    336 
    337   if (std::abs(matrix_.getDouble(1, 0)) > kEpsilon) {
    338     num_non_zero_in_row_1++;
    339     num_non_zero_in_col_0++;
    340   }
    341 
    342   if (std::abs(matrix_.getDouble(1, 1)) > kEpsilon) {
    343     num_non_zero_in_row_1++;
    344     num_non_zero_in_col_1++;
    345   }
    346 
    347   return
    348       num_non_zero_in_row_0 <= 1 &&
    349       num_non_zero_in_row_1 <= 1 &&
    350       num_non_zero_in_col_0 <= 1 &&
    351       num_non_zero_in_col_1 <= 1 &&
    352       !has_x_or_y_perspective;
    353 }
    354 
    355 void Transform::Transpose() {
    356   matrix_.transpose();
    357 }
    358 
    359 void Transform::FlattenTo2d() {
    360   matrix_.setDouble(2, 0, 0.0);
    361   matrix_.setDouble(2, 1, 0.0);
    362   matrix_.setDouble(0, 2, 0.0);
    363   matrix_.setDouble(1, 2, 0.0);
    364   matrix_.setDouble(2, 2, 1.0);
    365   matrix_.setDouble(3, 2, 0.0);
    366   matrix_.setDouble(2, 3, 0.0);
    367 }
    368 
    369 void Transform::TransformPoint(Point& point) const {
    370   TransformPointInternal(matrix_, point);
    371 }
    372 
    373 void Transform::TransformPoint(Point3F& point) const {
    374   TransformPointInternal(matrix_, point);
    375 }
    376 
    377 bool Transform::TransformPointReverse(Point& point) const {
    378   // TODO(sad): Try to avoid trying to invert the matrix.
    379   SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
    380   if (!matrix_.invert(&inverse))
    381     return false;
    382 
    383   TransformPointInternal(inverse, point);
    384   return true;
    385 }
    386 
    387 bool Transform::TransformPointReverse(Point3F& point) const {
    388   // TODO(sad): Try to avoid trying to invert the matrix.
    389   SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
    390   if (!matrix_.invert(&inverse))
    391     return false;
    392 
    393   TransformPointInternal(inverse, point);
    394   return true;
    395 }
    396 
    397 void Transform::TransformRect(RectF* rect) const {
    398   if (matrix_.isIdentity())
    399     return;
    400 
    401   SkRect src = RectFToSkRect(*rect);
    402   const SkMatrix& matrix = matrix_;
    403   matrix.mapRect(&src);
    404   *rect = SkRectToRectF(src);
    405 }
    406 
    407 bool Transform::TransformRectReverse(RectF* rect) const {
    408   if (matrix_.isIdentity())
    409     return true;
    410 
    411   SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
    412   if (!matrix_.invert(&inverse))
    413     return false;
    414 
    415   const SkMatrix& matrix = inverse;
    416   SkRect src = RectFToSkRect(*rect);
    417   matrix.mapRect(&src);
    418   *rect = SkRectToRectF(src);
    419   return true;
    420 }
    421 
    422 bool Transform::Blend(const Transform& from, double progress) {
    423   DecomposedTransform to_decomp;
    424   DecomposedTransform from_decomp;
    425   if (!DecomposeTransform(&to_decomp, *this) ||
    426       !DecomposeTransform(&from_decomp, from))
    427     return false;
    428 
    429   if (!BlendDecomposedTransforms(&to_decomp, to_decomp, from_decomp, progress))
    430     return false;
    431 
    432   matrix_ = ComposeTransform(to_decomp).matrix();
    433   return true;
    434 }
    435 
    436 void Transform::TransformPointInternal(const SkMatrix44& xform,
    437                                        Point3F& point) const {
    438   if (xform.isIdentity())
    439     return;
    440 
    441   SkMScalar p[4] = {
    442     SkDoubleToMScalar(point.x()),
    443     SkDoubleToMScalar(point.y()),
    444     SkDoubleToMScalar(point.z()),
    445     SkDoubleToMScalar(1)
    446   };
    447 
    448   xform.mapMScalars(p);
    449 
    450   if (p[3] != 1 && abs(p[3]) > 0) {
    451     point.SetPoint(p[0] / p[3], p[1] / p[3], p[2]/ p[3]);
    452   } else {
    453     point.SetPoint(p[0], p[1], p[2]);
    454   }
    455 }
    456 
    457 void Transform::TransformPointInternal(const SkMatrix44& xform,
    458                                        Point& point) const {
    459   if (xform.isIdentity())
    460     return;
    461 
    462   SkMScalar p[4] = {
    463     SkDoubleToMScalar(point.x()),
    464     SkDoubleToMScalar(point.y()),
    465     SkDoubleToMScalar(0),
    466     SkDoubleToMScalar(1)
    467   };
    468 
    469   xform.mapMScalars(p);
    470 
    471   point.SetPoint(ToRoundedInt(p[0]), ToRoundedInt(p[1]));
    472 }
    473 
    474 std::string Transform::ToString() const {
    475   return base::StringPrintf(
    476       "[ %+0.4f %+0.4f %+0.4f %+0.4f  \n"
    477       "  %+0.4f %+0.4f %+0.4f %+0.4f  \n"
    478       "  %+0.4f %+0.4f %+0.4f %+0.4f  \n"
    479       "  %+0.4f %+0.4f %+0.4f %+0.4f ]\n",
    480       matrix_.getDouble(0, 0),
    481       matrix_.getDouble(0, 1),
    482       matrix_.getDouble(0, 2),
    483       matrix_.getDouble(0, 3),
    484       matrix_.getDouble(1, 0),
    485       matrix_.getDouble(1, 1),
    486       matrix_.getDouble(1, 2),
    487       matrix_.getDouble(1, 3),
    488       matrix_.getDouble(2, 0),
    489       matrix_.getDouble(2, 1),
    490       matrix_.getDouble(2, 2),
    491       matrix_.getDouble(2, 3),
    492       matrix_.getDouble(3, 0),
    493       matrix_.getDouble(3, 1),
    494       matrix_.getDouble(3, 2),
    495       matrix_.getDouble(3, 3));
    496 }
    497 
    498 }  // namespace gfx
    499