Index: cc/trees/property_tree.cc |
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..95c2b5a7730b570330e9d346cf42e8c241799fbf |
--- /dev/null |
+++ b/cc/trees/property_tree.cc |
@@ -0,0 +1,199 @@ |
+// Copyright 2014 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. |
+ |
+#include <set> |
+ |
+#include "base/logging.h" |
+#include "cc/trees/property_tree.h" |
+ |
+namespace cc { |
+ |
+template <typename T> |
+PropertyTree<T>::PropertyTree() { |
+ nodes_.push_back(T()); |
+ back()->id = 0; |
+ back()->parent_id = -1; |
+} |
+ |
+template <typename T> |
+PropertyTree<T>::~PropertyTree() { |
+} |
+ |
+template <typename T> |
+int PropertyTree<T>::Insert(const T& tree_node, int parent_id) { |
+ DCHECK_GT(nodes_.size(), 0u); |
+ nodes_.push_back(tree_node); |
+ T& node = nodes_.back(); |
+ node.parent_id = parent_id; |
+ node.id = static_cast<int>(nodes_.size()) - 1; |
+ return node.id; |
+} |
+ |
+template class PropertyTree<TransformNode>; |
+template class PropertyTree<ClipNode>; |
+ |
+TransformNodeData::TransformNodeData() |
+ : target_id(-1), |
+ is_invertible(true), |
+ ancestors_are_invertible(true), |
+ is_animated(false), |
+ to_screen_is_animated(false), |
+ flattens(false) { |
+} |
+ |
+TransformNodeData::~TransformNodeData() { |
+} |
+ |
+ClipNodeData::ClipNodeData() : transform_id(-1), target_id(-1) { |
+} |
+ |
+bool TransformTree::ComputeTransform(int source_id, |
+ int dest_id, |
+ gfx::Transform* transform) const { |
+ transform->MakeIdentity(); |
+ |
+ if (source_id == dest_id) |
+ return true; |
+ |
+ if (source_id > dest_id && IsDescendant(source_id, dest_id)) |
+ return CombineTransformsBetween(source_id, dest_id, transform); |
+ |
+ if (dest_id > source_id && IsDescendant(dest_id, source_id)) |
+ return CombineInversesBetween(source_id, dest_id, transform); |
+ |
+ int lca = LowestCommonAncestor(source_id, dest_id); |
+ |
+ bool no_singular_matrices_to_lca = |
+ CombineTransformsBetween(source_id, lca, transform); |
+ |
+ bool no_singular_matrices_from_lca = |
+ CombineInversesBetween(lca, dest_id, transform); |
+ |
+ return no_singular_matrices_to_lca && no_singular_matrices_from_lca; |
+} |
+ |
+bool TransformTree::Are2DAxisAligned(int source_id, int dest_id) const { |
+ gfx::Transform transform; |
+ return ComputeTransform(source_id, dest_id, &transform) && |
+ transform.Preserves2dAxisAlignment(); |
+} |
+ |
+void TransformTree::UpdateScreenSpaceTransform(int id) { |
+ TransformNode* current_node = Node(id); |
+ TransformNode* parent_node = parent(current_node); |
+ TransformNode* target_node = Node(current_node->data.target_id); |
+ |
+ if (!parent_node) { |
+ current_node->data.to_screen = current_node->data.to_parent; |
+ current_node->data.ancestors_are_invertible = true; |
+ current_node->data.to_screen_is_animated = false; |
+ } else if (parent_node->data.flattens) { |
+ // 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. |
+ current_node->data.to_screen = target_node->data.to_screen; |
+ gfx::Transform flattened; |
+ ComputeTransform(parent_node->id, target_node->id, &flattened); |
+ flattened.FlattenTo2d(); |
+ current_node->data.to_screen.PreconcatTransform(flattened); |
+ current_node->data.to_screen.PreconcatTransform( |
+ current_node->data.to_parent); |
+ current_node->data.ancestors_are_invertible = |
+ parent_node->data.ancestors_are_invertible; |
+ } else { |
+ current_node->data.to_screen = parent_node->data.to_screen; |
+ current_node->data.to_screen.PreconcatTransform( |
+ current_node->data.to_parent); |
+ current_node->data.ancestors_are_invertible = |
+ parent_node->data.ancestors_are_invertible; |
+ } |
+ if (!current_node->data.to_screen.GetInverse(¤t_node->data.from_screen)) |
+ current_node->data.ancestors_are_invertible = false; |
+ |
+ if (parent_node) { |
+ current_node->data.to_screen_is_animated = |
+ current_node->data.is_animated || |
+ parent_node->data.to_screen_is_animated; |
+ } |
+} |
+ |
+bool TransformTree::IsDescendant(int desc_id, int source_id) const { |
+ while (desc_id != source_id) { |
+ if (desc_id < 0) |
+ return false; |
+ desc_id = Node(desc_id)->parent_id; |
+ } |
+ return true; |
+} |
+ |
+int TransformTree::LowestCommonAncestor(int a, int b) const { |
+ std::set<int> chain_a; |
+ std::set<int> chain_b; |
+ while (a || b) { |
+ if (a) { |
+ a = Node(a)->parent_id; |
+ if (a > -1 && chain_b.find(a) != chain_b.end()) |
+ return a; |
+ chain_a.insert(a); |
+ } |
+ if (b) { |
+ b = Node(b)->parent_id; |
+ if (b > -1 && chain_a.find(b) != chain_a.end()) |
+ return b; |
+ chain_b.insert(b); |
+ } |
+ } |
+ NOTREACHED(); |
+ return 0; |
+} |
+ |
+bool TransformTree::CombineTransformsBetween(int source_id, |
+ int dest_id, |
+ gfx::Transform* transform) const { |
+ const TransformNode* current = Node(source_id); |
+ const TransformNode* dest = Node(dest_id); |
+ if (!dest || dest->data.ancestors_are_invertible) { |
+ transform->ConcatTransform(current->data.to_screen); |
+ if (dest) |
+ transform->ConcatTransform(dest->data.from_screen); |
+ return true; |
+ } |
+ |
+ bool all_are_invertible = true; |
+ for (; current && current->id > dest_id; current = parent(current)) { |
+ transform->ConcatTransform(current->data.to_parent); |
+ if (!current->data.is_invertible) |
+ all_are_invertible = false; |
+ } |
+ |
+ return all_are_invertible; |
+} |
+ |
+bool TransformTree::CombineInversesBetween(int source_id, |
+ int dest_id, |
+ gfx::Transform* transform) const { |
+ const TransformNode* current = Node(dest_id); |
+ const TransformNode* dest = Node(source_id); |
+ if (current->data.ancestors_are_invertible) { |
+ 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; |
+ } |
+ |
+ return all_are_invertible; |
+} |
+ |
+} // namespace cc |