blob: a83185bbc5e58c280be69ba5c7fec5a92307f980 [file] [log] [blame]
// 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_