summaryrefslogtreecommitdiff
path: root/chromium/ui/gfx/transform.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/ui/gfx/transform.cc')
-rw-r--r--chromium/ui/gfx/transform.cc507
1 files changed, 507 insertions, 0 deletions
diff --git a/chromium/ui/gfx/transform.cc b/chromium/ui/gfx/transform.cc
new file mode 100644
index 00000000000..3e94f44e21c
--- /dev/null
+++ b/chromium/ui/gfx/transform.cc
@@ -0,0 +1,507 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// MSVC++ requires this to be set before any other includes to get M_PI.
+#define _USE_MATH_DEFINES
+
+#include "ui/gfx/transform.h"
+
+#include <cmath>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "ui/gfx/point.h"
+#include "ui/gfx/point3_f.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/safe_integer_conversions.h"
+#include "ui/gfx/skia_util.h"
+#include "ui/gfx/transform_util.h"
+#include "ui/gfx/vector3d_f.h"
+
+namespace gfx {
+
+namespace {
+
+// Taken from SkMatrix44.
+const double kEpsilon = 1e-8;
+
+double TanDegrees(double degrees) {
+ double radians = degrees * M_PI / 180;
+ return std::tan(radians);
+}
+
+} // namespace
+
+Transform::Transform(
+ double col1row1, double col2row1, double col3row1, double col4row1,
+ double col1row2, double col2row2, double col3row2, double col4row2,
+ double col1row3, double col2row3, double col3row3, double col4row3,
+ double col1row4, double col2row4, double col3row4, double col4row4)
+ : matrix_(SkMatrix44::kUninitialized_Constructor)
+{
+ matrix_.setDouble(0, 0, col1row1);
+ matrix_.setDouble(1, 0, col1row2);
+ matrix_.setDouble(2, 0, col1row3);
+ matrix_.setDouble(3, 0, col1row4);
+
+ matrix_.setDouble(0, 1, col2row1);
+ matrix_.setDouble(1, 1, col2row2);
+ matrix_.setDouble(2, 1, col2row3);
+ matrix_.setDouble(3, 1, col2row4);
+
+ matrix_.setDouble(0, 2, col3row1);
+ matrix_.setDouble(1, 2, col3row2);
+ matrix_.setDouble(2, 2, col3row3);
+ matrix_.setDouble(3, 2, col3row4);
+
+ matrix_.setDouble(0, 3, col4row1);
+ matrix_.setDouble(1, 3, col4row2);
+ matrix_.setDouble(2, 3, col4row3);
+ matrix_.setDouble(3, 3, col4row4);
+}
+
+Transform::Transform(
+ double col1row1, double col2row1,
+ double col1row2, double col2row2,
+ double x_translation, double y_translation)
+ : matrix_(SkMatrix44::kIdentity_Constructor)
+{
+ matrix_.setDouble(0, 0, col1row1);
+ matrix_.setDouble(1, 0, col1row2);
+ matrix_.setDouble(0, 1, col2row1);
+ matrix_.setDouble(1, 1, col2row2);
+ matrix_.setDouble(0, 3, x_translation);
+ matrix_.setDouble(1, 3, y_translation);
+}
+
+void Transform::RotateAboutXAxis(double degrees) {
+ double radians = degrees * M_PI / 180;
+ double cosTheta = std::cos(radians);
+ double sinTheta = std::sin(radians);
+ if (matrix_.isIdentity()) {
+ matrix_.set3x3(1, 0, 0,
+ 0, cosTheta, sinTheta,
+ 0, -sinTheta, cosTheta);
+ } else {
+ SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
+ rot.set3x3(1, 0, 0,
+ 0, cosTheta, sinTheta,
+ 0, -sinTheta, cosTheta);
+ matrix_.preConcat(rot);
+ }
+}
+
+void Transform::RotateAboutYAxis(double degrees) {
+ double radians = degrees * M_PI / 180;
+ double cosTheta = std::cos(radians);
+ double sinTheta = std::sin(radians);
+ if (matrix_.isIdentity()) {
+ // Note carefully the placement of the -sinTheta for rotation about
+ // y-axis is different than rotation about x-axis or z-axis.
+ matrix_.set3x3(cosTheta, 0, -sinTheta,
+ 0, 1, 0,
+ sinTheta, 0, cosTheta);
+ } else {
+ SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
+ rot.set3x3(cosTheta, 0, -sinTheta,
+ 0, 1, 0,
+ sinTheta, 0, cosTheta);
+ matrix_.preConcat(rot);
+ }
+}
+
+void Transform::RotateAboutZAxis(double degrees) {
+ double radians = degrees * M_PI / 180;
+ double cosTheta = std::cos(radians);
+ double sinTheta = std::sin(radians);
+ if (matrix_.isIdentity()) {
+ matrix_.set3x3(cosTheta, sinTheta, 0,
+ -sinTheta, cosTheta, 0,
+ 0, 0, 1);
+ } else {
+ SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
+ rot.set3x3(cosTheta, sinTheta, 0,
+ -sinTheta, cosTheta, 0,
+ 0, 0, 1);
+ matrix_.preConcat(rot);
+ }
+}
+
+void Transform::RotateAbout(const Vector3dF& axis, double degrees) {
+ if (matrix_.isIdentity()) {
+ matrix_.setRotateDegreesAbout(SkDoubleToMScalar(axis.x()),
+ SkDoubleToMScalar(axis.y()),
+ SkDoubleToMScalar(axis.z()),
+ SkDoubleToMScalar(degrees));
+ } else {
+ SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
+ rot.setRotateDegreesAbout(SkDoubleToMScalar(axis.x()),
+ SkDoubleToMScalar(axis.y()),
+ SkDoubleToMScalar(axis.z()),
+ SkDoubleToMScalar(degrees));
+ matrix_.preConcat(rot);
+ }
+}
+
+void Transform::Scale(double x, double y) {
+ matrix_.preScale(SkDoubleToMScalar(x), SkDoubleToMScalar(y), 1);
+}
+
+void Transform::Scale3d(double x, double y, double z) {
+ matrix_.preScale(SkDoubleToMScalar(x),
+ SkDoubleToMScalar(y),
+ SkDoubleToMScalar(z));
+}
+
+void Transform::Translate(double x, double y) {
+ matrix_.preTranslate(SkDoubleToMScalar(x), SkDoubleToMScalar(y), 0);
+}
+
+void Transform::Translate3d(double x, double y, double z) {
+ matrix_.preTranslate(SkDoubleToMScalar(x),
+ SkDoubleToMScalar(y),
+ SkDoubleToMScalar(z));
+}
+
+void Transform::SkewX(double angle_x) {
+ if (matrix_.isIdentity())
+ matrix_.setDouble(0, 1, TanDegrees(angle_x));
+ else {
+ SkMatrix44 skew(SkMatrix44::kIdentity_Constructor);
+ skew.setDouble(0, 1, TanDegrees(angle_x));
+ matrix_.preConcat(skew);
+ }
+}
+
+void Transform::SkewY(double angle_y) {
+ if (matrix_.isIdentity())
+ matrix_.setDouble(1, 0, TanDegrees(angle_y));
+ else {
+ SkMatrix44 skew(SkMatrix44::kIdentity_Constructor);
+ skew.setDouble(1, 0, TanDegrees(angle_y));
+ matrix_.preConcat(skew);
+ }
+}
+
+void Transform::ApplyPerspectiveDepth(double depth) {
+ if (depth == 0)
+ return;
+ if (matrix_.isIdentity())
+ matrix_.setDouble(3, 2, -1.0 / depth);
+ else {
+ SkMatrix44 m(SkMatrix44::kIdentity_Constructor);
+ m.setDouble(3, 2, -1.0 / depth);
+ matrix_.preConcat(m);
+ }
+}
+
+void Transform::PreconcatTransform(const Transform& transform) {
+ matrix_.preConcat(transform.matrix_);
+}
+
+void Transform::ConcatTransform(const Transform& transform) {
+ matrix_.postConcat(transform.matrix_);
+}
+
+bool Transform::IsIdentityOrIntegerTranslation() const {
+ if (!IsIdentityOrTranslation())
+ return false;
+
+ bool no_fractional_translation =
+ static_cast<int>(matrix_.getDouble(0, 3)) == matrix_.getDouble(0, 3) &&
+ static_cast<int>(matrix_.getDouble(1, 3)) == matrix_.getDouble(1, 3) &&
+ static_cast<int>(matrix_.getDouble(2, 3)) == matrix_.getDouble(2, 3);
+
+ return no_fractional_translation;
+}
+
+bool Transform::IsBackFaceVisible() const {
+ // Compute whether a layer with a forward-facing normal of (0, 0, 1, 0)
+ // would have its back face visible after applying the transform.
+ if (matrix_.isIdentity())
+ return false;
+
+ // This is done by transforming the normal and seeing if the resulting z
+ // value is positive or negative. However, note that transforming a normal
+ // actually requires using the inverse-transpose of the original transform.
+ //
+ // We can avoid inverting and transposing the matrix since we know we want
+ // to transform only the specific normal vector (0, 0, 1, 0). In this case,
+ // we only need the 3rd row, 3rd column of the inverse-transpose. We can
+ // calculate only the 3rd row 3rd column element of the inverse, skipping
+ // everything else.
+ //
+ // For more information, refer to:
+ // http://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution
+ //
+
+ double determinant = matrix_.determinant();
+
+ // If matrix was not invertible, then just assume back face is not visible.
+ if (std::abs(determinant) <= kEpsilon)
+ return false;
+
+ // Compute the cofactor of the 3rd row, 3rd column.
+ double cofactor_part_1 =
+ matrix_.getDouble(0, 0) *
+ matrix_.getDouble(1, 1) *
+ matrix_.getDouble(3, 3);
+
+ double cofactor_part_2 =
+ matrix_.getDouble(0, 1) *
+ matrix_.getDouble(1, 3) *
+ matrix_.getDouble(3, 0);
+
+ double cofactor_part_3 =
+ matrix_.getDouble(0, 3) *
+ matrix_.getDouble(1, 0) *
+ matrix_.getDouble(3, 1);
+
+ double cofactor_part_4 =
+ matrix_.getDouble(0, 0) *
+ matrix_.getDouble(1, 3) *
+ matrix_.getDouble(3, 1);
+
+ double cofactor_part_5 =
+ matrix_.getDouble(0, 1) *
+ matrix_.getDouble(1, 0) *
+ matrix_.getDouble(3, 3);
+
+ double cofactor_part_6 =
+ matrix_.getDouble(0, 3) *
+ matrix_.getDouble(1, 1) *
+ matrix_.getDouble(3, 0);
+
+ double cofactor33 =
+ cofactor_part_1 +
+ cofactor_part_2 +
+ cofactor_part_3 -
+ cofactor_part_4 -
+ cofactor_part_5 -
+ cofactor_part_6;
+
+ // Technically the transformed z component is cofactor33 / determinant. But
+ // we can avoid the costly division because we only care about the resulting
+ // +/- sign; we can check this equivalently by multiplication.
+ return cofactor33 * determinant < 0;
+}
+
+bool Transform::GetInverse(Transform* transform) const {
+ if (!matrix_.invert(&transform->matrix_)) {
+ // Initialize the return value to identity if this matrix turned
+ // out to be un-invertible.
+ transform->MakeIdentity();
+ return false;
+ }
+
+ return true;
+}
+
+bool Transform::Preserves2dAxisAlignment() const {
+ // Check whether an axis aligned 2-dimensional rect would remain axis-aligned
+ // after being transformed by this matrix (and implicitly projected by
+ // dropping any non-zero z-values).
+ //
+ // The 4th column can be ignored because translations don't affect axis
+ // alignment. The 3rd column can be ignored because we are assuming 2d
+ // inputs, where z-values will be zero. The 3rd row can also be ignored
+ // because we are assuming 2d outputs, and any resulting z-value is dropped
+ // anyway. For the inner 2x2 portion, the only effects that keep a rect axis
+ // aligned are (1) swapping axes and (2) scaling axes. This can be checked by
+ // verifying only 1 element of every column and row is non-zero. Degenerate
+ // cases that project the x or y dimension to zero are considered to preserve
+ // axis alignment.
+ //
+ // If the matrix does have perspective component that is affected by x or y
+ // values: The current implementation conservatively assumes that axis
+ // alignment is not preserved.
+
+ bool has_x_or_y_perspective = matrix_.getDouble(3, 0) != 0 ||
+ matrix_.getDouble(3, 1) != 0;
+
+ int num_non_zero_in_row_0 = 0;
+ int num_non_zero_in_row_1 = 0;
+ int num_non_zero_in_col_0 = 0;
+ int num_non_zero_in_col_1 = 0;
+
+ if (std::abs(matrix_.getDouble(0, 0)) > kEpsilon) {
+ num_non_zero_in_row_0++;
+ num_non_zero_in_col_0++;
+ }
+
+ if (std::abs(matrix_.getDouble(0, 1)) > kEpsilon) {
+ num_non_zero_in_row_0++;
+ num_non_zero_in_col_1++;
+ }
+
+ if (std::abs(matrix_.getDouble(1, 0)) > kEpsilon) {
+ num_non_zero_in_row_1++;
+ num_non_zero_in_col_0++;
+ }
+
+ if (std::abs(matrix_.getDouble(1, 1)) > kEpsilon) {
+ num_non_zero_in_row_1++;
+ num_non_zero_in_col_1++;
+ }
+
+ return
+ num_non_zero_in_row_0 <= 1 &&
+ num_non_zero_in_row_1 <= 1 &&
+ num_non_zero_in_col_0 <= 1 &&
+ num_non_zero_in_col_1 <= 1 &&
+ !has_x_or_y_perspective;
+}
+
+void Transform::Transpose() {
+ matrix_.transpose();
+}
+
+void Transform::FlattenTo2d() {
+ matrix_.setDouble(2, 0, 0.0);
+ matrix_.setDouble(2, 1, 0.0);
+ matrix_.setDouble(0, 2, 0.0);
+ matrix_.setDouble(1, 2, 0.0);
+ matrix_.setDouble(2, 2, 1.0);
+ matrix_.setDouble(3, 2, 0.0);
+ matrix_.setDouble(2, 3, 0.0);
+}
+
+Vector2dF Transform::To2dTranslation() const {
+ DCHECK(IsIdentityOrTranslation());
+ // Ensure that this translation is truly 2d.
+ const double translate_z = matrix_.getDouble(2, 3);
+ DCHECK_EQ(0.0, translate_z);
+ return gfx::Vector2dF(matrix_.getDouble(0, 3), matrix_.getDouble(1, 3));
+}
+
+void Transform::TransformPoint(Point& point) const {
+ TransformPointInternal(matrix_, point);
+}
+
+void Transform::TransformPoint(Point3F& point) const {
+ TransformPointInternal(matrix_, point);
+}
+
+bool Transform::TransformPointReverse(Point& point) const {
+ // TODO(sad): Try to avoid trying to invert the matrix.
+ SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
+ if (!matrix_.invert(&inverse))
+ return false;
+
+ TransformPointInternal(inverse, point);
+ return true;
+}
+
+bool Transform::TransformPointReverse(Point3F& point) const {
+ // TODO(sad): Try to avoid trying to invert the matrix.
+ SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
+ if (!matrix_.invert(&inverse))
+ return false;
+
+ TransformPointInternal(inverse, point);
+ return true;
+}
+
+void Transform::TransformRect(RectF* rect) const {
+ if (matrix_.isIdentity())
+ return;
+
+ SkRect src = RectFToSkRect(*rect);
+ const SkMatrix& matrix = matrix_;
+ matrix.mapRect(&src);
+ *rect = SkRectToRectF(src);
+}
+
+bool Transform::TransformRectReverse(RectF* rect) const {
+ if (matrix_.isIdentity())
+ return true;
+
+ SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
+ if (!matrix_.invert(&inverse))
+ return false;
+
+ const SkMatrix& matrix = inverse;
+ SkRect src = RectFToSkRect(*rect);
+ matrix.mapRect(&src);
+ *rect = SkRectToRectF(src);
+ return true;
+}
+
+bool Transform::Blend(const Transform& from, double progress) {
+ DecomposedTransform to_decomp;
+ DecomposedTransform from_decomp;
+ if (!DecomposeTransform(&to_decomp, *this) ||
+ !DecomposeTransform(&from_decomp, from))
+ return false;
+
+ if (!BlendDecomposedTransforms(&to_decomp, to_decomp, from_decomp, progress))
+ return false;
+
+ matrix_ = ComposeTransform(to_decomp).matrix();
+ return true;
+}
+
+void Transform::TransformPointInternal(const SkMatrix44& xform,
+ Point3F& point) const {
+ if (xform.isIdentity())
+ return;
+
+ SkMScalar p[4] = {
+ SkDoubleToMScalar(point.x()),
+ SkDoubleToMScalar(point.y()),
+ SkDoubleToMScalar(point.z()),
+ SkDoubleToMScalar(1)
+ };
+
+ xform.mapMScalars(p);
+
+ if (p[3] != 1 && abs(p[3]) > 0) {
+ point.SetPoint(p[0] / p[3], p[1] / p[3], p[2]/ p[3]);
+ } else {
+ point.SetPoint(p[0], p[1], p[2]);
+ }
+}
+
+void Transform::TransformPointInternal(const SkMatrix44& xform,
+ Point& point) const {
+ if (xform.isIdentity())
+ return;
+
+ SkMScalar p[4] = {
+ SkDoubleToMScalar(point.x()),
+ SkDoubleToMScalar(point.y()),
+ SkDoubleToMScalar(0),
+ SkDoubleToMScalar(1)
+ };
+
+ xform.mapMScalars(p);
+
+ point.SetPoint(ToRoundedInt(p[0]), ToRoundedInt(p[1]));
+}
+
+std::string Transform::ToString() const {
+ return base::StringPrintf(
+ "[ %+0.4f %+0.4f %+0.4f %+0.4f \n"
+ " %+0.4f %+0.4f %+0.4f %+0.4f \n"
+ " %+0.4f %+0.4f %+0.4f %+0.4f \n"
+ " %+0.4f %+0.4f %+0.4f %+0.4f ]\n",
+ matrix_.getDouble(0, 0),
+ matrix_.getDouble(0, 1),
+ matrix_.getDouble(0, 2),
+ matrix_.getDouble(0, 3),
+ matrix_.getDouble(1, 0),
+ matrix_.getDouble(1, 1),
+ matrix_.getDouble(1, 2),
+ matrix_.getDouble(1, 3),
+ matrix_.getDouble(2, 0),
+ matrix_.getDouble(2, 1),
+ matrix_.getDouble(2, 2),
+ matrix_.getDouble(2, 3),
+ matrix_.getDouble(3, 0),
+ matrix_.getDouble(3, 1),
+ matrix_.getDouble(3, 2),
+ matrix_.getDouble(3, 3));
+}
+
+} // namespace gfx