Index: cc/trees/property_tree.cc |
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc |
index 8e8ef50cfedcc200c6c53c75a900ee31ae90d548..544755b211625cf9ab73cfdbbc88b9d28bca0333 100644 |
--- a/cc/trees/property_tree.cc |
+++ b/cc/trees/property_tree.cc |
@@ -3,6 +3,7 @@ |
// found in the LICENSE file. |
#include <set> |
+#include <vector> |
#include "base/logging.h" |
#include "cc/base/math_util.h" |
@@ -44,7 +45,7 @@ TransformNodeData::TransformNodeData() |
is_animated(false), |
to_screen_is_animated(false), |
flattens_inherited_transform(false), |
- flattens_local_transform(false), |
+ node_and_ancestors_are_flat(true), |
scrolls(false), |
needs_sublayer_scale(false), |
layer_scale_factor(1.0f) { |
@@ -135,7 +136,18 @@ bool TransformTree::CombineTransformsBetween(int source_id, |
gfx::Transform* transform) const { |
const TransformNode* current = Node(source_id); |
const TransformNode* dest = Node(dest_id); |
- if (!dest || dest->data.ancestors_are_invertible) { |
+ // Combine transforms to and from the screen when possible. Since flattening |
+ // is a non-linear operation, we cannot use this approach when there is |
+ // non-trivial flattening between the source and destination nodes. For |
+ // example, consider the tree R->A->B->C, where B flattens its inherited |
+ // transform, and A has a non-flat transform. Suppose C is the source and A is |
+ // the destination. The expected result is C * B. But C's to_screen |
+ // transform is C * B * flattened(A * R), and A's from_screen transform is |
+ // R^{-1} * A^{-1}. If at least one of A and R isn't flat, the inverse of |
+ // flattened(A * R) won't be R^{-1} * A{-1}, so multiplying C's to_screen and |
+ // A's from_screen will not produce the correct result. |
+ if (!dest || (dest->data.ancestors_are_invertible && |
+ current->data.node_and_ancestors_are_flat)) { |
transform->ConcatTransform(current->data.to_screen); |
if (dest) |
transform->ConcatTransform(dest->data.from_screen); |
@@ -143,12 +155,43 @@ bool TransformTree::CombineTransformsBetween(int source_id, |
} |
bool all_are_invertible = true; |
+ |
+ // Flattening is defined in a way that requires it to be applied while |
+ // traversing downward in the tree. We first identify nodes that are on the |
+ // path from the source to the destination (this is traversing upward), and |
+ // then we visit these nodes in reverse order, flattening as needed. We |
+ // early-out if we get to a node whose target node is the destination, since |
+ // we can then re-use the target space transform stored at that node. |
+ std::vector<int> source_to_destination; |
+ source_to_destination.push_back(current->id); |
+ current = parent(current); |
for (; current && current->id > dest_id; current = parent(current)) { |
- transform->ConcatTransform(current->data.to_parent); |
- if (!current->data.is_invertible) |
+ if (current->data.target_id == dest_id && |
+ current->data.content_target_id == dest_id) |
+ break; |
+ source_to_destination.push_back(current->id); |
+ } |
+ |
+ gfx::Transform combined_transform; |
+ if (current->id > dest_id) { |
+ combined_transform = current->data.to_target; |
+ // The stored target space transform has sublayer scale baked in, but we |
+ // need the unscaled transform. |
+ combined_transform.Scale(1.0f / dest->data.sublayer_scale.x(), |
+ 1.0f / dest->data.sublayer_scale.y()); |
+ } |
+ |
+ for (int i = source_to_destination.size() - 1; i >= 0; i--) { |
+ const TransformNode* node = Node(source_to_destination[i]); |
+ if (node->data.flattens_inherited_transform) |
+ combined_transform.FlattenTo2d(); |
+ combined_transform.PreconcatTransform(node->data.to_parent); |
+ |
+ if (!node->data.is_invertible) |
all_are_invertible = false; |
} |
+ transform->ConcatTransform(combined_transform); |
return all_are_invertible; |
} |
@@ -157,20 +200,27 @@ bool TransformTree::CombineInversesBetween(int source_id, |
gfx::Transform* transform) const { |
const TransformNode* current = Node(dest_id); |
const TransformNode* dest = Node(source_id); |
- if (current->data.ancestors_are_invertible) { |
+ // Just as in CombineTransformsBetween, we can use screen space transforms in |
+ // this computation only when there isn't any non-trivial flattening |
+ // involved. |
+ if (current->data.ancestors_are_invertible && |
+ current->data.node_and_ancestors_are_flat) { |
transform->PreconcatTransform(current->data.from_screen); |
if (dest) |
transform->PreconcatTransform(dest->data.to_screen); |
return true; |
} |
- bool all_are_invertible = true; |
- for (; current && current->id > source_id; current = parent(current)) { |
- transform->PreconcatTransform(current->data.from_parent); |
- if (!current->data.is_invertible) |
- all_are_invertible = false; |
- } |
- |
+ // Inverting a flattening is not equivalent to flattening an inverse. This |
+ // means we cannot, for example, use the inverse of each node's to_parent |
+ // transform, flattening where needed. Instead, we must compute the transform |
+ // from the destination to the source, with flattening, and then invert the |
+ // result. |
+ gfx::Transform dest_to_source; |
+ CombineTransformsBetween(dest_id, source_id, &dest_to_source); |
+ gfx::Transform source_to_dest; |
+ bool all_are_invertible = dest_to_source.GetInverse(&source_to_dest); |
+ transform->PreconcatTransform(source_to_dest); |
return all_are_invertible; |
} |
@@ -191,28 +241,17 @@ void TransformTree::UpdateScreenSpaceTransform(TransformNode* node, |
node->data.to_screen = node->data.to_parent; |
node->data.ancestors_are_invertible = true; |
node->data.to_screen_is_animated = false; |
- } else if (parent_node->data.flattens_local_transform || |
- node->data.flattens_inherited_transform) { |
- // Flattening is tricky. Once a layer is drawn into its render target, it |
- // cannot escape, so we only need to consider transforms between the layer |
- // and its target when flattening (i.e., its draw transform). To compute the |
- // screen space transform when flattening is involved we combine three |
- // transforms, A * B * C, where A is the screen space transform of the |
- // target, B is the flattened draw transform of the layer's parent, and C is |
- // the local transform. |
- node->data.to_screen = target_node->data.to_screen; |
- gfx::Transform flattened; |
- ComputeTransform(parent_node->id, target_node->id, &flattened); |
- flattened.FlattenTo2d(); |
- node->data.to_screen.PreconcatTransform(flattened); |
- node->data.to_screen.PreconcatTransform(node->data.to_parent); |
- node->data.ancestors_are_invertible = |
- parent_node->data.ancestors_are_invertible; |
+ node->data.node_and_ancestors_are_flat = node->data.to_parent.IsFlat(); |
} else { |
node->data.to_screen = parent_node->data.to_screen; |
+ if (node->data.flattens_inherited_transform) |
+ node->data.to_screen.FlattenTo2d(); |
node->data.to_screen.PreconcatTransform(node->data.to_parent); |
node->data.ancestors_are_invertible = |
parent_node->data.ancestors_are_invertible; |
+ node->data.node_and_ancestors_are_flat = |
+ parent_node->data.node_and_ancestors_are_flat && |
+ node->data.to_parent.IsFlat(); |
} |
if (!node->data.to_screen.GetInverse(&node->data.from_screen)) |
@@ -287,7 +326,6 @@ void TransformTree::UpdateSnapping(TransformNode* node) { |
// Now that we have our scroll delta, we must apply it to each of our |
// combined, to/from matrices. |
node->data.to_parent.PreconcatTransform(delta); |
- node->data.from_parent.ConcatTransform(inverse_delta); |
node->data.to_target.PreconcatTransform(delta); |
node->data.from_target.ConcatTransform(inverse_delta); |
node->data.to_screen.PreconcatTransform(delta); |