| // Copyright 2016 The Cobalt Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #ifndef COBALT_RENDER_TREE_ANIMATIONS_ANIMATE_NODE_H_ |
| #define COBALT_RENDER_TREE_ANIMATIONS_ANIMATE_NODE_H_ |
| |
| #include <map> |
| #include <vector> |
| |
| #include "base/containers/small_map.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/optional.h" |
| #include "cobalt/math/rect_f.h" |
| #include "cobalt/render_tree/animations/animation_list.h" |
| #include "cobalt/render_tree/movable.h" |
| #include "cobalt/render_tree/node.h" |
| #include "cobalt/render_tree/node_visitor.h" |
| |
| namespace cobalt { |
| namespace render_tree { |
| namespace animations { |
| |
| // An AnimateNode describes a set of animations that affect the subtree attached |
| // to the AnimateNode. In order to create an AnimateNode, one must first |
| // populate an AnimateNode::Builder with a mapping of render tree node |
| // references to corresponding animations that should be applied to them. |
| // Construction of an AnimateNode object requires a populated |
| // AnimateNode::Builder instance as well as the sub-tree that the animations |
| // apply to. Upon construction, AnimateNode will compile the set of animated |
| // nodes into a format specialized to the associated subtree such that it is |
| // much faster to apply the animations. Once an AnimateNode is constructed, |
| // it can generate static render trees representing frames of animation via |
| // calls to AnimateNode::Apply(), which returns a static render tree. |
| // When AnimateNodes are constructed they maintain an invariant that |
| // AnimateNodes never have other AnimateNodes as descendants. It does this |
| // by traversing the subtree on construction and if another AnimateNode is |
| // encountered, its information is merged into this root AnimateNode, and the |
| // sub-AnimateNode is removed from the tree. |
| class AnimateNode : public Node { |
| public: |
| // Manages a mapping of render tree nodes to corresponding animations. Users |
| // of AnimateNode should populate the Builder with animations and then use |
| // that to construct the AnimateNode. |
| class Builder { |
| public: |
| DECLARE_AS_MOVABLE(Builder); |
| |
| Builder() {} |
| explicit Builder(Moved moved) { |
| node_animation_map_ = moved->node_animation_map_; |
| node_refs_.swap(moved->node_refs_); |
| } |
| |
| // This method is a template so that we can ensure that animations are not |
| // mismatched with render tree nodes of the wrong type. |
| template <typename T> |
| void Add(const scoped_refptr<T>& target_node, |
| const scoped_refptr<AnimationList<T> >& animation_list) { |
| AddInternal(target_node, animation_list); |
| } |
| |
| // Convenience method to attach a single animation to a target node. |
| template <typename T> |
| void Add(const scoped_refptr<T>& target_node, |
| const typename Animation<T>::Function& single_animation, |
| base::TimeDelta expiry) { |
| AddInternal(target_node, |
| scoped_refptr<AnimationListBase>( |
| new AnimationList<T>(single_animation, expiry))); |
| } |
| |
| template <typename T> |
| void Add( |
| const scoped_refptr<T>& target_node, |
| const typename Animation<T>::TimeIndependentFunction& single_animation, |
| base::TimeDelta expiry) { |
| AddInternal(target_node, |
| scoped_refptr<AnimationListBase>( |
| new AnimationList<T>(single_animation, expiry))); |
| } |
| |
| template <typename T> |
| void Add(const scoped_refptr<T>& target_node, |
| const typename Animation<T>::Function& single_animation) { |
| AddInternal(target_node, |
| scoped_refptr<AnimationListBase>(new AnimationList<T>( |
| single_animation, base::TimeDelta::Max()))); |
| } |
| template <typename T> |
| void Add(const scoped_refptr<T>& target_node, |
| const typename Animation<T>::TimeIndependentFunction& |
| single_animation) { |
| AddInternal(target_node, |
| scoped_refptr<AnimationListBase>(new AnimationList<T>( |
| single_animation, base::TimeDelta::Max()))); |
| } |
| |
| // Merge all mappings from another AnimateNode::Builder into this one. |
| // There cannot be any keys that are in both the merge target and source. |
| void Merge(const Builder& other); |
| |
| // Returns true if there are no animations added to this |
| // AnimateNode::Builder. |
| bool empty() const { return node_animation_map_.empty(); } |
| |
| private: |
| // A non-template function that contains the logic for storing a target |
| // node and animation list pair. |
| void AddInternal(const scoped_refptr<Node>& target_node, |
| const scoped_refptr<AnimationListBase>& animation_list); |
| |
| // The primary internal data structure used to organize and store the |
| // mapping between target render tree node and animation list. |
| // In many cases there are not many active animations, and so we use a |
| // base::small_map for this. std::map was found to be more performant than |
| // base::hash_map, so it is used as the fallback map. |
| typedef base::small_map<std::map<Node*, scoped_refptr<AnimationListBase> >, |
| 4> |
| InternalMap; |
| InternalMap node_animation_map_; |
| std::vector<scoped_refptr<Node> > node_refs_; |
| |
| friend class AnimateNode; |
| }; |
| |
| AnimateNode(const Builder& builder, const scoped_refptr<Node>& source); |
| |
| // This will create an AnimateNode with no animations. It is useful because |
| // construction of AnimateNodes maintain an invariant that there are no |
| // sub-AnimateNodes underneath them. Thus, adding a AnimateNode, even if it |
| // has no animations, to an existing tree will result in existing AnimateNodes |
| // being merged into the new root AnimateNode, which can simplify the |
| // underlying tree. |
| explicit AnimateNode(const scoped_refptr<Node>& source); |
| |
| // Cannot visit this node. |
| void Accept(NodeVisitor* visitor) override { visitor->Visit(this); } |
| |
| // Since we don't have any bounds on the animations contained in this tree, |
| // we cannot know the bounds of the tree over all time. Indeed animations |
| // may not have any bounds as time goes to infinity. Despite this, we return |
| // the bounds of the initial render tree to enable AnimateNodes to be setup |
| // as children of CompositionNodes. |
| math::RectF GetBounds() const override { return source_->GetBounds(); } |
| |
| base::TypeId GetTypeId() const override { |
| return base::GetTypeId<AnimateNode>(); |
| } |
| |
| struct AnimateResults { |
| // The animated render tree, which is guaranteed to not contain any |
| // AnimateNodes. Note that it is returned as an AnimateNode... The actual |
| // animated render tree can be obtained via |animated->source()|, the |
| // AnimateNode however allows one to animate the animated node so that |
| // it can be checked if anything has actually been animated or not. |
| scoped_refptr<AnimateNode> animated; |
| |
| // Can be called in order to return a bounding rectangle around all |
| // nodes that are actively animated. The parameter specifies a "since" |
| // time. Any animations that had already expired *before* the "since" time |
| // will not be included in the returned bounding box. |
| // This information is likely to be used to enable optimizations where only |
| // regions of the screen that have changed are rendered. |
| base::Callback<math::RectF(base::TimeDelta)> get_animation_bounds_since; |
| }; |
| // Apply the animations to the sub render tree with the given |time_offset|. |
| // Note that if |animated| in the results is equivalent to |this| on which |
| // Apply() is called, then no animations have actually taken place, and a |
| // re-render can be avoided. |
| AnimateResults Apply(base::TimeDelta time_offset); |
| |
| // Returns the sub-tree for which the animations apply to. |
| const scoped_refptr<Node> source() const { return source_; } |
| |
| // Returns the time at which all animations will have completed, or |
| // base::TimeDelta::Max() if they will never complete. |
| // It will be true that AnimateNode::Apply(x) == AnimateNode::Apply(y) for |
| // all x, y >= expiry(). |
| const base::TimeDelta& expiry() const { return expiry_; } |
| |
| // Similar to |expiry()|, but returns the expiration for all animations whose |
| // callback function actually depends on the time parameter passed into them. |
| const base::TimeDelta& depends_on_time_expiry() const { |
| return depends_on_time_expiry_; |
| } |
| |
| private: |
| // The compiled node animation list is a sequence of nodes that are either |
| // animated themselves, or on the path to an animated node. Only nodes in |
| // this sequence need to be traversed. TraverseListEntry is an entry in this |
| // list, complete with animations to be applied to the given node, if any. |
| struct TraverseListEntry { |
| TraverseListEntry(Node* node, |
| const scoped_refptr<AnimationListBase>& animations, |
| bool did_animate_previously) |
| : node(node), |
| animations(animations), |
| did_animate_previously(did_animate_previously) {} |
| explicit TraverseListEntry(Node* node) |
| : node(node), did_animate_previously(false) {} |
| |
| Node* node; |
| scoped_refptr<AnimationListBase> animations; |
| // Used when checking animation bounds to see which nodes were actually |
| // animated and whose bounds we need to accumulate. |
| bool did_animate_previously; |
| }; |
| typedef std::vector<TraverseListEntry> TraverseList; |
| |
| class RefCountedTraversalList; |
| |
| // A helper render tree visitor class used to compile sub render-tree |
| // animations. |
| class TraverseListBuilder; |
| // A helper render tree visitor class used to apply compiled sub render-tree |
| // animations. This class follows the traversal generated by |
| // TraverseListBuilder. |
| class ApplyVisitor; |
| // A helper class to traverse an animated tree, and compute a bounding |
| // rectangle for all active animations. |
| class BoundsVisitor; |
| |
| // This private constructor is used by AnimateNode::Apply() to create a new |
| // AnimateNode that matches the resulting animated render tree. |
| AnimateNode(const TraverseList& traverse_list, scoped_refptr<Node> source, |
| const base::TimeDelta& expiry, |
| const base::TimeDelta& depends_on_time_expiry, |
| const base::TimeDelta& snapshot_time) |
| : traverse_list_(traverse_list), |
| source_(source), |
| expiry_(expiry), |
| depends_on_time_expiry_(depends_on_time_expiry), |
| snapshot_time_(snapshot_time) {} |
| |
| void CommonInit(const Builder::InternalMap& node_animation_map, |
| const scoped_refptr<Node>& source); |
| |
| static math::RectF GetAnimationBoundsSince( |
| const scoped_refptr<RefCountedTraversalList>& traverse_list, |
| base::TimeDelta time_offset, const scoped_refptr<Node>& animated, |
| base::TimeDelta since); |
| |
| // The compiled traversal list through the sub-tree represented by |source_| |
| // that guides us towards all nodes that need to be animated. |
| TraverseList traverse_list_; |
| scoped_refptr<Node> source_; |
| base::TimeDelta expiry_; |
| base::TimeDelta depends_on_time_expiry_; |
| // Time when the |source_| render tree was last animated, if known. This |
| // will get set when Apply() is called to produce a new AnimateNode that can |
| // then be Apply()d again. It can be used during the second apply to check |
| // if some animations have expired. |
| base::Optional<base::TimeDelta> snapshot_time_; |
| }; |
| |
| } // namespace animations |
| } // namespace render_tree |
| } // namespace cobalt |
| |
| #endif // COBALT_RENDER_TREE_ANIMATIONS_ANIMATE_NODE_H_ |