Index: ui/gfx/transform.cc |
diff --git a/ui/gfx/transform.cc b/ui/gfx/transform.cc |
index 33946cba67f07a79f9330b3dd1c7eea009ce6d7d..574d85a3dc1628889fb478d84b72e330e072ca0b 100644 |
--- a/ui/gfx/transform.cc |
+++ b/ui/gfx/transform.cc |
@@ -258,60 +258,76 @@ bool Transform::IsIdentityOrIntegerTranslation() const { |
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. |
+ // Case 1: Shortcut if simple translate and scale. |
+ if (!(matrix_.getType() & SkMatrix44::kAffine_Mask)) |
+ return matrix_.get(2, 2) < 0; |
+ |
+ // Case 2: Determine back face in the projected space. |
// |
- // 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. |
+ // This is done by projecting an arbitrary vector that points to the |
+ // front side of the plane. For simplicity we can use (0, 0, 1, 0). |
+ // Note that this projected vector will no longer be the normal vector |
+ // of the projected plane. To avoid confusion, it will be referred as |
+ // the front-face indicator. |
// |
- // For more information, refer to: |
- // http://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution |
+ // Then we find the normal vector of the projected plane. This vector |
+ // is not normalized and does not indicate the face direction. Compute |
+ // the dot product of the front-face indicator by the normal vector to |
+ // determine which face is the front face. |
// |
- |
- double determinant = matrix_.determinant(); |
- |
- // If matrix was not invertible, then just assume back face is not visible. |
- if (std::abs(determinant) <= kEpsilon) |
+ // The face that faces the half-space where the origin resides is visible. |
+ |
+ // Compute the normal vector of the projected plane. |
+ double normal_x = matrix_.get(1, 0) * matrix_.get(3, 1) - |
+ matrix_.get(3, 0) * matrix_.get(1, 1); |
+ double normal_y = matrix_.get(3, 0) * matrix_.get(0, 1) - |
+ matrix_.get(0, 0) * matrix_.get(3, 1); |
+ double normal_w = matrix_.get(0, 0) * matrix_.get(1, 1) - |
+ matrix_.get(1, 0) * matrix_.get(0, 1); |
+ |
+ // Compute the determinant to determine which half-space the origin resides. |
+ // Note that the origin resides in the half-space of the opposite sign. |
+ double determinant = normal_x * matrix_.get(0, 3) + |
+ normal_y * matrix_.get(1, 3) + |
+ normal_w * matrix_.get(3, 3); |
+ |
+ // The origin coplanes the projected plane or the plane is degenerate. |
+ if (!determinant) |
return false; |
- // Compute the cofactor of the 3rd row, 3rd column. |
- double cofactor_part_1 = |
- matrix_.get(0, 0) * matrix_.get(1, 1) * matrix_.get(3, 3); |
- |
- double cofactor_part_2 = |
- matrix_.get(0, 1) * matrix_.get(1, 3) * matrix_.get(3, 0); |
+ // Compute the dot product of the normal vector by the front-face indicator. |
+ double face_direction = normal_x * matrix_.get(0, 2) + |
+ normal_y * matrix_.get(1, 2) + |
+ normal_w * matrix_.get(3, 2); |
- double cofactor_part_3 = |
- matrix_.get(0, 3) * matrix_.get(1, 0) * matrix_.get(3, 1); |
+ // We are seeing the back face if the front face faces the opposite |
+ // half-space the origin resides. |
+ if (face_direction) |
+ return determinant * face_direction > 0; |
- double cofactor_part_4 = |
- matrix_.get(0, 0) * matrix_.get(1, 3) * matrix_.get(3, 1); |
- |
- double cofactor_part_5 = |
- matrix_.get(0, 1) * matrix_.get(1, 0) * matrix_.get(3, 3); |
- |
- double cofactor_part_6 = |
- matrix_.get(0, 3) * matrix_.get(1, 1) * matrix_.get(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; |
+ // Case 3: Front-face indicator is 0. |
+ // |
+ // This can happen if the z component does not participate the projection, |
+ // which means the original space gets flattened there is no front or back. |
+ // A notable example is no perspective (= infinite perspective). |
+ // |
+ // Try again as if a perspective that approaches infinite is applied. |
+ // | 1 0 0 0 | |
+ // lim | 0 1 0 0 | |
+ // e->+0 | 0 0 1 0 | |
+ // | 0 0 -e 1 | |
+ |
+ double normal_x_epsilon = matrix_.get(1, 0) * -matrix_.get(2, 1) - |
+ -matrix_.get(2, 0) * matrix_.get(1, 1); |
+ double normal_y_epsilon = -matrix_.get(2, 0) * matrix_.get(0, 1) - |
+ matrix_.get(0, 0) * -matrix_.get(2, 1); |
+ |
+ face_direction = normal_x_epsilon * matrix_.get(0, 2) + |
+ normal_y_epsilon * matrix_.get(1, 2) + |
+ normal_w * -matrix_.get(2, 2); |
+ |
+ return determinant * face_direction > 0; |
} |
bool Transform::GetInverse(Transform* transform) const { |