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